commit d62d1a12436816cb0715de503852e8d0f3efc282 Author: yjb <3169838138@qq.com> Date: Mon Sep 1 10:24:02 2025 +0800 职工工资管理系统后端文件 diff --git a/main/java/com/esms/controller/DepartmentController.java b/main/java/com/esms/controller/DepartmentController.java new file mode 100644 index 0000000..44fe19c --- /dev/null +++ b/main/java/com/esms/controller/DepartmentController.java @@ -0,0 +1,179 @@ +package com.esms.controller; + +import com.esms.po.Department; +import com.esms.service.DepartmentService; +import com.esms.vo.DepartmentPages; +import com.github.pagehelper.PageHelper; +import com.github.pagehelper.PageInfo; +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.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; + +import java.util.List; + +/** + * @Description: +*/ + +@Controller +@RequestMapping("/department") +public class DepartmentController { + @Autowired + public DepartmentService departmentService = null; + + /** + * 查找部门 + * @param pageNum + * @param limit + * @param d_name + * @return + * @throws Exception + */ + @RequestMapping("/findSelective.do") + @ResponseBody + public DepartmentPages findSelective( + @RequestParam(value="page", defaultValue="1")int pageNum, + @RequestParam(value="limit", defaultValue="5") int limit, + @RequestParam(value="d_name", defaultValue="") String d_name) throws Exception { + List list; + + //模糊查询,有多少个条件就接收多少个字段 + + Department department = new Department(); + department.setdName(d_name); + + //pageNum:起始页面 pageSize:每页的大小 + PageHelper.startPage(pageNum,limit); + //查找条件,一定要紧跟在startPage后 + list = departmentService.findSelective(department); + PageInfo pageResult = new PageInfo(list); + + //设置前台需要的数据 + DepartmentPages departmentPages = new DepartmentPages(); + departmentPages.setCode(0); + departmentPages.setMsg(""); + departmentPages.setCount((int) pageResult.getTotal()); + departmentPages.setData(pageResult.getList()); + + return departmentPages; + } + + /** + * 添加部门 + * @param d_name + * @param d_remark + * @return + * @throws Exception + */ + @RequestMapping("/add.do") + @ResponseBody + public int add(String d_name, String d_remark) throws Exception { + + Department department = departmentService.findByDname(d_name); + + //查找是否同名 + if(department != null) { + return department.getdId(); + } else { + Department d = new Department(); + d.setdId(null); + d.setdName(d_name); + d.setdRemark(d_remark); + d.setdIsdel(1); + departmentService.insertSelective(d); + return 0; + } + } + + + /** + * 查找一个部门 + * @param id + * @return + * @throws Exception + */ + @RequestMapping("/findByPrimaryKey.do") + @ResponseBody + public Department findByPrimaryKey(int id) throws Exception { + Department department = departmentService.selectByPrimaryKey(id); + return department; + } + + /** + * 更新部门 + * @param id + * @param d_name + * @param d_remark + * @throws Exception + */ + @RequestMapping("/updateByPrimaryKey.do") + @ResponseBody + public int updateByPrimaryKey(int id, String d_name, String d_remark) throws Exception { + Department department = departmentService.findByDname(d_name); + //有同名的且不是同一个 + if(department != null && !department.getdId().equals(id) ) { + return department.getdId(); + } else { + Department d = new Department(); + d.setdId(id); + d.setdName(d_name); + d.setdRemark(d_remark); + d.setdIsdel(null); + departmentService.updateByPrimaryKeySelective(d); + return 0; + } + } + + @RequestMapping("/findByDname.do") + @ResponseBody + public int findByDname(String d_name) { + + Department department = departmentService.findByDname(d_name); + if(department != null) { + return department.getdId(); + } else { + return 0; + } + } + + /** + * 删除部门 + * @param id + */ + @RequestMapping("/deleteByPrimaryKey.do") + @ResponseBody + public int deleteByPrimaryKey(int id) throws Exception { + //删除部门,调用更新操作,将状态改为0 + Department department = new Department(); + department.setdId(id); + department.setdIsdel(0); + departmentService.updateByPrimaryKeySelective(department); + return 1; + } + + /** + * 批量删除 + * @param ids + */ + @RequestMapping("/deleteByQuery.do") + public void deleteByQuery (@RequestParam(value = "arr")int[] ids) { + //批量删除,实则修改状态为0 + //如果有id才执行 + if(ids.length > 0) { + departmentService.deleteByQuery(ids); + } + +// var arr = [3,4,5]; +// $.ajax({ +// type:'post', +// url:'department/deleteByQuery.do', +// data: {"arr":arr}, +// traditional: true, +// dataType:'json', +// }); + + } + +} diff --git a/main/java/com/esms/controller/DownloadExcelController.java b/main/java/com/esms/controller/DownloadExcelController.java new file mode 100644 index 0000000..29d6f3e --- /dev/null +++ b/main/java/com/esms/controller/DownloadExcelController.java @@ -0,0 +1,28 @@ +package com.esms.controller; + +import com.esms.exception.CustomException; +import com.esms.service.IDownloadExcelService; +import com.esms.service.impl.DownloadExcelServiceImpl; +import jxl.read.biff.BiffException; +import jxl.write.WriteException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * @Description:下载excel表格 + */ +@Controller +public class DownloadExcelController { + @Autowired + private IDownloadExcelService downloadExcelService; + + @RequestMapping(value = "downloadExcel.do") + public void downloadExcel(HttpServletRequest request, HttpServletResponse response,String eAccount,Integer dId,String sTime) throws Exception{ + downloadExcelService.getSalaryExcel(request,response,eAccount,dId,sTime) ; + } +} diff --git a/main/java/com/esms/controller/EchartsController.java b/main/java/com/esms/controller/EchartsController.java new file mode 100644 index 0000000..b769856 --- /dev/null +++ b/main/java/com/esms/controller/EchartsController.java @@ -0,0 +1,175 @@ +package com.esms.controller; + +import com.esms.service.IEchartsService; +import com.esms.service.impl.EchartsServiceImpl; +import com.esms.vo.EchDepartmentSalary; +import com.esms.vo.EchEmployeeNums; +import com.esms.vo.EchMonthSalary; +import com.esms.vo.EchSalaryPercent; +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.ResponseBody; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +/** + * @Description: 图表制作类 + **/ +@Controller +public class EchartsController { + @Autowired + private IEchartsService echartsService; + + @RequestMapping(value = "/showEmployeeNums.do") + @ResponseBody + public Map> showEmployeeNums(){ + /** + * 各部门的员工数量柱状图的数据 + * json格式{"list":[{"department":"人事部","num":480},{"department":"行政部","num":380}]} + */ + /* + List echEmployeeNumsList = new ArrayList(); + Map> map = new HashMap>(); + EchEmployeeNums e1 = new EchEmployeeNums(); + e1.setDepartment("人事部"); + e1.setNum(123); + EchEmployeeNums e2 = new EchEmployeeNums(); + e2.setDepartment("行政部"); + e2.setNum(123); + echEmployeeNumsList.add(e1); + echEmployeeNumsList.add(e2); + map.put("list",echEmployeeNumsList);*/ + return echartsService.getEmployeeNums(); + } + + @RequestMapping(value = "/showDepartmentSalary.do") + @ResponseBody + public Map> showDepartmentSalary(String date){ + /** + *各年月工资对比图的数据 + * json格式{"list":[{"year":"1","salary":[480,500,600,..]},{"year":"2","salary":[480,500,600...]}]} + */ +/* + List echDepartmentSalaryList = new ArrayList(); + Map> stringListMap = new HashMap>(); + + EchDepartmentSalary e1 = new EchDepartmentSalary(); + e1.setDepartment("人事部"); + List salary1 = new ArrayList(); + salary1.add(5000.00); + salary1.add(8000.00); + salary1.add(20000.00); + e1.setSalary(salary1); + + EchDepartmentSalary e2 = new EchDepartmentSalary(); + e2.setDepartment("科研部"); + List salary2 = new ArrayList(); + salary2.add(6000.00); + salary2.add(8500.00); + salary2.add(25000.00); + e2.setSalary(salary2); + + EchDepartmentSalary e3 = new EchDepartmentSalary(); + e3.setDepartment("策划部"); + List salary3 = new ArrayList(); + salary3.add(6000.00); + salary3.add(8800.00); + salary3.add(23000.00); + e3.setSalary(salary3); + + echDepartmentSalaryList.add(e1); + echDepartmentSalaryList.add(e2); + echDepartmentSalaryList.add(e3); + stringListMap.put("list",echDepartmentSalaryList); + return stringListMap;*/ + return echartsService.getDepartmentSalary(date); + } + + @RequestMapping(value = "/showSalaryPercent.do") + @ResponseBody + public Map> showSalaryPercent(String date,int state){ + /*List echSalaryPercentList = new ArrayList(); + Map> map = new HashMap>(); + EchSalaryPercent s1 = new EchSalaryPercent(); + EchSalaryPercent s2 = new EchSalaryPercent(); + s1.setDepartment("人事部"); + s1.setSalary(1010010.00); + s2.setDepartment("科研部"); + s2.setSalary(1510010.00); + echSalaryPercentList.add(s1); + echSalaryPercentList.add(s2); + map.put("list",echSalaryPercentList);*/ + + return echartsService.getSalaryPercent(date,state); + } + + @RequestMapping(value = "/showMonthSalary.do") + @ResponseBody + public Map> showMonthSalary(){ + /* + List echMonthSalaryList = new ArrayList(); + Map> stringListMap = new HashMap>(); + + EchMonthSalary e1 = new EchMonthSalary(); + e1.setYear("2015年"); + List salary1 = new ArrayList(); + salary1.add(5000.00); + salary1.add(8000.00); + salary1.add(20000.00); + salary1.add(5000.00); + salary1.add(8000.00); + salary1.add(20000.00); + salary1.add(5000.00); + salary1.add(8000.00); + salary1.add(20000.00); + salary1.add(5000.00); + salary1.add(8000.00); + salary1.add(20000.00); + e1.setSalary(salary1); + + EchMonthSalary e2 = new EchMonthSalary(); + e2.setYear("2016年"); + List salary2 = new ArrayList(); + salary2.add(5800.00); + salary2.add(8080.00); + salary2.add(20800.00); + salary2.add(5080.00); + salary2.add(8800.00); + salary2.add(20800.00); + salary2.add(5080.00); + salary2.add(8080.00); + salary2.add(20800.00); + salary2.add(5080.00); + salary2.add(8080.00); + salary2.add(20080.00); + e2.setSalary(salary2); + + EchMonthSalary e3 = new EchMonthSalary(); + e3.setYear("2017年"); + List salary3 = new ArrayList(); + salary3.add(6000.00); + salary3.add(8800.00); + salary3.add(23000.00); + salary3.add(6000.00); + salary3.add(8800.00); + salary3.add(23000.00); + salary3.add(6000.00); + salary3.add(8800.00); + salary3.add(23000.00); + salary3.add(6000.00); + salary3.add(8800.00); + salary3.add(23000.00); + e3.setSalary(salary3); + + echMonthSalaryList.add(e1); + echMonthSalaryList.add(e2); + echMonthSalaryList.add(e3); + stringListMap.put("list",echMonthSalaryList); + return stringListMap;*/ + return echartsService.getMonthSalary(); + } + +} diff --git a/main/java/com/esms/controller/EmployeeController.java b/main/java/com/esms/controller/EmployeeController.java new file mode 100644 index 0000000..d423924 --- /dev/null +++ b/main/java/com/esms/controller/EmployeeController.java @@ -0,0 +1,228 @@ +package com.esms.controller; + +import com.esms.dao.EmployeeCustomVoMapper; +import com.esms.po.Employee; +import com.esms.service.EmployeeService; +import com.esms.utils.MD5Utils; +import com.esms.vo.EmployeeCustomVo; +import com.esms.vo.EmployeePages; +import com.github.pagehelper.PageHelper; +import com.github.pagehelper.PageInfo; +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.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; + +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.HashMap; +import java.util.List; + +/** + * @Description: + */ +@Controller +@RequestMapping("/employee") +public class EmployeeController { + @Autowired + private EmployeeService employeeService; + + @Autowired + private EmployeeCustomVoMapper employeeCustomVoMapper = null; + + + @RequestMapping("/findSelective.do") + @ResponseBody + public EmployeePages findSelective( + @RequestParam(value="page", defaultValue="1")int pageNum, + @RequestParam(value="limit", defaultValue="5") int limit, + @RequestParam(value="e_account", defaultValue="") String e_account, + @RequestParam(value="e_name", defaultValue="") String e_name, + @RequestParam(value="d_id", defaultValue="0") int d_id) throws Exception { + HashMap map = new HashMap(); + map.put("e_account",e_account); + map.put("e_name",e_name); + map.put("d_id",d_id); + //pageNum:起始页面 pageSize:每页的大小 + PageHelper.startPage(pageNum,limit); + //查找条件,一定要紧跟在startPage后 + List list = + employeeCustomVoMapper.selectEmployeeSelective(map); +// System.out.println(list.get(0).getMonthlyAttendance().getAttendanceTime()+"========================"); + PageInfo pageResult = new PageInfo(list); + + //设置前台需要的数据 + EmployeePages employeePages = new EmployeePages(); + employeePages.setCode(0); + employeePages.setMsg(""); + employeePages.setCount((int) pageResult.getTotal()); + employeePages.setData(pageResult.getList()); + return employeePages; + } + + /** + * 查找一个 + * @param id + * @return + */ + @RequestMapping("/selectVoByPrimaryKey.do") + @ResponseBody + public EmployeeCustomVo selectVoByPrimaryKey(int id) { + EmployeeCustomVo monthlyAttendanceCustomVo = new EmployeeCustomVo(); + monthlyAttendanceCustomVo = employeeCustomVoMapper.selectVoByPrimaryKey(id); +// System.out.println(monthlyAttendanceCustomVo+"======================="); + return monthlyAttendanceCustomVo; + } + + + @RequestMapping("/add.do") + @ResponseBody + public int add(String eAccount, + String eName, + String ePassword, + String eIdcard, + int rbId, + String eSex, + String eBirthday, + String eDagree, + int dId, + int pId, + String eEntryTime, + double eBasePay, + String ePhone, + String eEmail, + String eUrgentPerson, + String eUrgentPhone, + String eHometown, + String headPath) throws Exception { + + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd"); + + //检测工号是否相同 + //查找是否同名 + Employee e = employeeService.findByeAccount(eAccount); + if(e != null) { + return 0; + } else { + Employee employee = new Employee(); + employee.seteAccount(eAccount); + employee.seteName(eName); + employee.setePassword(MD5Utils.encodeByMD5(ePassword)); + employee.seteIdcard(eIdcard); + employee.seteRank(rbId); + employee.seteSex(eSex); + employee.seteBirthday(format.parse(eBirthday)); + employee.seteDagree(eDagree); + employee.setdId(dId); + employee.setpId(pId); + employee.seteEntryTime(format.parse(eEntryTime)); + employee.seteBasePay(eBasePay); + employee.setePhone(ePhone); + employee.seteEmail(eEmail); + employee.seteUrgentPerson(eUrgentPerson); + employee.seteUrgentPhone(eUrgentPhone); + employee.seteHometown(eHometown); + employee.seteHeadPath(headPath); + employee.seteIsdel(1); + employeeService.insert(employee); + return 1; + } + + + } + + + + @RequestMapping("/updateByPrimaryKeySelective.do") + @ResponseBody + public int updateByPrimaryKeySelective( + int eId, + String eName, + String eIdcard, + int rbId, + String eSex, + String eBirthday, + String eDagree, + int dId, + int pId, + String eEntryTime, + double eBasePay, + String ePhone, + String eEmail, + String eUrgentPerson, + String eUrgentPhone, + String eHometown, + String headPath) throws Exception { + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd"); + + Employee employee = new Employee(); + employee.seteId(eId); + employee.seteName(eName); + employee.seteIdcard(eIdcard); + employee.seteRank(rbId); + employee.seteSex(eSex); + employee.seteBirthday(format.parse(eBirthday)); + employee.seteDagree(eDagree); + employee.setdId(dId); + employee.setpId(pId); + employee.seteEntryTime(format.parse(eEntryTime)); + employee.seteBasePay(eBasePay); + employee.setePhone(ePhone); + employee.seteEmail(eEmail); + employee.seteUrgentPerson(eUrgentPerson); + employee.seteUrgentPhone(eUrgentPhone); + employee.seteHometown(eHometown); + employee.seteHeadPath(headPath); + employee.seteIsdel(1); + employeeService.updateByPrimaryKeySelective(employee); + return 1; + + + } + + /** + * 删除 + * @param id + * @throws Exception + */ + @RequestMapping("/deleteByPrimaryKey.do") + @ResponseBody + public int deleteByPrimaryKey(int id) throws Exception { + +// SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");//设置日期格式 +// Date date = new Date(); +// System.out.println(df.format(new Date()));// new Date()为获取当前系统时间 + + //假删除,设置离职时间 + Employee employee = new Employee(); + employee.seteId(id); + employee.seteLeaveTime(new Date()); + employee.seteIsdel(0); + employeeService.updateByPrimaryKeySelective(employee); + return 1; + } + + /** + * 批量删除 + * @param ids + */ + @RequestMapping("/deleteByQuery.do") + @ResponseBody + public int deleteByQuery (@RequestParam(value = "arr")int[] ids) { +// System.out.println(ids+"&"+ids.length); +// for (int i =0 ; i< ids.length;i++) { +// System.out.println(ids[i]); +// } + //假删除 + //如果有id才执行 + if(ids.length > 0) { + employeeService.deleteByQuery(ids); + } + return 1; + + } + + + +} diff --git a/main/java/com/esms/controller/ImportDataController.java b/main/java/com/esms/controller/ImportDataController.java new file mode 100644 index 0000000..778e8a4 --- /dev/null +++ b/main/java/com/esms/controller/ImportDataController.java @@ -0,0 +1,88 @@ +package com.esms.controller; + +import com.esms.service.IImportDataService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.multipart.MultipartFile; + +import javax.servlet.http.HttpSession; +import java.util.HashMap; +import java.util.Map; + +/** + + * @Description: 考勤表和补发金额表数据导入 + **/ +@Controller +public class ImportDataController { + @Autowired + private IImportDataService importDataService; + /** + * 跳转到importAttendanceTeable.jsp + * @return + */ + @RequestMapping("toImportAttendance.do") + public String toImportAttendance(){ + return "admin/importData/importAttendanceTable.jsp"; + } + /** + * 跳转到importReissueTable.jsp + * @return + */ + @RequestMapping("toImportReissue.do") + public String toImportReissue(){ + return "admin/importData/importReissueTable.jsp"; + } + + /** + *上传月出勤表 + * @param excel 考勤表文件 + * @return + */ + @RequestMapping("/importAttendance.do") + @ResponseBody + public Map importAttendanceTable(@RequestParam("file") MultipartFile excel) throws Exception{ + Map map = new HashMap(); + if (!excel.isEmpty()) { + String result = importDataService.insertMATable(excel); + map.put("result",result); + return map; + /*if (importDataService.insertMATable(excel)) { + //return "admin/importData/success.jsp"; + map.put("result","导入成功"); + return map; + } + else{ + map.put("result","只能导入Microsoft Excel 97-2003 工作表 (.xls),请检查文件是否正确"); + return map; + }*/ + } else { + map.put("result","导入的文件不存在,请重新选择文件"); + return map; + } + } + + /** + * + * @param excel 补发金额表excel + * @return + */ + @RequestMapping("/importReissue.do") + @ResponseBody + public Map importReissueTable(@RequestParam("file") MultipartFile excel) throws Exception{ + Map map = new HashMap(); + if (!excel.isEmpty()) { + String result = importDataService.insertReissueTable(excel); + map.put("result",result); + return map; + } + else { + map.put("result","导入的文件不存在,请重新选择文件"); + return map; + } + } +} diff --git a/main/java/com/esms/controller/KeyValueController.java b/main/java/com/esms/controller/KeyValueController.java new file mode 100644 index 0000000..13251c6 --- /dev/null +++ b/main/java/com/esms/controller/KeyValueController.java @@ -0,0 +1,127 @@ +package com.esms.controller; + +import com.esms.po.KeyValue; +import com.esms.service.impl.KeyValueServiceImpl; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; + +@Controller +public class KeyValueController { + @Autowired + private KeyValueServiceImpl keyValueService = null; + +// 测试 + @RequestMapping("/getKeyValueById.do") + public @ResponseBody String getKeyValueById(Model model, Integer id_id)throws Exception{ + return "error.jsp"; + } + @RequestMapping("/changeWageItem.do") + @ResponseBody + public int changeWageItem(double late_buckle_pay,double early_buckle_pay,double missionallowance,double full_attendance_pay,double food_pay,double traffic_pay)throws Exception{ + /*return late_buckle_pay;*/ + int count=0; + + KeyValue kv1=new KeyValue(); + kv1.setKvKey("food_pay"); + kv1.setKvId(1); + kv1.setKvValue(food_pay); + count+=keyValueService.updateByPrimaryKey(kv1); + + KeyValue kv2=new KeyValue(); + kv2.setKvId(2); + kv2.setKvKey("traffic_pay"); + kv2.setKvValue(traffic_pay); + count+=keyValueService.updateByPrimaryKey(kv2); + + KeyValue kv3=new KeyValue(); + kv3.setKvId(3); + kv3.setKvKey("late_buckle_pay"); + kv3.setKvValue(late_buckle_pay); + count+=keyValueService.updateByPrimaryKey(kv3); + + KeyValue kv4=new KeyValue(); + kv4.setKvId(4); + kv4.setKvKey("early_buckle_pay"); + kv4.setKvValue(early_buckle_pay); + count+=keyValueService.updateByPrimaryKey(kv4); + + KeyValue kv5=new KeyValue(); + kv5.setKvId(5); + kv5.setKvKey("missionallowance"); + kv5.setKvValue(missionallowance); + count+=keyValueService.updateByPrimaryKey(kv5); + + KeyValue kv6=new KeyValue(); + kv6.setKvId(6); + kv6.setKvKey("full_attendance_pay"); + kv6.setKvValue(full_attendance_pay); + count+=keyValueService.updateByPrimaryKey(kv6); + + return count; + } + @RequestMapping("/get_late_buckle_pay.do") + public @ResponseBody KeyValue get_late_buckle_pay(){ + KeyValue keyValue=new KeyValue(); + try { + keyValue=keyValueService.selectBykvKey("late_buckle_pay"); + } catch (Exception e) { + e.printStackTrace(); + } + return keyValue; + } + @RequestMapping("/get_food_pay.do") + public @ResponseBody KeyValue get_food_pay(){ + KeyValue keyValue=new KeyValue(); + try { + keyValue=keyValueService.selectBykvKey("food_pay"); + } catch (Exception e) { + e.printStackTrace(); + } + return keyValue; + } + @RequestMapping("/get_traffic_pay.do") + public @ResponseBody KeyValue get_traffic_pay(){ + KeyValue keyValue=new KeyValue(); + try { + keyValue=keyValueService.selectBykvKey("traffic_pay"); + } catch (Exception e) { + e.printStackTrace(); + } + return keyValue; + } + @RequestMapping("/get_early_buckle_pay.do") + public @ResponseBody KeyValue get_early_buckle_pay(){ + KeyValue keyValue=new KeyValue(); + try { + keyValue=keyValueService.selectBykvKey("early_buckle_pay"); + } catch (Exception e) { + e.printStackTrace(); + } + return keyValue; + } + @RequestMapping("/get_missionallowance.do") + public @ResponseBody KeyValue get_missionallowance(){ + KeyValue keyValue=new KeyValue(); + try { + keyValue=keyValueService.selectBykvKey("missionallowance"); + } catch (Exception e) { + e.printStackTrace(); + } + return keyValue; + } + @RequestMapping("/get_full_attendance_pay.do") + public @ResponseBody KeyValue get_full_attendance_pay(){ + KeyValue keyValue=new KeyValue(); + try { + keyValue=keyValueService.selectBykvKey("full_attendance_pay"); + } catch (Exception e) { + e.printStackTrace(); + } + return keyValue; + } + + +} diff --git a/main/java/com/esms/controller/LoginController.java b/main/java/com/esms/controller/LoginController.java new file mode 100644 index 0000000..1558e7f --- /dev/null +++ b/main/java/com/esms/controller/LoginController.java @@ -0,0 +1,155 @@ +package com.esms.controller; + +import com.esms.exception.CustomException; +import com.esms.po.Employee; +import com.esms.po.SystemManager; +import com.esms.service.impl.LoginServiceImpl; +import com.esms.utils.CaptchaUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +/** + * @Description: 管理员和员工登陆控制 + **/ +@Controller +public class LoginController { + @Autowired + private LoginServiceImpl loginService = null; + /** + * @Author: admin + * @Description: + * @Param: [request, response] + * @Return: void + **/ + + //获取验证码 + @RequestMapping(value = "/changeCode.do") + @ResponseBody + public void getIdentifyingCode(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException + { + // 验证码存储在session的identifyingCode,属性中 + CaptchaUtil.outputCaptcha(request, response); + } + + // 获取员工登陆界面 + @RequestMapping("/") + public String getLoginPage(){ + return "employee/login.html"; + } + + // 获取管理员登陆界面 + @RequestMapping("/admin.do") + public String getAdminLoginPage(){ + return "admin/adminLogin.html"; + } + + //员工登录判断 + @RequestMapping(value = "/employeeLogin.do") + @ResponseBody + public Map employeeLogin(Model model, HttpSession httpSession, String username, + String password, String identifyingcode) + { + String code = (String) httpSession.getAttribute("identifyingCode"); + HashMap map = new HashMap(); + if(identifyingcode.equalsIgnoreCase(code)){ + Employee employee = null; + try { + employee = loginService.findEmployeeByIdAndPassword(username, password); + } catch (CustomException e) { + map.put("msg",e.getMessage()); + map.put("status","500"); + return map; + } + // 保存到session + httpSession.setAttribute("employeeId",employee.geteId()); + map.put("url","/ssm_esms/loginSuccess.do"); + map.put("msg","成功"); + map.put("status","200"); + return map; + }else{ + map.put("msg","验证码错误"); + map.put("status","0"); + return map; + } + } + + + + @RequestMapping(value = "/loginSuccess.do") + public String loginSucceses(Model model) throws Exception + { + return "employee/index.html"; + } + + + //管理员登录判断 + @RequestMapping(value = "/adminLogin.do") + @ResponseBody + public Map adminLogin(Model model, HttpSession httpSession, String username, + String password, String identifyingcode) + { + String code = (String) httpSession.getAttribute("identifyingCode"); + HashMap map = new HashMap(); + if(identifyingcode.equalsIgnoreCase(code)){ + SystemManager manager = null; + try { + manager = loginService.findSystemManagerByIdAndPassword(username, password); + } catch (CustomException e) { + map.put("msg",e.getMessage()); + map.put("status","500"); + return map; + } + // 保存到session + httpSession.setAttribute("admin",manager); + map.put("url","toPage.do?url=admin/index.html"); + map.put("msg","成功"); + map.put("status","200"); + return map; + }else{ + map.put("msg","验证码错误"); + map.put("status","0"); + return map; + } + } + @RequestMapping(value = "/getAdminAccount.do") + @ResponseBody + public String getAdminAccount(HttpSession httpSession){ + SystemManager systemManager = (SystemManager) httpSession.getAttribute("admin"); +// SystemManager manager = loginService.findSystemManagerById(id); + return systemManager.getSmAccount(); + } + + @RequestMapping(value = "/getEmployeeAccount.do") + @ResponseBody + public Map getEmployeeAccount(HttpSession httpSession){ + Integer id = (Integer) httpSession.getAttribute("employeeId"); + Employee employee = loginService.findEmployeeById(id); + HashMap map = new HashMap(); + map.put("account",employee.geteAccount()); + map.put("name",employee.geteName()); + return map; + } + @RequestMapping(value = "/logout.do") + public String logout(HttpSession httpSession){ + httpSession.removeAttribute("employeeId"); + return "redirect:/"; + } + + @RequestMapping(value = "/logoutAdmin.do") + public String logoutAdmin(HttpSession httpSession){ + httpSession.removeAttribute("admin"); + return "redirect:/admin.do"; + } +} diff --git a/main/java/com/esms/controller/MonthlyAttendanceController.java b/main/java/com/esms/controller/MonthlyAttendanceController.java new file mode 100644 index 0000000..3b710c7 --- /dev/null +++ b/main/java/com/esms/controller/MonthlyAttendanceController.java @@ -0,0 +1,207 @@ +package com.esms.controller; + +import com.esms.dao.MonthlyAttendanceCustomVoMapper; +import com.esms.dao.MonthlyAttendanceMapper; +import com.esms.po.MonthlyAttendance; +import com.esms.service.impl.MonthlyAttendanceServiceImpl; +import com.esms.vo.EmployeeMonthlyAttendancePages; +import com.esms.vo.MonthlyAttendanceCustomVo; +import com.esms.vo.MonthlyAttendancePages; +import com.github.pagehelper.PageHelper; +import com.github.pagehelper.PageInfo; +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.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; + +import javax.servlet.http.HttpSession; +import java.util.HashMap; +import java.util.List; + +/** + * @Description: + */ +@Controller +@RequestMapping("/monthlyattendance") +public class MonthlyAttendanceController{ + @Autowired + private MonthlyAttendanceServiceImpl monthlyAttendanceService; + + @Autowired + private MonthlyAttendanceCustomVoMapper monthlyAttendanceCustomVoMapper = null; + + @Autowired + private MonthlyAttendanceMapper monthlyAttendanceMapper = null; + + @RequestMapping("test.do") + @ResponseBody + public List test(){ + HashMap map = new HashMap(); + map.put("e_account","4"); + map.put("d_id",4); + map.put("attendance_time","2017-08"); + List monthlyAttendanceCustomVos = + monthlyAttendanceCustomVoMapper.selectMonthlyAttendanceCustomVoMapperByeAccountAnddIdAndTime(map); + return monthlyAttendanceCustomVos; + } + + + /** + * 查找 + * @param pageNum + * @param limit + * @param e_account + * @param d_id + * @param attendance_time + * @return + * @throws Exception + */ + @RequestMapping("/findSelective.do") + @ResponseBody + public MonthlyAttendancePages findSelective( + @RequestParam(value="page", defaultValue="1")int pageNum, + @RequestParam(value="limit", defaultValue="5") int limit, + @RequestParam(value="e_account", defaultValue="") String e_account, + @RequestParam(value="d_id", defaultValue="0") int d_id, + @RequestParam(value="attendance_time", defaultValue="") String attendance_time) throws Exception { + HashMap map = new HashMap(); + map.put("e_account",e_account); + map.put("d_id",d_id); + map.put("attendance_time",attendance_time); + //pageNum:起始页面 pageSize:每页的大小 + PageHelper.startPage(pageNum,limit); + //查找条件,一定要紧跟在startPage后 + List list = + monthlyAttendanceCustomVoMapper.selectMonthlyAttendanceCustomVoMapperByeAccountAnddIdAndTime(map); +// System.out.println(list.get(0).getMonthlyAttendance().getAttendanceTime()+"========================"); + PageInfo pageResult = new PageInfo(list); + + //设置前台需要的数据 + MonthlyAttendancePages monthlyAttendancePages = new MonthlyAttendancePages(); + monthlyAttendancePages.setCode(0); + monthlyAttendancePages.setMsg(""); + monthlyAttendancePages.setCount((int) pageResult.getTotal()); + monthlyAttendancePages.setData(pageResult.getList()); + return monthlyAttendancePages; + } + + /** + * 查找一个 + * @param id + * @return + */ + @RequestMapping("/selectVoByPrimaryKey.do") + @ResponseBody + public MonthlyAttendanceCustomVo selectVoByPrimaryKey(int id) { + MonthlyAttendanceCustomVo monthlyAttendanceCustomVo = new MonthlyAttendanceCustomVo(); + monthlyAttendanceCustomVo = monthlyAttendanceCustomVoMapper.selectVoByPrimaryKey(id); +// System.out.println(monthlyAttendanceCustomVo+"======================="); + return monthlyAttendanceCustomVo; + } + + /** + * 更新 + * @param maId + * @param sickLeaveNum + * @param compassionateLeaveNum + * @param overtimeHour + * @param weekendHour + * @param holidayHour + * @param lateNum + * @param earlyNum + * @param absenceNum + * @param businessTravelNum + * @return + * @throws Exception + */ + @RequestMapping("/updateByPrimaryKeySelective.do") + @ResponseBody + public int updateByPrimaryKeySelective(int maId, + int sickLeaveNum, + int compassionateLeaveNum, + double overtimeHour, + double weekendHour, + double holidayHour, + int lateNum, + int earlyNum, + int absenceNum, + int businessTravelNum + ) throws Exception { + MonthlyAttendance monthlyAttendance = new MonthlyAttendance(); + monthlyAttendance.setMaId(maId); + monthlyAttendance.setSickLeaveNum(sickLeaveNum); + monthlyAttendance.setCompassionateLeaveNum(compassionateLeaveNum); + monthlyAttendance.setOvertimeHour(overtimeHour); + monthlyAttendance.setWeekendHour(weekendHour); + monthlyAttendance.setHolidayHour(holidayHour); + monthlyAttendance.setLateNum(lateNum); + monthlyAttendance.setEarlyNum(earlyNum); + monthlyAttendance.setAbsenceNum(absenceNum); + monthlyAttendance.setBusinessTravelNum(businessTravelNum); +// System.out.println(monthlyAttendance+"======================================================"); + monthlyAttendanceService.updateByPrimaryKeySelective(monthlyAttendance); + return 1; + } + + /** + * 删除 + * @param id + * @throws Exception + */ + @RequestMapping("/deleteByPrimaryKey.do") + @ResponseBody + public int deleteByPrimaryKey(int id) throws Exception { + //真删除 + MonthlyAttendance monthlyAttendance = new MonthlyAttendance(); + monthlyAttendance.setMaId(id); + monthlyAttendanceService.deleteByPrimaryKey(id); + return 1; + } + + /** + * 批量删除 + * @param ids + */ + @RequestMapping("/deleteByQuery.do") + @ResponseBody + public int deleteByQuery (@RequestParam(value = "arr")int[] ids) { + +// for (int i =0 ; i< ids.length;i++) { +// System.out.println(ids[i]); +// } + //真删除 + //如果有id才执行 + if(ids.length > 0) { + monthlyAttendanceService.deleteByQuery(ids); + } + return 1; + + } + + @RequestMapping("/findEmployeeAttendance.do") + @ResponseBody + public EmployeeMonthlyAttendancePages findEmployeeAttendance( + @RequestParam(value="page", defaultValue="1")int pageNum, + @RequestParam(value="limit", defaultValue="5") int limit, + @RequestParam(value="attendance_time", defaultValue="") String attendance_time, + HttpSession httpSession) throws Exception { + Integer id = (Integer) httpSession.getAttribute("employeeId"); + //pageNum:起始页面 pageSize:每页的大小 + PageHelper.startPage(pageNum,limit); + //查找条件,一定要紧跟在startPage后 + HashMap map = new HashMap(); + map.put("e_id",id); + map.put("attendance_time",attendance_time); + List list = monthlyAttendanceMapper.selectMonthlyAttendanceMapperByeEidAndTime(map); + PageInfo pageResult = new PageInfo(list); + + //设置前台需要的数据 + EmployeeMonthlyAttendancePages employeeMonthlyAttendancePages = new EmployeeMonthlyAttendancePages(); + employeeMonthlyAttendancePages.setCode(0); + employeeMonthlyAttendancePages.setMsg(""); + employeeMonthlyAttendancePages.setCount((int) pageResult.getTotal()); + employeeMonthlyAttendancePages.setData(pageResult.getList()); + return employeeMonthlyAttendancePages; + } +} diff --git a/main/java/com/esms/controller/PositionController.java b/main/java/com/esms/controller/PositionController.java new file mode 100644 index 0000000..feef686 --- /dev/null +++ b/main/java/com/esms/controller/PositionController.java @@ -0,0 +1,183 @@ +package com.esms.controller; + +import com.esms.po.Position; +import com.esms.service.PositionService; +import com.esms.vo.PositionPages; +import com.github.pagehelper.PageHelper; +import com.github.pagehelper.PageInfo; +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.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; + +import java.util.List; + +/** + * @Description: + */ +@Controller +@RequestMapping("/position") +public class PositionController { + @Autowired + public PositionService positionService = null; + + + /** + * + * @param pageNum + * @param limit + * @param p_name + * @return + * @throws Exception + */ + @RequestMapping("findSelective.do") + @ResponseBody + public PositionPages findSelective( + @RequestParam(value="page", defaultValue="1") int pageNum, + @RequestParam(value="limit", defaultValue="5") int limit, + @RequestParam(value="p_name", defaultValue="") String p_name) throws Exception { + List list; + + //模糊查询,有多少个条件就接收多少个字段 + + Position position = new Position(); + position.setpName(p_name); + + //pageNum:起始页面 pageSize:每页的大小 + PageHelper.startPage(pageNum,limit); + //查找条件,一定要紧跟在startPage后 + list = positionService.findSelective(position); + + PageInfo pageResult = new PageInfo(list); + + //设置前台需要的数据 + PositionPages positionPages = new PositionPages(); + positionPages.setCode(0); + positionPages.setMsg(""); + positionPages.setCount((int)pageResult.getTotal()); + positionPages.setData(pageResult.getList()); + return positionPages; + } + + + /** + * 添加 + * @param p_name + * @param p_duty + * @param p_post_pay + * @return + * @throws Exception + */ + @RequestMapping("/add.do") + @ResponseBody + public int add(String p_name, String p_duty, Double p_post_pay) throws Exception { + + Position position = positionService.findByDname(p_name); + //查找是否同名 + if(position != null) { + return position.getpId(); + } else { + Position p = new Position(); + p.setpId(null); + p.setpName(p_name); + p.setpDuty(p_duty); + p.setpPostPay(p_post_pay); + p.setpIsdel(1); + positionService.insertSelective(p); + return 0; + } + } + + /** + * 查找一个 + * @param id + * @return + * @throws Exception + */ + @RequestMapping("/findByPrimaryKey.do") + @ResponseBody + public Position findByPrimaryKey(int id) throws Exception { + Position position = positionService.findByPrimaryKey(id); + return position; + } + + /** + * 更新 + * @param id + * @param p_name + * @param p_duty + * @param p_post_pay + * @throws Exception + */ + @RequestMapping("/updateByPrimaryKey.do") + @ResponseBody + public int updateByPrimaryKey(int id, String p_name, String p_duty, Double p_post_pay) throws Exception { + + Position position = positionService.findByDname(p_name); + System.out.println(p_name); + System.out.println(position); + System.out.println(position != null && !position.getpName().equals(p_name)); + //有同名的且不是同一个 + if(position != null && !position.getpId().equals(id) ) { + return position.getpId(); + } else { + Position p = new Position(); + p.setpId(id); + p.setpName(p_name); + p.setpDuty(p_duty); + p.setpPostPay(p_post_pay); + p.setpIsdel(null); + positionService.updateByPrimaryKeySelective(p); + return 0; + } + } + + /** + * 根据名称查找 + * @param p_name + * @return + */ + @RequestMapping("/findByDname.do") + @ResponseBody + public int findByDname(String p_name) { + Position position = positionService.findByDname(p_name); + if(position != null) { + return position.getpId(); + } else { + return 0; + } + } + + + /** + * 删除一个 + * @param id + * @throws Exception + */ + @RequestMapping("/deleteByPrimaryKey.do") + @ResponseBody + public int deleteByPrimaryKey(int id) throws Exception { + //删除部门,调用更新操作,将状态改为0 + Position position = new Position(); + position.setpId(id); + positionService.deleteByPrimaryKey(id); + return 1; + } + + + /** + * 批量删除 + * @param ids + */ + @RequestMapping("/deleteByQuery.do") + public void deleteByQuery (@RequestParam(value = "arr")int[] ids) { + //批量删除,实则修改状态为0 + //如果有id才执行 + if(ids.length > 0) { + positionService.deleteByQuery(ids); + } + + + } +} diff --git a/main/java/com/esms/controller/RankBonusController.java b/main/java/com/esms/controller/RankBonusController.java new file mode 100644 index 0000000..cc2fa9e --- /dev/null +++ b/main/java/com/esms/controller/RankBonusController.java @@ -0,0 +1,137 @@ +package com.esms.controller; + +import com.esms.po.Department; +import com.esms.po.RankBonus; +import com.esms.service.IRankBonusService; +import com.esms.vo.DepartmentPages; +import com.esms.vo.RankBonusPages; +import com.github.pagehelper.PageHelper; +import com.github.pagehelper.PageInfo; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.servlet.ModelAndView; + +import java.util.List; + + +/** + * @Description: + */ +@Controller +public class RankBonusController { + @Autowired + private IRankBonusService service; + //@Qualifier("rankBonusService") + + public void setiRankBonusService(IRankBonusService iRankBonusService) { + this.service = iRankBonusService; + } + + + //插入数据 + @RequestMapping("/insertRankBonus.do") + @ResponseBody + public String doInsertRankBonus(int bonus,String name){ + if(service.CountByName(name)>0){ + return "exist"; + } + if(!name.equals("")&& bonus>=0){ + RankBonus rb= new RankBonus(); + rb.setRankName(name); + rb.setRbBonus(bonus); + service.addRankBonus(rb); + + return "ok"; + + } + else{ + return "no"; + } + + } + //查询出所有记录 + @RequestMapping("/findAll.do") + public String doFindAll(@RequestParam(value="pn",defaultValue = "1") Integer pn, Model model){ + // 引入PageHelper分页插件 + // 在查询之前只需要调用,传入页码,以及每页的大小 + int pageSize = 10; + PageHelper.startPage(pn, pageSize); + // startPage后面紧跟的这个查询就是一个分页查询 + List rbs = service.findAll(); + System.out.println(rbs+"================================="); + /*for (RankBonus rb:rbs) { + System.out.println(rb.getRankName()); + }*/ + // 使用pageInfo包装查询后的结果,只需要将pageInfo交给页面就行了。 + // 封装了详细的分页信息,包括有我们查询出来的数据,传入连续显示的页数 + PageInfo page = new PageInfo(rbs, 5); + //ModelAndView modelAndView = new ModelAndView(); + // modelAndView.setViewName("admin/rank-list.jsp"); + // modelAndView.addObject("pageInfo",page); + model.addAttribute("pageInfo", page); + return "admin/rank-list.jsp"; + } + + @RequestMapping("/findRankBonusList.do") + @ResponseBody + public RankBonusPages findSelective( + @RequestParam(value="page", defaultValue="1")int pageNum, + @RequestParam(value="limit", defaultValue="10") int limit, + @RequestParam(value="rb_name", defaultValue="") String rb_name) throws Exception { + /*return rb_name;*/ + List list; + //模糊查询,有多少个条件就接收多少个字段 + RankBonus rb = new RankBonus(); + rb.setRankName(rb_name); + //pageNum:起始页面 pageSize:每页的大小 + PageHelper.startPage(pageNum,limit); + //查找条件,一定要紧跟在startPage后 + list = service.findSelective(rb); + PageInfo pageResult = new PageInfo(list); + + //设置前台需要的数据 + RankBonusPages rankBonusPages = new RankBonusPages(); + rankBonusPages.setCode(0); + rankBonusPages.setMsg(""); + rankBonusPages.setCount((int) pageResult.getTotal()); + rankBonusPages.setData(pageResult.getList()); + return rankBonusPages; + } + + //根据id删除数据 + @RequestMapping("rankBonusDelete.do") + @ResponseBody + public String doDelete(int id){ + if(service.CountByRbid(id)>0){ + return "exist"; + } + if(service.moveRankBonus(id)==true) + return "ok"; + else{ + return "no";} + } + //更新记录 + @RequestMapping("/updateRankBonus.do") + @ResponseBody + public String doUpdate(int id,String rank,int bonus){ + RankBonus rankBonus=service.findByName(rank); + if(rankBonus!=null&& !rankBonus.getRbId().equals(id)){ + return "exist"; + } + RankBonus rb=new RankBonus(); + rb.setRbId(id); + rb.setRbBonus(bonus); + rb.setRankName(rank); + if(service.modifyRankName(rb)==true) + return "ok"; + else + return "no"; + } + + +} diff --git a/main/java/com/esms/controller/SalaryController.java b/main/java/com/esms/controller/SalaryController.java new file mode 100644 index 0000000..c1db5f2 --- /dev/null +++ b/main/java/com/esms/controller/SalaryController.java @@ -0,0 +1,104 @@ +package com.esms.controller; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.serializer.SerializerFeature; +import com.esms.exception.CustomException; +import com.esms.service.impl.SalaryServiceImpl; +import com.esms.vo.SalaryPages; +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.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; + +import java.util.HashMap; +import java.util.Map; + +/** + * @Description: + */ +@Controller +public class SalaryController { + @Autowired + private SalaryServiceImpl salaryService = null; + + @RequestMapping("salarySettlementByAcount.do") + @ResponseBody + public Map salarySettlementByAcount(String eAccount, String date) { + Map stringMap = new HashMap(); + try { + salaryService.insertSalaryByAcountAndDate(eAccount, date); + stringMap.put("msg", "工资结算完成"); + return stringMap; + } catch (CustomException e) { + stringMap.put("msg", e.getMessage()); + return stringMap; + } + } + + @RequestMapping("salarySettlementAll.do") + @ResponseBody + public Map salarySettlementAll(String date) { + Map stringMap = new HashMap(); + try { + salaryService.insertSalaryAllByDate(date); + stringMap.put("msg", "工资结算完成"); + return stringMap; + } catch (CustomException e) { + stringMap.put("msg", e.getMessage()); + return stringMap; + } + } + + // 删除工资项目 + @RequestMapping("deleteSalaryByEid.do") + @ResponseBody + public int deleteSalaryByEid(@RequestParam(value = "arr")int[] ids) { + salaryService.deleteSalaryByEid(ids); + return 1; + } + + // 发放工资项目 + @RequestMapping("issueSalaryByEid.do") + @ResponseBody + public void issueSalaryByEid(@RequestParam(value = "arr")int[] ids) { + salaryService.updateSalaryBySid(ids); + } + + @RequestMapping(value = "selectSalaryByEaccountDIdDate.do", + produces = "application/json;charset=utf-8") + @ResponseBody + public String selectSalaryByEaccountDIdDate(@RequestParam(value = "page", defaultValue = "1") int pageNum, + @RequestParam(value = "limit", defaultValue = "5") int limit, + String eAccount, Integer dId, String sTime) { + /** + * @Author: 方宏泰 + * @Description: 工资查询 + * @Date: 12:37 2020/02/11 + * @Param: [pageNum , limit, eAccount, dId, sTime] + * @Return: java.lang.String + **/ + SalaryPages salaryPages = salaryService.selectSalaryByEaccountDIdDate(pageNum, limit, eAccount, dId, sTime); + //使用fastjson以字符串形式返回数据 + JSON.DEFFAULT_DATE_FORMAT = "yyyy-MM"; + return JSON.toJSONString(salaryPages, SerializerFeature.WriteDateUseDateFormat); + } + + @RequestMapping(value = "selectSalaryByEaccountDIdDateState.do", + produces = "application/json;charset=utf-8") + @ResponseBody + public String selectSalaryByEaccountDIdDateState(@RequestParam(value = "page", defaultValue = "1") int pageNum, + @RequestParam(value = "limit", defaultValue = "5") int limit, + String eAccount, Integer dId, String sTime) { + SalaryPages salaryPages = salaryService.selectSalaryByEaccountDIdDateState(pageNum, limit, eAccount, dId, sTime); + //使用fastjson以字符串形式返回数据 + JSON.DEFFAULT_DATE_FORMAT = "yyyy-MM"; + return JSON.toJSONString(salaryPages, SerializerFeature.WriteDateUseDateFormat); + } + + @RequestMapping(value = "updateSalaryByEidAndRissuePay.do") + @ResponseBody + public void updateSalaryByEidAndRissuePay(int sId, double rissuePay) { + salaryService.updateSalaryByEidAndRissuePay(sId, rissuePay); + } +} diff --git a/main/java/com/esms/controller/UploadPhotoController.java b/main/java/com/esms/controller/UploadPhotoController.java new file mode 100644 index 0000000..cc8ba06 --- /dev/null +++ b/main/java/com/esms/controller/UploadPhotoController.java @@ -0,0 +1,75 @@ +package com.esms.controller; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.multipart.MultipartFile; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +/** + * @Description: + */ +@Controller +public class UploadPhotoController { + @RequestMapping("uploadPhoto.do") + @ResponseBody + public Object updateHeadPhoto(@RequestParam(value = "file", required = false) MultipartFile file, HttpServletRequest request, HttpServletResponse response) throws Exception { + // 文件后缀 + String prefix = ""; + // 时间前缀 + String dateStr = ""; + //保存上传 + OutputStream out = null; + InputStream fileInput = null; + // 随机数前缀 + double index = 1000 * (Math.random()); + DateFormat format = new SimpleDateFormat("yyyy-MM-dd"); + try { + if (file != null) { + String originalName = file.getOriginalFilename(); + prefix = originalName.substring(originalName.lastIndexOf(".") + 1); + dateStr = format.format(new Date()); + String filepath = request.getServletContext().getRealPath("/img/head/") + dateStr + index + "." + prefix; + filepath = filepath.replace("\\", "/"); + File files = new File(filepath); + //打印查看上传路径 + System.out.println(filepath); + if (!files.getParentFile().exists()) { + files.getParentFile().mkdirs(); + } + file.transferTo(files); + } + } catch (Exception e) { + } finally { + try { + if (out != null) { + out.close(); + } + if (fileInput != null) { + fileInput.close(); + } + } catch (IOException e) { + } + } + Map map2 = new HashMap(); + Map map = new HashMap(); + // 返回上传后的图片地址 img/head/……jpg + map2.put("src", "/ssm_esms/img/head/" + dateStr + index + "." + prefix); + map.put("code", 0); + map.put("msg", ""); + map.put("data", map2); + return map; + } +} diff --git a/main/java/com/esms/controller/UserInforController.java b/main/java/com/esms/controller/UserInforController.java new file mode 100644 index 0000000..7d88a7f --- /dev/null +++ b/main/java/com/esms/controller/UserInforController.java @@ -0,0 +1,144 @@ +package com.esms.controller; + +import com.esms.exception.CustomException; +import com.esms.po.Employee; +import com.esms.po.Salary; +import com.esms.po.SystemManager; +import com.esms.service.impl.UserInforServiceImpl; +import com.esms.utils.MD5Utils; +import com.esms.vo.EmployeeCustomVo; +import com.esms.vo.EmployeeSalaryVO; +import com.github.pagehelper.PageHelper; +import com.github.pagehelper.PageInfo; +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.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; + +import javax.servlet.http.HttpSession; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @Description: + */ +@Controller +public class UserInforController { + @Autowired + private UserInforServiceImpl userInforService = null; + + @RequestMapping("changePassword.do") + @ResponseBody + public Map changePassword(String oldPassword, String newPassword1, + String newPassword2, HttpSession httpSession){ + System.out.println(oldPassword+" "+newPassword1+" "+newPassword2); + HashMap map = new HashMap(); + if (newPassword1.equals(newPassword2)){ + SystemManager admin = (SystemManager) httpSession.getAttribute("admin"); + String encodeByMD5 = MD5Utils.encodeByMD5(oldPassword); + if (encodeByMD5.equals(admin.getSmPassword())){ + String newPassword = MD5Utils.encodeByMD5(newPassword1); + admin.setSmPassword(newPassword); + userInforService.updateSystemManagePassword(admin.getSmId(),admin); + map.put("status","1"); + map.put("msg","密码修改成功"); + return map; + }else{ + map.put("status","2"); + map.put("msg","原密码错误"); + return map; + } + }else{ + map.put("status","0"); + map.put("msg","两次密码不一致"); + return map; + } + } + @RequestMapping("changeEmployeePassword.do") + @ResponseBody + public Map changeEmployeePassword(String oldPassword, String newPassword1, + String newPassword2, HttpSession httpSession){ + System.out.println(oldPassword+" "+newPassword1+" "+newPassword2); + HashMap map = new HashMap(); + if (newPassword1.equals(newPassword2)){ + Integer eid = (Integer) httpSession.getAttribute("employeeId"); + try { + userInforService.updateEmployeePassword(eid, oldPassword, newPassword1); + map.put("status","1"); + map.put("msg","密码修改成功"); + return map; + } catch (CustomException e) { + map.put("status","2"); + map.put("msg","原密码错误"); + return map; + } + }else{ + map.put("status","0"); + map.put("msg","两次密码不一致"); + return map; + } + } + + @RequestMapping("inforEmployee.do") + public @ResponseBody EmployeeCustomVo getInforEmployee(HttpSession httpSession){ + Integer id = (Integer) httpSession.getAttribute("employeeId"); + EmployeeCustomVo employeeCustomVo = userInforService.getInforEmployee(id); + return employeeCustomVo; + } + + @RequestMapping("updateInforEmployee.do") + public @ResponseBody String updateInforEmployee(HttpSession httpSession, Employee employee){ + Integer id = (Integer) httpSession.getAttribute("employeeId"); + employee.seteId(id); + int i = userInforService.updateEmploueeById(id,employee); + if (i==1){ + return "1"; + }else { + return "0"; + } + } + +// @RequestMapping("employeeSalaryList.do") +// @ResponseBody +// public EmployeeSalaryVO employeeSalaryList(@RequestParam(name="year",defaultValue = "1") String year, +// HttpSession httpSession){ +// Integer eId = (Integer) httpSession.getAttribute("employeeId"); +// List salaryList = userInforService.getEmployeeSalaryList(eId, year); +// +// EmployeeSalaryVO employeeSalaryVO = new EmployeeSalaryVO(); +// employeeSalaryVO.setCode(0); +// employeeSalaryVO.setMsg(""); +// employeeSalaryVO.setCount((int) salaryList.size()); +// employeeSalaryVO.setData(salaryList); +// return employeeSalaryVO; +// } + + + @RequestMapping("employeeSalaryList.do") + @ResponseBody + public EmployeeSalaryVO findSelective( + @RequestParam(value="page", defaultValue="1")int pageNum, + @RequestParam(value="limit", defaultValue="10") int limit, + @RequestParam(value="year", defaultValue="1") String year, + HttpSession httpSession) throws Exception { + + Integer eId = (Integer) httpSession.getAttribute("employeeId"); + //pageNum:起始页面 pageSize:每页的大小 + PageHelper.startPage(pageNum,limit); + //查找条件,一定要紧跟在startPage后 + List salaryList = userInforService.getEmployeeSalaryList(eId, year); + + PageInfo pageResult = new PageInfo(salaryList); + + //设置前台需要的数据 + EmployeeSalaryVO employeeSalaryVO = new EmployeeSalaryVO(); + employeeSalaryVO.setCode(0); + employeeSalaryVO.setMsg(""); + employeeSalaryVO.setCount((int) pageResult.getTotal()); + employeeSalaryVO.setData(pageResult.getList()); + return employeeSalaryVO; + } + +} diff --git a/main/java/com/esms/controller/WorkYearController.java b/main/java/com/esms/controller/WorkYearController.java new file mode 100644 index 0000000..e7a0764 --- /dev/null +++ b/main/java/com/esms/controller/WorkYearController.java @@ -0,0 +1,131 @@ +package com.esms.controller; + +import com.esms.po.RankBonus; +import com.esms.po.WorkingYearsBonus; +import com.esms.service.IRankBonusService; +import com.esms.service.IWorkYearService; +import com.esms.vo.WokingYearsBonusPages; +import com.github.pagehelper.PageHelper; +import com.github.pagehelper.PageInfo; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; + +import java.util.List; + +/** + * @Description: + */ +@Controller +public class WorkYearController { + @Autowired + private IWorkYearService service; + + /*查询所有数据*/ + @RequestMapping("/findWorkingYearBonusList.do") + @ResponseBody + public WokingYearsBonusPages findAll( + @RequestParam(value="page", defaultValue="1")int pageNum, + @RequestParam(value="limit", defaultValue="5") int limit, + @RequestParam(value="year", defaultValue="0") int year) throws Exception { + List list; + + //模糊查询,有多少个条件就接收多少个字段 + + WorkingYearsBonus wyb = new WorkingYearsBonus(); + wyb.setWybYear(year); + + //pageNum:起始页面 pageSize:每页的大小 + PageHelper.startPage(pageNum,limit); + //查找条件,一定要紧跟在startPage后 + list = service.findSelective(wyb); + PageInfo pageResult = new PageInfo(list); + + //设置前台需要的数据 + WokingYearsBonusPages wybp = new WokingYearsBonusPages(); + wybp.setCode(0); + wybp.setMsg(""); + wybp.setCount((int) pageResult.getTotal()); + wybp.setData(pageResult.getList()); + + return wybp; + } + + //插入数据 + @RequestMapping("/addWorkingYearsBonus.do") + @ResponseBody + public String doWorkYearBonus(int year,double bonus){ + + if(service.Count(year)>0) + { + return "exist"; + } + if(year>=0 && bonus>=0){ + WorkingYearsBonus wyb= new WorkingYearsBonus(); + wyb.setWybYear(year); + wyb.setWybBonus(bonus); + service.addWorkYearBonus(wyb); + return "ok"; + } + else{ + return "no"; + } + } + /*//查询出所有记录 + @RequestMapping("/findList.do") + public String doFindList(@RequestParam(value="pn",defaultValue = "1") Integer pn, Model model){ + // 引入PageHelper分页插件 + // 在查询之前只需要调用,传入页码,以及每页的大小 + int pageSize = 10; + PageHelper.startPage(pn, pageSize); + // startPage后面紧跟的这个查询就是一个分页查询 + List wyb = service.findAll(); + System.out.println(wyb+"================================="); + *//*for (RankBonus rb:rbs) { + System.out.println(rb.getRankName()); + }*//* + // 使用pageInfo包装查询后的结果,只需要将pageInfo交给页面就行了。 + // 封装了详细的分页信息,包括有我们查询出来的数据,传入连续显示的页数 + PageInfo page = new PageInfo(wyb, 5); + //ModelAndView modelAndView = new ModelAndView(); + // modelAndView.setViewName("admin/rank-list.jsp"); + // modelAndView.addObject("pageInfo",page); + model.addAttribute("pageInfo", page); + model.addAttribute("ttttt","tttttttttt********8"); + return "admin/year-list.jsp"; + + }*/ + //根据id删除数据 + @RequestMapping("/deleteRecord.do") + @ResponseBody + public String doDelete(int id){ + if (service.moveWorkYearBonus(id)==true) { + return "ok"; + } + else{ + return "no"; + } + + } + //更新记录 + @RequestMapping("/updateRecord.do") + @ResponseBody + public String doUpdate(int id,int year,double bonus){ + WorkingYearsBonus workingYearsBonus=service.findByYear(year); + if(workingYearsBonus!=null&&!workingYearsBonus.getWybId().equals(id)) + { + return "exist"; + } + WorkingYearsBonus wyb=new WorkingYearsBonus(); + wyb.setWybId(id); + wyb.setWybYear(year); + wyb.setWybBonus(bonus); + if(service.modifyWorkYearBonus(wyb)==true) + return "ok"; + else + return "no"; + } +} diff --git a/main/java/com/esms/dao/DepartmentMapper.java b/main/java/com/esms/dao/DepartmentMapper.java new file mode 100644 index 0000000..c708c2e --- /dev/null +++ b/main/java/com/esms/dao/DepartmentMapper.java @@ -0,0 +1,27 @@ +package com.esms.dao; + +import com.esms.po.Department; + +import java.util.List; + +public interface DepartmentMapper { + int deleteByPrimaryKey(Integer dId); + + int insert(Department record); + + int insertSelective(Department record); + + Department selectByPrimaryKey(Integer dId); + + List selectAll(); + + int updateByPrimaryKeySelective(Department record); + + int updateByPrimaryKey(Department record); + + List findSelective(Department department); + + void deleteByQuery(int[] ids); + + Department findByDname(String d_name); +} \ No newline at end of file diff --git a/main/java/com/esms/dao/EmployeeCustomVoMapper.java b/main/java/com/esms/dao/EmployeeCustomVoMapper.java new file mode 100644 index 0000000..2f30f9b --- /dev/null +++ b/main/java/com/esms/dao/EmployeeCustomVoMapper.java @@ -0,0 +1,14 @@ +package com.esms.dao; + +import com.esms.vo.EmployeeCustomVo; + +import java.util.HashMap; +import java.util.List; + +public interface EmployeeCustomVoMapper { + public EmployeeCustomVo selectEmployeeById(int eId); + + List selectEmployeeSelective(HashMap map); + + EmployeeCustomVo selectVoByPrimaryKey(int id); +} diff --git a/main/java/com/esms/dao/EmployeeMapper.java b/main/java/com/esms/dao/EmployeeMapper.java new file mode 100644 index 0000000..06bfec0 --- /dev/null +++ b/main/java/com/esms/dao/EmployeeMapper.java @@ -0,0 +1,37 @@ +package com.esms.dao; + +import com.esms.po.Employee; + +import java.util.List; + +public interface EmployeeMapper { + int deleteByPrimaryKey(Integer eId); + + int insert(Employee record); + + int insertSelective(Employee record); + + Employee selectByPrimaryKey(Integer eId); + + int updateByPrimaryKeySelective(Employee record); + + int updateByPrimaryKey(Employee record); + + Employee selectByAccountAndPassword(Employee employee); + + int countByDid(Integer eId); + + int countByRbid(Integer eRank); + + Employee selectByAccount(String eAcount); + +// void deleteByQuery(int[] ids); + + Employee findByeAccount(String eAccount); + + List selectAll(); + + int isExistEmployee(String eAccount); + + int selectEidByEaccount(String eAccount); +} \ No newline at end of file diff --git a/main/java/com/esms/dao/KeyValueMapper.java b/main/java/com/esms/dao/KeyValueMapper.java new file mode 100644 index 0000000..96b7969 --- /dev/null +++ b/main/java/com/esms/dao/KeyValueMapper.java @@ -0,0 +1,19 @@ +package com.esms.dao; + +import com.esms.po.KeyValue; + +public interface KeyValueMapper { + int deleteByPrimaryKey(Integer kvId); + + int insert(KeyValue record); + + int insertSelective(KeyValue record); + + KeyValue selectByPrimaryKey(Integer kvId); + + int updateByPrimaryKeySelective(KeyValue record); + + int updateByPrimaryKey(KeyValue record); + + KeyValue selectBykvKey(String kvKey); +} \ No newline at end of file diff --git a/main/java/com/esms/dao/MonthlyAttendanceCustomVoMapper.java b/main/java/com/esms/dao/MonthlyAttendanceCustomVoMapper.java new file mode 100644 index 0000000..274c9f6 --- /dev/null +++ b/main/java/com/esms/dao/MonthlyAttendanceCustomVoMapper.java @@ -0,0 +1,16 @@ +package com.esms.dao; + +import com.esms.vo.MonthlyAttendanceCustomVo; + +import java.util.List; +import java.util.Map; + +/** + * @Description: + */ +public interface MonthlyAttendanceCustomVoMapper { + public List + selectMonthlyAttendanceCustomVoMapperByeAccountAnddIdAndTime(Map map); + + MonthlyAttendanceCustomVo selectVoByPrimaryKey(int id); +} diff --git a/main/java/com/esms/dao/MonthlyAttendanceMapper.java b/main/java/com/esms/dao/MonthlyAttendanceMapper.java new file mode 100644 index 0000000..bbd3f97 --- /dev/null +++ b/main/java/com/esms/dao/MonthlyAttendanceMapper.java @@ -0,0 +1,26 @@ +package com.esms.dao; + +import com.esms.po.MonthlyAttendance; + +import java.util.List; +import java.util.Map; + +public interface MonthlyAttendanceMapper { + int deleteByPrimaryKey(Integer maId); + + int insert(MonthlyAttendance record); + + int insertSelective(MonthlyAttendance record); + + MonthlyAttendance selectByPrimaryKey(Integer maId); + + int updateByPrimaryKeySelective(MonthlyAttendance record); + + int updateByPrimaryKey(MonthlyAttendance record); + + MonthlyAttendance selectByeIdAndDate(int eId, String date); + + void deleteByQuery(int[] ids); + + List selectMonthlyAttendanceMapperByeEidAndTime (Map map); +} \ No newline at end of file diff --git a/main/java/com/esms/dao/PositionMapper.java b/main/java/com/esms/dao/PositionMapper.java new file mode 100644 index 0000000..a30c59d --- /dev/null +++ b/main/java/com/esms/dao/PositionMapper.java @@ -0,0 +1,25 @@ +package com.esms.dao; + +import com.esms.po.Position; + +import java.util.List; + +public interface PositionMapper { + int deleteByPrimaryKey(Integer pId); + + int insert(Position record); + + int insertSelective(Position record); + + Position selectByPrimaryKey(Integer pId); + + int updateByPrimaryKeySelective(Position record); + + int updateByPrimaryKey(Position record); + + List findSelective(Position position); + + void deleteByQuery(int[] ids); + + Position findByDname(String p_name); +} \ No newline at end of file diff --git a/main/java/com/esms/dao/RankBonusMapper.java b/main/java/com/esms/dao/RankBonusMapper.java new file mode 100644 index 0000000..76af2e3 --- /dev/null +++ b/main/java/com/esms/dao/RankBonusMapper.java @@ -0,0 +1,28 @@ +package com.esms.dao; + +import com.esms.po.RankBonus; + +import java.util.List; + +public interface RankBonusMapper { + + int deleteByPrimaryKey(Integer rbId); + + int insert(RankBonus record); + + int insertSelective(RankBonus record); + + RankBonus selectByPrimaryKey(Integer rbId); + + List selectAll(); + + int updateByPrimaryKeySelective(RankBonus record); + + int updateByPrimaryKey(RankBonus record); + + int CountByRankName(String name); + + List findSelective(RankBonus record); + + RankBonus findByname(String name); +} \ No newline at end of file diff --git a/main/java/com/esms/dao/SalaryMapper.java b/main/java/com/esms/dao/SalaryMapper.java new file mode 100644 index 0000000..d548dd4 --- /dev/null +++ b/main/java/com/esms/dao/SalaryMapper.java @@ -0,0 +1,43 @@ +package com.esms.dao; + +import com.esms.po.Salary; +import com.esms.vo.EchSalary; + +import java.util.List; +import java.util.Map; + +public interface SalaryMapper { + int deleteByPrimaryKey(Integer sId); + + int insert(Salary record); + + int insertSelective(Salary record); + + Salary selectByPrimaryKey(Integer sId); + + int updateByPrimaryKeySelective(Salary record); + + int updateByPrimaryKey(Salary record); + + Salary selectByEidAndTime(Integer eId, String time); + + Double selectMaxSalaryByDepartment(Integer dId,String date); + //查每个最低,平均,最高工资 + EchSalary selectSalaryByDepartment(Integer dId, String date); + //查每个工资总数,时间年月 + Double selectAllSalaryByDepartment(Integer dId,String date); + //查每个工资总数,时间年 + Double selectAllSalaryByDepAndYear(Integer dId,String date); + // 查找员工个人的某年工资记录 + List selectEmployeeSalaryList(Integer eId, String data); + List selectByeId(Integer eId); + //根据年月时间查工资总数 + Double selectAllSalaryByDate(String date); + List test(String eAccount); + List selectByEaccountDIdDate(Map map); + + List selectByeTimeAndStatus(String date, int status); + Salary selectByEidAndTimeAndStatus(Integer eId, String time, int status); + + List selectByEaccountDIdDateState(Map map); +} \ No newline at end of file diff --git a/main/java/com/esms/dao/SystemManagerMapper.java b/main/java/com/esms/dao/SystemManagerMapper.java new file mode 100644 index 0000000..4282e62 --- /dev/null +++ b/main/java/com/esms/dao/SystemManagerMapper.java @@ -0,0 +1,19 @@ +package com.esms.dao; + +import com.esms.po.SystemManager; + +public interface SystemManagerMapper { + int deleteByPrimaryKey(Integer smId); + + int insert(SystemManager record); + + int insertSelective(SystemManager record); + + SystemManager selectByPrimaryKey(Integer smId); + + int updateByPrimaryKeySelective(SystemManager record); + + int updateByPrimaryKey(SystemManager record); + + SystemManager selectByAccountAndPassword(SystemManager systemManager); +} \ No newline at end of file diff --git a/main/java/com/esms/dao/WorkingYearsBonusMapper.java b/main/java/com/esms/dao/WorkingYearsBonusMapper.java new file mode 100644 index 0000000..9cf8ad1 --- /dev/null +++ b/main/java/com/esms/dao/WorkingYearsBonusMapper.java @@ -0,0 +1,27 @@ +package com.esms.dao; + +import com.esms.po.WorkingYearsBonus; + +import java.util.List; + +public interface WorkingYearsBonusMapper { + int deleteByPrimaryKey(Integer wybId); + + int insert(WorkingYearsBonus record); + + int insertSelective(WorkingYearsBonus record); + + WorkingYearsBonus selectByPrimaryKey(Integer wybId); + + int updateByPrimaryKeySelective(WorkingYearsBonus record); + + int updateByPrimaryKey(WorkingYearsBonus record); + + List selectAll(); + + int countByYear(Integer year); + + List findSelective(WorkingYearsBonus record); + + WorkingYearsBonus findByYear(int year); +} \ No newline at end of file diff --git a/main/java/com/esms/exception/CustomException.java b/main/java/com/esms/exception/CustomException.java new file mode 100644 index 0000000..2b0e847 --- /dev/null +++ b/main/java/com/esms/exception/CustomException.java @@ -0,0 +1,19 @@ +package com.esms.exception; + +public class CustomException extends Exception{ + private String message; + + public CustomException(String message) { + super(message); + this.message = message; + } + + @Override + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } +} diff --git a/main/java/com/esms/exception/CustomExceptionResolver.java b/main/java/com/esms/exception/CustomExceptionResolver.java new file mode 100644 index 0000000..986c254 --- /dev/null +++ b/main/java/com/esms/exception/CustomExceptionResolver.java @@ -0,0 +1,25 @@ +package com.esms.exception; + +import org.springframework.web.servlet.HandlerExceptionResolver; +import org.springframework.web.servlet.ModelAndView; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +public class CustomExceptionResolver implements HandlerExceptionResolver { + public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) { + CustomException customException = null; + if (e instanceof CustomException) { + customException = (CustomException) e; + }else{ + customException = new CustomException("系统出现未知错误!"); + } + + String message = customException.getMessage(); + ModelAndView modelAndView = new ModelAndView(); + modelAndView.addObject("message",message); + modelAndView.setViewName("error/error.jsp"); + + return modelAndView; + } +} diff --git a/main/java/com/esms/interceptor/LoginInterceptor.java b/main/java/com/esms/interceptor/LoginInterceptor.java new file mode 100644 index 0000000..9cd4f22 --- /dev/null +++ b/main/java/com/esms/interceptor/LoginInterceptor.java @@ -0,0 +1,56 @@ +package com.esms.interceptor; + +import com.esms.po.SystemManager; +import org.springframework.web.servlet.HandlerInterceptor; +import org.springframework.web.servlet.ModelAndView; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * @Description: + */ +public class LoginInterceptor implements HandlerInterceptor { + @Override + public boolean preHandle(HttpServletRequest httpServletRequest, + HttpServletResponse httpServletResponse, Object o) throws Exception { +// String requestUri = httpServletRequest.getRequestURI(); +// String contextPath = httpServletRequest.getContextPath(); +// String url = requestUri.substring(contextPath.length()); +// System.out.println(url); +// System.out.println(requestUri); +// if ("/".equals(url)){ +// return true; +// } +// if ("/employeeLogin.do".equals(url)){ +// return true; +// }else{ + String string = (String) httpServletRequest.getSession().getAttribute("employeeId"); + SystemManager systemManager = (SystemManager) httpServletRequest.getSession().getAttribute("admin"); + if (string != null) { + return true; + } else if (string == null){ + httpServletResponse.sendRedirect("/"); + return false; + } else if (systemManager != null) { + return true; + } else { + httpServletResponse.sendRedirect("/admin.do"); + return false; + } + } + + @Override + public void postHandle(HttpServletRequest httpServletRequest, + HttpServletResponse httpServletResponse, + Object o, ModelAndView modelAndView) throws Exception { + + } + + @Override + public void afterCompletion(HttpServletRequest httpServletRequest, + HttpServletResponse httpServletResponse, + Object o, Exception e) throws Exception { + + } +} diff --git a/main/java/com/esms/interceptor/PermissionInterceptor.java b/main/java/com/esms/interceptor/PermissionInterceptor.java new file mode 100644 index 0000000..6d09904 --- /dev/null +++ b/main/java/com/esms/interceptor/PermissionInterceptor.java @@ -0,0 +1,34 @@ +package com.esms.interceptor; + +import org.springframework.web.servlet.HandlerInterceptor; +import org.springframework.web.servlet.ModelAndView; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * @Description: + */ +public class PermissionInterceptor implements HandlerInterceptor { + @Override + public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception { +// if (httpServletRequest.getSession().getAttribute("admin") != null){ +// return true; +// }else{ +//// httpServletRequest.getRequestDispatcher("/login.do").forward(httpServletRequest, httpServletResponse); +// httpServletResponse.sendRedirect("/admin.do"); +// return false; +// } + return true; + } + + @Override + public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception { + + } + + @Override + public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception { + + } +} diff --git a/main/java/com/esms/po/Department.java b/main/java/com/esms/po/Department.java new file mode 100644 index 0000000..836aa21 --- /dev/null +++ b/main/java/com/esms/po/Department.java @@ -0,0 +1,53 @@ +package com.esms.po; + +public class Department { + private Integer dId; + + private String dName; + + private String dRemark; + + private Integer dIsdel; + + public Integer getdId() { + return dId; + } + + public void setdId(Integer dId) { + this.dId = dId; + } + + public String getdName() { + return dName; + } + + public void setdName(String dName) { + this.dName = dName == null ? null : dName.trim(); + } + + public String getdRemark() { + return dRemark; + } + + public void setdRemark(String dRemark) { + this.dRemark = dRemark == null ? null : dRemark.trim(); + } + + public Integer getdIsdel() { + return dIsdel; + } + + public void setdIsdel(Integer dIsdel) { + this.dIsdel = dIsdel; + } + + @Override + public String toString() { + return "Department{" + + "dId=" + dId + + ", dName='" + dName + '\'' + + ", dRemark='" + dRemark + '\'' + + ", dIsdel=" + dIsdel + + '}'; + } +} \ No newline at end of file diff --git a/main/java/com/esms/po/Employee.java b/main/java/com/esms/po/Employee.java new file mode 100644 index 0000000..a328527 --- /dev/null +++ b/main/java/com/esms/po/Employee.java @@ -0,0 +1,273 @@ +package com.esms.po; + +import com.fasterxml.jackson.annotation.JsonFormat; + +import java.util.Date; + +public class Employee { + private Integer eId; + + private String eAccount; + + private String ePassword; + + private String eName; + + private String eIdcard; + + private String eSex; + + private String eDagree; + + private Date eBirthday; + + private String eEmail; + + private String ePhone; + + private String eHometown; + + private Integer eRank; +// private RankBonus rankBonus; + + private String eHeadPath; + + private String eUrgentPerson; + + private String eUrgentPhone; + + private Integer pId; +// private Position position; + private Integer dId; +// private Department department; + + private Double eBasePay; + + private Integer eIsdel; + + private Date eEntryTime; + + private Date eLeaveTime; + + public Integer geteId() { + return eId; + } + + public void seteId(Integer eId) { + this.eId = eId; + } + + public String geteAccount() { + return eAccount; + } + + public void seteAccount(String eAccount) { + this.eAccount = eAccount == null ? null : eAccount.trim(); + } + + public String getePassword() { + return ePassword; + } + + public void setePassword(String ePassword) { + this.ePassword = ePassword == null ? null : ePassword.trim(); + } + + public String geteName() { + return eName; + } + + public void seteName(String eName) { + this.eName = eName == null ? null : eName.trim(); + } + + public String geteIdcard() { + return eIdcard; + } + + public void seteIdcard(String eIdcard) { + this.eIdcard = eIdcard == null ? null : eIdcard.trim(); + } + + public String geteSex() { + return eSex; + } + + public void seteSex(String eSex) { + this.eSex = eSex; + } + + public String geteDagree() { + return eDagree; + } + + public void seteDagree(String eDagree) { + this.eDagree = eDagree == null ? null : eDagree.trim(); + } + + @JsonFormat(pattern="yyyy-MM-dd",timezone = "GMT+8") + public Date geteBirthday() { + return eBirthday; + } + + public void seteBirthday(Date eBirthday) { + this.eBirthday = eBirthday; + } + + public String geteEmail() { + return eEmail; + } + + public void seteEmail(String eEmail) { + this.eEmail = eEmail == null ? null : eEmail.trim(); + } + + public String getePhone() { + return ePhone; + } + + public void setePhone(String ePhone) { + this.ePhone = ePhone == null ? null : ePhone.trim(); + } + + public String geteHometown() { + return eHometown; + } + + public void seteHometown(String eHometown) { + this.eHometown = eHometown == null ? null : eHometown.trim(); + } + + public Integer geteRank() { + return eRank; + } + + public void seteRank(Integer eRank) { + this.eRank = eRank; + } + + public String geteHeadPath() { + return eHeadPath; + } + + public void seteHeadPath(String eHeadPath) { + this.eHeadPath = eHeadPath == null ? null : eHeadPath.trim(); + } + + public String geteUrgentPerson() { + return eUrgentPerson; + } + + public void seteUrgentPerson(String eUrgentPerson) { + this.eUrgentPerson = eUrgentPerson == null ? null : eUrgentPerson.trim(); + } + + public String geteUrgentPhone() { + return eUrgentPhone; + } + + public void seteUrgentPhone(String eUrgentPhone) { + this.eUrgentPhone = eUrgentPhone == null ? null : eUrgentPhone.trim(); + } + + public Integer getpId() { + return pId; + } + + public void setpId(Integer pId) { + this.pId = pId; + } + + public Integer getdId() { + return dId; + } + + public void setdId(Integer dId) { + this.dId = dId; + } + +// public RankBonus getRankBonus() { +// return rankBonus; +// } +// +// public void setRankBonus(RankBonus rankBonus) { +// this.rankBonus = rankBonus; +// } +// +// public Position getPosition() { +// return position; +// } +// +// public void setPosition(Position position) { +// this.position = position; +// } +// +// public Department getDepartment() { +// return department; +// } +// +// public void setDepartment(Department department) { +// this.department = department; +// } + + public Double geteBasePay() { + return eBasePay; + } + + public void seteBasePay(Double eBasePay) { + this.eBasePay = eBasePay; + } + + public Integer geteIsdel() { + return eIsdel; + } + + public void seteIsdel(Integer eIsdel) { + this.eIsdel = eIsdel; + } + + @JsonFormat(pattern="yyyy-MM-dd",timezone = "GMT+8") + public Date geteEntryTime() { + return eEntryTime; + } + + public void seteEntryTime(Date eEntryTime) { + this.eEntryTime = eEntryTime; + } + + public Date geteLeaveTime() { + return eLeaveTime; + } + + public void seteLeaveTime(Date eLeaveTime) { + this.eLeaveTime = eLeaveTime; + } + + + @Override + public String toString() { + return "Employee{" + + "eId=" + eId + + ", eAccount='" + eAccount + '\'' + + ", ePassword='" + ePassword + '\'' + + ", eName='" + eName + '\'' + + ", eIdcard='" + eIdcard + '\'' + + ", eSex=" + eSex + + ", eDagree='" + eDagree + '\'' + + ", eBirthday=" + eBirthday + + ", eEmail='" + eEmail + '\'' + + ", ePhone='" + ePhone + '\'' + + ", eHometown='" + eHometown + '\'' + + ", eRank=" + eRank + + ", eHeadPath='" + eHeadPath + '\'' + + ", eUrgentPerson='" + eUrgentPerson + '\'' + + ", eUrgentPhone='" + eUrgentPhone + '\'' + + ", pId=" + pId + + ", dId=" + dId + + ", eBasePay=" + eBasePay + + ", eIsdel=" + eIsdel + + ", eEntryTime=" + eEntryTime + + ", eLeaveTime=" + eLeaveTime + + '}'; + } +} \ No newline at end of file diff --git a/main/java/com/esms/po/KeyValue.java b/main/java/com/esms/po/KeyValue.java new file mode 100644 index 0000000..25aa0b5 --- /dev/null +++ b/main/java/com/esms/po/KeyValue.java @@ -0,0 +1,42 @@ +package com.esms.po; + +public class KeyValue { + private Integer kvId; + + private String kvKey; + + private double kvValue; + + public Integer getKvId() { + return kvId; + } + + public void setKvId(Integer kvId) { + this.kvId = kvId; + } + + public String getKvKey() { + return kvKey; + } + + public void setKvKey(String kvKey) { + this.kvKey = kvKey == null ? null : kvKey.trim(); + } + + public double getKvValue() { + return kvValue; + } + + public void setKvValue(double kvValue) { + this.kvValue = kvValue; + } + + @Override + public String toString() { + return "KeyValue{" + + "kvId=" + kvId + + ", kvKey='" + kvKey + '\'' + + ", kvValue='" + kvValue + '\'' + + '}'; + } +} \ No newline at end of file diff --git a/main/java/com/esms/po/MonthlyAttendance.java b/main/java/com/esms/po/MonthlyAttendance.java new file mode 100644 index 0000000..98476b5 --- /dev/null +++ b/main/java/com/esms/po/MonthlyAttendance.java @@ -0,0 +1,156 @@ +package com.esms.po; + +import com.fasterxml.jackson.annotation.JsonFormat; + +import java.util.Date; + +public class MonthlyAttendance { + private Integer maId; + + private Integer eId; + private Employee employee; + + private Date attendanceTime; + + private Integer sickLeaveNum; + + private Double overtimeHour; + + private Double weekendHour; + + private Double holidayHour; + + private Integer lateNum; + + private Integer earlyNum; + + private Integer absenceNum; + + private Integer businessTravelNum; + + private Integer compassionateLeaveNum; + + public Integer getMaId() { + return maId; + } + + public void setMaId(Integer maId) { + this.maId = maId; + } + + public Integer geteId() { + return eId; + } + + public void seteId(Integer eId) { + this.eId = eId; + } + + public Employee getEmployee() { + return employee; + } + + public void setEmployee(Employee employee) { + this.employee = employee; + } + + @JsonFormat(pattern="yyyy-MM",timezone = "GMT+8") + public Date getAttendanceTime() { + return attendanceTime; + } + + public void setAttendanceTime(Date attendanceTime) { + this.attendanceTime = attendanceTime; + } + + public Integer getSickLeaveNum() { + return sickLeaveNum; + } + + public void setSickLeaveNum(Integer sickLeaveNum) { + this.sickLeaveNum = sickLeaveNum; + } + + public Double getOvertimeHour() { + return overtimeHour; + } + + public void setOvertimeHour(Double overtimeHour) { + this.overtimeHour = overtimeHour; + } + + public Double getWeekendHour() { + return weekendHour; + } + + public void setWeekendHour(Double weekendHour) { + this.weekendHour = weekendHour; + } + + public Double getHolidayHour() { + return holidayHour; + } + + public void setHolidayHour(Double holidayHour) { + this.holidayHour = holidayHour; + } + + public Integer getLateNum() { + return lateNum; + } + + public void setLateNum(Integer lateNum) { + this.lateNum = lateNum; + } + + public Integer getEarlyNum() { + return earlyNum; + } + + public void setEarlyNum(Integer earlyNum) { + this.earlyNum = earlyNum; + } + + public Integer getAbsenceNum() { + return absenceNum; + } + + public void setAbsenceNum(Integer absenceNum) { + this.absenceNum = absenceNum; + } + + public Integer getBusinessTravelNum() { + return businessTravelNum; + } + + public void setBusinessTravelNum(Integer businessTravelNum) { + this.businessTravelNum = businessTravelNum; + } + + public Integer getCompassionateLeaveNum() { + return compassionateLeaveNum; + } + + public void setCompassionateLeaveNum(Integer compassionateLeaveNum) { + this.compassionateLeaveNum = compassionateLeaveNum; + } + + @Override + public String toString() { + return "MonthlyAttendance{" + + "maId=" + maId + + ", eId=" + eId + + ", employee=" + employee + + ", attendanceTime=" + attendanceTime + + ", sickLeaveNum=" + sickLeaveNum + + ", overtimeHour=" + overtimeHour + + ", weekendHour=" + weekendHour + + ", holidayHour=" + holidayHour + + ", lateNum=" + lateNum + + ", earlyNum=" + earlyNum + + ", absenceNum=" + absenceNum + + ", businessTravelNum=" + businessTravelNum + + ", compassionateLeaveNum=" + compassionateLeaveNum + + '}'; + } +} \ No newline at end of file diff --git a/main/java/com/esms/po/Position.java b/main/java/com/esms/po/Position.java new file mode 100644 index 0000000..8d44aa9 --- /dev/null +++ b/main/java/com/esms/po/Position.java @@ -0,0 +1,64 @@ +package com.esms.po; + +public class Position { + private Integer pId; + + private String pName; + + private String pDuty; + + private Double pPostPay; + + private Integer pIsdel; + + public Integer getpId() { + return pId; + } + + public void setpId(Integer pId) { + this.pId = pId; + } + + public String getpName() { + return pName; + } + + public void setpName(String pName) { + this.pName = pName == null ? null : pName.trim(); + } + + public String getpDuty() { + return pDuty; + } + + public void setpDuty(String pDuty) { + this.pDuty = pDuty == null ? null : pDuty.trim(); + } + + public Double getpPostPay() { + return pPostPay; + } + + public void setpPostPay(Double pPostPay) { + this.pPostPay = pPostPay; + } + + public Integer getpIsdel() { + return pIsdel; + } + + public void setpIsdel(Integer pIsdel) { + this.pIsdel = pIsdel; + } + + @Override + public String toString() { + return "Position{" + + "pId=" + pId + + ", pName='" + pName + '\'' + + ", pDuty='" + pDuty + '\'' + + ", pPostPay=" + pPostPay + + ", pIsdel=" + pIsdel + + '}'; + } +} \ No newline at end of file diff --git a/main/java/com/esms/po/RankBonus.java b/main/java/com/esms/po/RankBonus.java new file mode 100644 index 0000000..af6a8cc --- /dev/null +++ b/main/java/com/esms/po/RankBonus.java @@ -0,0 +1,42 @@ +package com.esms.po; + +public class RankBonus { + private Integer rbId; + + private String rankName; + + private Integer rbBonus; + + public Integer getRbId() { + return rbId; + } + + public void setRbId(Integer rbId) { + this.rbId = rbId; + } + + public String getRankName() { + return rankName; + } + + public void setRankName(String rankName) { + this.rankName = rankName == null ? null : rankName.trim(); + } + + public Integer getRbBonus() { + return rbBonus; + } + + public void setRbBonus(Integer rbBonus) { + this.rbBonus = rbBonus; + } + + @Override + public String toString() { + return "RankBonus{" + + "rbId=" + rbId + + ", rankName='" + rankName + '\'' + + ", rbBonus=" + rbBonus + + '}'; + } +} \ No newline at end of file diff --git a/main/java/com/esms/po/Salary.java b/main/java/com/esms/po/Salary.java new file mode 100644 index 0000000..576cd9d --- /dev/null +++ b/main/java/com/esms/po/Salary.java @@ -0,0 +1,342 @@ +package com.esms.po; + +import com.fasterxml.jackson.annotation.JsonFormat; + +import java.util.Date; + +public class Salary { + private Integer sId; + + private Integer eId; + private Employee employee; + private Integer dId; + private Department department; + + private Date sTime; + + + private Integer sState; + + private Double basePay; + + private Double foodPay; + + private Double postPay; + + private Double workingYearPay; + + private Double rankPay; + + private Double trafficPay; + + private Double persionPay; + + private Double medicalPay; + + private Double unemploymentPay; + + private Double injuryPay; + + private Double birthPay; + + private Double housingPay; + + private Double latePay; + + private Double earlyPay; + + private Double overtimePay; + + private Double sickPay; + + private Double thingPay; + + private Double businessTravelPay; + + private Double fullAttendancePay; + + private Double rissuePay; + + private Double individualIncomeTax; + + private Double shouldPay; + + private Double actualPay; + + public Integer getsId() { + return sId; + } + + public void setsId(Integer sId) { + this.sId = sId; + } + + public Integer geteId() { + return eId; + } + + public void seteId(Integer eId) { + this.eId = eId; + } + + public Integer getdId() { + return dId; + } + + public void setdId(Integer dId) { + this.dId = dId; + } + + public Department getDepartment() { + return department; + } + + public void setDepartment(Department department) { + this.department = department; + } + + public Employee getEmployee() { + return employee; + } + + public void setEmployee(Employee employee) { + this.employee = employee; + } + + @JsonFormat(pattern="yyyy-MM",timezone = "GMT+8") + public Date getsTime() { + return sTime; + } + + public void setsTime(Date sTime) { + this.sTime = sTime; + } + + public Integer getsState() { + return sState; + } + + public void setsState(Integer sState) { + this.sState = sState; + } + + public Double getBasePay() { + return basePay; + } + + public void setBasePay(Double basePay) { + this.basePay = basePay; + } + + public Double getFoodPay() { + return foodPay; + } + + public void setFoodPay(Double foodPay) { + this.foodPay = foodPay; + } + + public Double getPostPay() { + return postPay; + } + + public void setPostPay(Double postPay) { + this.postPay = postPay; + } + + public Double getWorkingYearPay() { + return workingYearPay; + } + + public void setWorkingYearPay(Double workingYearPay) { + this.workingYearPay = workingYearPay; + } + + public Double getRankPay() { + return rankPay; + } + + public void setRankPay(Double rankPay) { + this.rankPay = rankPay; + } + + public Double getTrafficPay() { + return trafficPay; + } + + public void setTrafficPay(Double trafficPay) { + this.trafficPay = trafficPay; + } + + public Double getPersionPay() { + return persionPay; + } + + public void setPersionPay(Double persionPay) { + this.persionPay = persionPay; + } + + public Double getMedicalPay() { + return medicalPay; + } + + public void setMedicalPay(Double medicalPay) { + this.medicalPay = medicalPay; + } + + public Double getUnemploymentPay() { + return unemploymentPay; + } + + public void setUnemploymentPay(Double unemploymentPay) { + this.unemploymentPay = unemploymentPay; + } + + public Double getInjuryPay() { + return injuryPay; + } + + public void setInjuryPay(Double injuryPay) { + this.injuryPay = injuryPay; + } + + public Double getBirthPay() { + return birthPay; + } + + public void setBirthPay(Double birthPay) { + this.birthPay = birthPay; + } + + public Double getHousingPay() { + return housingPay; + } + + public void setHousingPay(Double housingPay) { + this.housingPay = housingPay; + } + + public Double getLatePay() { + return latePay; + } + + public void setLatePay(Double latePay) { + this.latePay = latePay; + } + + public Double getEarlyPay() { + return earlyPay; + } + + public void setEarlyPay(Double earlyPay) { + this.earlyPay = earlyPay; + } + + public Double getOvertimePay() { + return overtimePay; + } + + public void setOvertimePay(Double overtimePay) { + this.overtimePay = overtimePay; + } + + public Double getSickPay() { + return sickPay; + } + + public void setSickPay(Double sickPay) { + this.sickPay = sickPay; + } + + public Double getThingPay() { + return thingPay; + } + + public void setThingPay(Double thingPay) { + this.thingPay = thingPay; + } + + public Double getBusinessTravelPay() { + return businessTravelPay; + } + + public void setBusinessTravelPay(Double businessTravelPay) { + this.businessTravelPay = businessTravelPay; + } + + public Double getFullAttendancePay() { + return fullAttendancePay; + } + + public void setFullAttendancePay(Double fullAttendancePay) { + this.fullAttendancePay = fullAttendancePay; + } + + public Double getRissuePay() { + return rissuePay; + } + + public void setRissuePay(Double rissuePay) { + this.rissuePay = rissuePay; + } + + public Double getIndividualIncomeTax() { + return individualIncomeTax; + } + + public void setIndividualIncomeTax(Double individualIncomeTax) { + this.individualIncomeTax = individualIncomeTax; + } + + public Double getShouldPay() { + return shouldPay; + } + + public void setShouldPay(Double shouldPay) { + this.shouldPay = shouldPay; + } + + public Double getActualPay() { + return actualPay; + } + + public void setActualPay(Double actualPay) { + this.actualPay = actualPay; + } + + @Override + public String toString() { + return "Salary{" + + "sId=" + sId + + ", eId=" + eId + + ", employee=" + employee + + ", dId=" + dId + + ", department=" + department + + ", sTime=" + sTime + + ", sState=" + sState + + ", basePay=" + basePay + + ", foodPay=" + foodPay + + ", postPay=" + postPay + + ", workingYearPay=" + workingYearPay + + ", rankPay=" + rankPay + + ", trafficPay=" + trafficPay + + ", persionPay=" + persionPay + + ", medicalPay=" + medicalPay + + ", unemploymentPay=" + unemploymentPay + + ", injuryPay=" + injuryPay + + ", birthPay=" + birthPay + + ", housingPay=" + housingPay + + ", latePay=" + latePay + + ", earlyPay=" + earlyPay + + ", overtimePay=" + overtimePay + + ", sickPay=" + sickPay + + ", thingPay=" + thingPay + + ", businessTravelPay=" + businessTravelPay + + ", fullAttendancePay=" + fullAttendancePay + + ", rissuePay=" + rissuePay + + ", individualIncomeTax=" + individualIncomeTax + + ", shouldPay=" + shouldPay + + ", actualPay=" + actualPay + + '}'; + } +} \ No newline at end of file diff --git a/main/java/com/esms/po/SystemManager.java b/main/java/com/esms/po/SystemManager.java new file mode 100644 index 0000000..295890c --- /dev/null +++ b/main/java/com/esms/po/SystemManager.java @@ -0,0 +1,43 @@ +package com.esms.po; + +public class SystemManager { + private Integer smId; + + private String smAccount; + + + private String smPassword; + + public Integer getSmId() { + return smId; + } + + public void setSmId(Integer smId) { + this.smId = smId; + } + + public String getSmAccount() { + return smAccount; + } + + public void setSmAccount(String smAccount) { + this.smAccount = smAccount == null ? null : smAccount.trim(); + } + + public String getSmPassword() { + return smPassword; + } + + public void setSmPassword(String smPassword) { + this.smPassword = smPassword == null ? null : smPassword.trim(); + } + + @Override + public String toString() { + return "SystemManager{" + + "smId=" + smId + + ", smAccount='" + smAccount + '\'' + + ", smPassword='" + smPassword + '\'' + + '}'; + } +} \ No newline at end of file diff --git a/main/java/com/esms/po/WorkingYearsBonus.java b/main/java/com/esms/po/WorkingYearsBonus.java new file mode 100644 index 0000000..391e62c --- /dev/null +++ b/main/java/com/esms/po/WorkingYearsBonus.java @@ -0,0 +1,43 @@ +package com.esms.po; + +public class WorkingYearsBonus { + private Integer wybId; + + private Integer wybYear; + + private Double wybBonus; + + public Integer getWybId() { + return wybId; + } + + public void setWybId(Integer wybId) { + this.wybId = wybId; + } + + public Integer getWybYear() { + return wybYear; + } + + public void setWybYear(Integer wybYear) { + this.wybYear = wybYear; + } + + public Double getWybBonus() { + return wybBonus; + } + + public void setWybBonus(Double wybBonus) { + this.wybBonus = wybBonus; + } + + + @Override + public String toString() { + return "WorkingYearsBonus{" + + "wybId=" + wybId + + ", wybYear=" + wybYear + + ", wybBonus=" + wybBonus + + '}'; + } +} \ No newline at end of file diff --git a/main/java/com/esms/service/DepartmentService.java b/main/java/com/esms/service/DepartmentService.java new file mode 100644 index 0000000..308aab2 --- /dev/null +++ b/main/java/com/esms/service/DepartmentService.java @@ -0,0 +1,22 @@ +package com.esms.service; + +import com.esms.po.Department; + +import java.util.List; + +public interface DepartmentService { + public Department selectByPrimaryKey(int id) throws Exception; + + int insertSelective(Department department) throws Exception; + + void updateByPrimaryKeySelective(Department department) throws Exception; + + void deleteByPrimaryKey(int id) throws Exception; + + List findSelective(Department department) throws Exception; + + void deleteByQuery(int[] ids); + + Department findByDname(String d_name); + +} diff --git a/main/java/com/esms/service/EmployeeService.java b/main/java/com/esms/service/EmployeeService.java new file mode 100644 index 0000000..73065bb --- /dev/null +++ b/main/java/com/esms/service/EmployeeService.java @@ -0,0 +1,18 @@ +package com.esms.service; + +import com.esms.po.Employee; /** + * + * 2020/02/10 15:20 + */ +public interface EmployeeService { + void deleteByPrimaryKey(int id); + + void deleteByQuery(int[] ids); + + void insert(Employee employee); + + Employee findByeAccount(String eAccount); + + void updateByPrimaryKeySelective(Employee employee); + +} diff --git a/main/java/com/esms/service/IDownloadExcelService.java b/main/java/com/esms/service/IDownloadExcelService.java new file mode 100644 index 0000000..58c51b3 --- /dev/null +++ b/main/java/com/esms/service/IDownloadExcelService.java @@ -0,0 +1,12 @@ +package com.esms.service; + +import com.esms.exception.CustomException; +import jxl.write.WriteException; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +public interface IDownloadExcelService { + void getSalaryExcel(HttpServletRequest request, HttpServletResponse response,String eAccount,Integer dId,String sTime) throws Exception; +} diff --git a/main/java/com/esms/service/IEchartsService.java b/main/java/com/esms/service/IEchartsService.java new file mode 100644 index 0000000..7b4f271 --- /dev/null +++ b/main/java/com/esms/service/IEchartsService.java @@ -0,0 +1,17 @@ +package com.esms.service; + +import com.esms.po.Department; +import com.esms.vo.EchDepartmentSalary; +import com.esms.vo.EchEmployeeNums; +import com.esms.vo.EchMonthSalary; +import com.esms.vo.EchSalaryPercent; + +import java.util.List; +import java.util.Map; + +public interface IEchartsService { + Map> getEmployeeNums(); + Map> getDepartmentSalary(String date); + Map> getSalaryPercent(String date,int state); + Map> getMonthSalary(); +} diff --git a/main/java/com/esms/service/IImportDataService.java b/main/java/com/esms/service/IImportDataService.java new file mode 100644 index 0000000..9fca368 --- /dev/null +++ b/main/java/com/esms/service/IImportDataService.java @@ -0,0 +1,8 @@ +package com.esms.service; + +import org.springframework.web.multipart.MultipartFile; + +public interface IImportDataService { + String insertMATable(MultipartFile excel) throws Exception; + String insertReissueTable(MultipartFile excel) throws Exception; +} diff --git a/main/java/com/esms/service/IKeyValueService.java b/main/java/com/esms/service/IKeyValueService.java new file mode 100644 index 0000000..d34693a --- /dev/null +++ b/main/java/com/esms/service/IKeyValueService.java @@ -0,0 +1,9 @@ +package com.esms.service; + +import com.esms.po.KeyValue; + +public interface IKeyValueService { + public KeyValue getKeyValueById(int id) throws Exception; + public KeyValue selectBykvKey(String key)throws Exception; + public int updateByPrimaryKey(KeyValue kv)throws Exception; +} diff --git a/main/java/com/esms/service/ILoginService.java b/main/java/com/esms/service/ILoginService.java new file mode 100644 index 0000000..b25e568 --- /dev/null +++ b/main/java/com/esms/service/ILoginService.java @@ -0,0 +1,15 @@ +package com.esms.service; + +import com.esms.exception.CustomException; +import com.esms.po.Employee; +import com.esms.po.SystemManager; + +/** + * @Description: + */ +public interface ILoginService { + public Employee findEmployeeByIdAndPassword(String account, String password) throws CustomException; + public SystemManager findSystemManagerByIdAndPassword(String account, String password) throws CustomException; + public Employee findEmployeeById(int id); + public SystemManager findSystemManagerById(int id); +} diff --git a/main/java/com/esms/service/IMonthlyAttendanceService.java b/main/java/com/esms/service/IMonthlyAttendanceService.java new file mode 100644 index 0000000..9f4f82b --- /dev/null +++ b/main/java/com/esms/service/IMonthlyAttendanceService.java @@ -0,0 +1,17 @@ +package com.esms.service; + +import com.esms.po.MonthlyAttendance; +import org.springframework.stereotype.Service; + +/** + * @Description: + */ +public interface IMonthlyAttendanceService { + void deleteByPrimaryKey(int id); + + void deleteByQuery(int[] ids); + + MonthlyAttendance selectByPrimaryKey(int id); + + void updateByPrimaryKeySelective(MonthlyAttendance monthlyAttendance); +} diff --git a/main/java/com/esms/service/IRankBonusService.java b/main/java/com/esms/service/IRankBonusService.java new file mode 100644 index 0000000..8f5b88e --- /dev/null +++ b/main/java/com/esms/service/IRankBonusService.java @@ -0,0 +1,35 @@ +package com.esms.service; + +import com.esms.po.RankBonus; + +import java.util.List; + +/** + * @Description: + */ +public interface IRankBonusService { + //添加职称奖金记录 + boolean addRankBonus(RankBonus rb); + + //删除 + boolean moveRankBonus(int rbId); + + //修改 + boolean modifyRankName(RankBonus rb); + + //查询所有 + List findAll(); + + //根据id查询 + RankBonus findById(int rbId); + //根据名字查询 + RankBonus findByName(String rankName); + // + int CountByRbid(Integer rbid); + // + int CountByName(String name); + + List findSelective(RankBonus rb) throws Exception; + + +} diff --git a/main/java/com/esms/service/ISalaryService.java b/main/java/com/esms/service/ISalaryService.java new file mode 100644 index 0000000..0abcb61 --- /dev/null +++ b/main/java/com/esms/service/ISalaryService.java @@ -0,0 +1,19 @@ +package com.esms.service; + +import com.esms.exception.CustomException; +import com.esms.vo.SalaryPages; + +/** + * @Description: + */ +public interface ISalaryService { + public void insertSalaryByAcountAndDate(String eAccount, String date) throws CustomException; + public void insertSalaryAllByDate(String date) throws CustomException; + public SalaryPages selectSalaryByEaccountDIdDate(Integer pageNum,Integer limit,String eAccount,Integer dId,String sTime); + public void deleteSalaryByEid(int[] ids); + public void updateSalaryBySid(int[] ids); + + public SalaryPages selectSalaryByEaccountDIdDateState(int pageNum, int limit, String eAccount, Integer dId, String sTime); + + void updateSalaryByEidAndRissuePay(int sId, double rissuePay); +} diff --git a/main/java/com/esms/service/IUserInforService.java b/main/java/com/esms/service/IUserInforService.java new file mode 100644 index 0000000..3c53bce --- /dev/null +++ b/main/java/com/esms/service/IUserInforService.java @@ -0,0 +1,21 @@ +package com.esms.service; + +import com.esms.exception.CustomException; +import com.esms.po.Employee; +import com.esms.vo.EmployeeCustomVo; +import com.esms.po.Salary; +import com.esms.po.SystemManager; + +import java.util.List; + +/** + * @Description: + */ +public interface IUserInforService { + public void updateSystemManagePassword(int id, SystemManager systemManager); + public void updateEmployeePassword(int id, String oldPassword, String newPassword) throws CustomException; + public EmployeeCustomVo getInforEmployee(int id); + public int updateEmploueeById(int id, Employee employee); + // 查找员工个人的某年工资记录 + public List getEmployeeSalaryList(int eId, String year); +} diff --git a/main/java/com/esms/service/IWorkYearService.java b/main/java/com/esms/service/IWorkYearService.java new file mode 100644 index 0000000..8e822b1 --- /dev/null +++ b/main/java/com/esms/service/IWorkYearService.java @@ -0,0 +1,36 @@ +package com.esms.service; + +import com.esms.po.RankBonus; +import com.esms.po.WorkingYearsBonus; + +import java.util.List; + +/** + * @Description:工作年限奖金管理 + */ +public interface IWorkYearService { + //添加工龄奖金记录 + boolean addWorkYearBonus(WorkingYearsBonus wyb); + + //删除 + boolean moveWorkYearBonus(int wybId); + + //修改奖金 + boolean modifyWorkYearBonus(WorkingYearsBonus wyb); + + //查询所有 + List findAll(); + + //根据id查询 + WorkingYearsBonus findById(int wybId); + + + //统计是否存在该工龄 + int Count(int wybYear); + + List findSelective(WorkingYearsBonus record); + + WorkingYearsBonus findByYear(int year); + + +} diff --git a/main/java/com/esms/service/PositionService.java b/main/java/com/esms/service/PositionService.java new file mode 100644 index 0000000..8934a3f --- /dev/null +++ b/main/java/com/esms/service/PositionService.java @@ -0,0 +1,24 @@ +package com.esms.service; + +import com.esms.po.Position; + +import java.util.List; + +/** + * @Description: + */ +public interface PositionService { + List findSelective(Position position); + + int insertSelective(Position position); + + Position findByPrimaryKey(int id); + + void updateByPrimaryKeySelective(Position position); + + void deleteByPrimaryKey(int id); + + void deleteByQuery(int[] ids); + + Position findByDname(String p_name); +} diff --git a/main/java/com/esms/service/impl/DepartmentServiceImpl.java b/main/java/com/esms/service/impl/DepartmentServiceImpl.java new file mode 100644 index 0000000..9494a42 --- /dev/null +++ b/main/java/com/esms/service/impl/DepartmentServiceImpl.java @@ -0,0 +1,48 @@ +package com.esms.service.impl; + +import com.esms.dao.DepartmentMapper; +import com.esms.po.Department; +import com.esms.service.DepartmentService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class DepartmentServiceImpl implements DepartmentService { + @Autowired + public DepartmentMapper departmentMapper = null; + + public Department selectByPrimaryKey(int id) { + return departmentMapper.selectByPrimaryKey(id); + } + + public int insertSelective(Department department) { + return departmentMapper.insertSelective(department); + } + + @Override + public void updateByPrimaryKeySelective(Department department) { + departmentMapper.updateByPrimaryKeySelective(department); + } + + @Override + public void deleteByPrimaryKey(int id) { + departmentMapper.deleteByPrimaryKey(id); + } + + @Override + public List findSelective(Department department) { + return departmentMapper.findSelective(department); + } + + @Override + public void deleteByQuery(int[] ids) { + departmentMapper.deleteByQuery(ids); + } + + @Override + public Department findByDname(String d_name) { + return departmentMapper.findByDname(d_name); + } +} diff --git a/main/java/com/esms/service/impl/DownloadExcelServiceImpl.java b/main/java/com/esms/service/impl/DownloadExcelServiceImpl.java new file mode 100644 index 0000000..4106891 --- /dev/null +++ b/main/java/com/esms/service/impl/DownloadExcelServiceImpl.java @@ -0,0 +1,56 @@ +package com.esms.service.impl; + +import com.esms.dao.SalaryMapper; +import com.esms.exception.CustomException; +import com.esms.po.Salary; +import com.esms.service.IDownloadExcelService; +import com.esms.utils.JXLUtils; +import jxl.write.WritableWorkbook; +import jxl.write.WriteException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.OutputStream; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @Description: + */ +@Service +public class DownloadExcelServiceImpl implements IDownloadExcelService{ + @Autowired + private SalaryMapper salaryMapper; + @Override + public void getSalaryExcel(HttpServletRequest request, HttpServletResponse response,String eAccount,Integer dId,String sTime) throws Exception{ + // response.setCharacterEncoding("utf-8"); + //response.setHeader("Content-Disposition", "text/html; charset=utf-8;attachment; filename=salary.xls"); + String excelName = "工资表.xls"; + //WritableWorkbook writableWorkbook = ExcelUtils.createTemplate(response.getOutputStream()); + + WritableWorkbook writableWorkbook = null; + Map map = new HashMap(); + map.put("eAccount",eAccount); + map.put("dId",dId); + map.put("sTime",sTime); + List salaries = salaryMapper.selectByEaccountDIdDate(map); + try { + response.setHeader("Content-Disposition", "attachment;filename=" + new String(excelName.getBytes("gbk"),"ISO-8859-1")); + writableWorkbook = JXLUtils.createSalaryExcel(response.getOutputStream(),salaries); + writableWorkbook.write(); + writableWorkbook.close(); + } catch (IOException e) { + e.printStackTrace(); + throw new CustomException("下载失败"); + } catch (WriteException e) { + e.printStackTrace(); + throw new CustomException("下载失败"); + } + } + + +} diff --git a/main/java/com/esms/service/impl/EchartsServiceImpl.java b/main/java/com/esms/service/impl/EchartsServiceImpl.java new file mode 100644 index 0000000..26e15ed --- /dev/null +++ b/main/java/com/esms/service/impl/EchartsServiceImpl.java @@ -0,0 +1,156 @@ +package com.esms.service.impl; + +import com.esms.dao.DepartmentMapper; +import com.esms.dao.EmployeeMapper; +import com.esms.dao.SalaryMapper; +import com.esms.po.Department; +import com.esms.po.Employee; +import com.esms.service.IEchartsService; +import com.esms.vo.*; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.text.SimpleDateFormat; +import java.util.*; + +/** + * @Description: + **/ +@Service +public class EchartsServiceImpl implements IEchartsService { + @Autowired + private DepartmentMapper departmentMapper; + @Autowired + private EmployeeMapper employeeMapper; + @Autowired + private SalaryMapper salaryMapper; + + @Override + public Map> getEmployeeNums() { + /** + * @Author: 方宏泰 + * @Description: + * @Date: 8:11 2020/02/7 + * @Param: [] + * @Return: java.util.Map> + **/ + List echEmployeeNumsList = new ArrayList(); + Map> map = new HashMap>(); + List departmentList = (List) departmentMapper.selectAll(); + int nums = 0; + for (Department department:departmentList){ + //统计部门员工人数 + nums = employeeMapper.countByDid(department.getdId()); + EchEmployeeNums echEmployeeNums = new EchEmployeeNums(); + //填入部门名 + echEmployeeNums.setDepartment(department.getdName()); + //填入部门员工人数 + echEmployeeNums.setNum(nums); + echEmployeeNumsList.add(echEmployeeNums); + nums = 0; //清零 + } + map.put("list",echEmployeeNumsList); + return map; + } + + @Override + public Map> getDepartmentSalary(String date) { + /** + * @Author: 方宏泰 + * @Description:各部门工资折线图 + * @Date: 14:36 2020/02/7 + * @Param: [] + * @Return: java.util.Map> + **/ + List echDepartmentSalaryList = new ArrayList(); + + Map> stringListMap = new HashMap>(); + List departmentList = (List) departmentMapper.selectAll(); + //遍历所有部门 + for (Department department:departmentList){ + EchDepartmentSalary echDepartmentSalary = new EchDepartmentSalary(); + //填入部门名称 + echDepartmentSalary.setDepartment(department.getdName()); + //查找每个部门最高,平均,最低工资,并用vo类echSalary接收 + EchSalary echSalary = salaryMapper.selectSalaryByDepartment(department.getdId(),date); + //查找不到数据,则赋值为0 + if(echSalary==null){ + echSalary = new EchSalary(0.00,0.00,0.00); + } + //填入每个部门最高,平均,最低工资 + List salary = new ArrayList(); + salary.add(echSalary.getMinSalary()); + salary.add(echSalary.getAvgSalary()); + salary.add(echSalary.getMaxSalary()); + + echDepartmentSalary.setSalary(salary); + //填入一条数据(department,salary[10,20,30])到VO + echDepartmentSalaryList.add(echDepartmentSalary); + } + stringListMap.put("list",echDepartmentSalaryList); + return stringListMap; + } + + @Override + public Map> getSalaryPercent(String date,int state) { + List echSalaryPercentList = new ArrayList(); + Map> map = new HashMap>(); + List departmentList = (List) departmentMapper.selectAll(); + //遍历所有部门 + for (Department department:departmentList){ + EchSalaryPercent echSalaryPercent = new EchSalaryPercent(); + //填入部门名称 + echSalaryPercent.setDepartment(department.getdName()); + //查找每个部门工资总数,并用vo类echSalaryDepartment接收 + //时间为年月 + Double allSalary = 0.00; + if(state == 0){ + allSalary = salaryMapper.selectAllSalaryByDepartment(department.getdId(),date); + }else { + allSalary = salaryMapper.selectAllSalaryByDepAndYear(department.getdId(),date); + } + //查找不到数据,则赋值为0 + if(allSalary==null){ + allSalary = 0.00; + } + //填入每个部门工资总数 + echSalaryPercent.setSalary(allSalary); + //填入一条数据到VO + echSalaryPercentList.add(echSalaryPercent); + } + map.put("list",echSalaryPercentList); + return map; + } + + @Override + public Map> getMonthSalary() { + List echMonthSalaryList = new ArrayList(); + Map> stringListMap = new HashMap>(); + SimpleDateFormat sdf = new SimpleDateFormat("yyyy"); + Integer year = Integer.parseInt(sdf.format(new Date())); //当前年 + String date = null; + //近三年(不包括本年) + for(int i = year-2;i<=year;i++){ + EchMonthSalary echMonthSalary = new EchMonthSalary(); + echMonthSalary.setYear(i+"年"); + List salary = new ArrayList(); + for(int month = 1; month<=12;month++) { + if(month<10){ + date = i + "-0"+month; + } + else { + date = i + "-"+month; + } + Double ymSalary = salaryMapper.selectAllSalaryByDate(date); //查找某年某一月的总工资 + if (ymSalary == null) { + ymSalary = 0.00; + } + salary.add(ymSalary); + } + echMonthSalary.setSalary(salary); + echMonthSalaryList.add(echMonthSalary); + } + stringListMap.put("list",echMonthSalaryList); + return stringListMap; + } +} diff --git a/main/java/com/esms/service/impl/EmployeeServiceImpl.java b/main/java/com/esms/service/impl/EmployeeServiceImpl.java new file mode 100644 index 0000000..d52ba16 --- /dev/null +++ b/main/java/com/esms/service/impl/EmployeeServiceImpl.java @@ -0,0 +1,49 @@ +package com.esms.service.impl; + +import com.esms.dao.EmployeeMapper; +import com.esms.po.Employee; +import com.esms.service.EmployeeService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.Date; + +/** + * @Description: + */ +@Service +public class EmployeeServiceImpl implements EmployeeService{ + @Autowired + private EmployeeMapper employeeMapper; + + @Override + public void deleteByPrimaryKey(int id) { + employeeMapper.deleteByPrimaryKey(id); + } + + @Override + public void deleteByQuery(int[] ids) { + for (int i:ids){ + Employee employee = new Employee(); + employee.seteId(i); + employee.seteLeaveTime(new Date()); + employee.seteIsdel(0); + employeeMapper.updateByPrimaryKeySelective(employee); + } + } + + @Override + public void insert(Employee employee) { + employeeMapper.insert(employee); + } + + @Override + public Employee findByeAccount(String eAccount) { + return employeeMapper.findByeAccount(eAccount); + } + + @Override + public void updateByPrimaryKeySelective(Employee employee) { + employeeMapper.updateByPrimaryKeySelective(employee); + } +} diff --git a/main/java/com/esms/service/impl/ImportDataServiceImpl.java b/main/java/com/esms/service/impl/ImportDataServiceImpl.java new file mode 100644 index 0000000..c1db1cf --- /dev/null +++ b/main/java/com/esms/service/impl/ImportDataServiceImpl.java @@ -0,0 +1,194 @@ +package com.esms.service.impl; + +import com.esms.dao.EmployeeMapper; +import com.esms.dao.MonthlyAttendanceMapper; +import com.esms.dao.SalaryMapper; +import com.esms.po.MonthlyAttendance; +import com.esms.po.Salary; +import com.esms.service.IImportDataService; +import com.esms.utils.JXLUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import java.io.File; +import java.io.IOException; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.List; + +@Service +public class ImportDataServiceImpl implements IImportDataService { + @Autowired + private MonthlyAttendanceMapper monthlyAttendanceMapper; + @Autowired + private SalaryMapper salaryMapper; + @Autowired + private EmployeeMapper employeeMapper; + + public String insertMATable(MultipartFile excel) throws Exception { + String fileName = excel.getOriginalFilename(); + //限制上传文件的类型 + if (fileName.endsWith(".xls")) { + String path = "D://ESMSexcel/monthly_attendance_table/"; + File fileDirectory = new File(path); + if (!fileDirectory.exists()) + fileDirectory.mkdirs(); + File file = new File(path, fileName); + //完成上传 + try { + excel.transferTo(file); + } catch (IOException e) { + e.printStackTrace(); + } + List monthlyAttendanceList = JXLUtils.readMonthlyAttendanceTable(file); + + //校验是否存在员工工号 + for (MonthlyAttendance temp : monthlyAttendanceList) { + Integer count = employeeMapper.isExistEmployee(temp.getEmployee().geteAccount()); + //如果不存在此员工 + if (count == 0) + return "不存在员工工号为" + temp.getEmployee().geteAccount() + ",请检查导入的excel文件"; + } + DateFormat dateFormat = new SimpleDateFormat("yyyy-MM"); + //添加数据到数据库 + for (MonthlyAttendance temp : monthlyAttendanceList) { + int eId = employeeMapper.selectEidByEaccount(temp.getEmployee().geteAccount()); + temp.getEmployee().seteId(eId); + MonthlyAttendance mon = new MonthlyAttendance(); + mon = monthlyAttendanceMapper.selectByeIdAndDate(temp.getEmployee().geteId(),dateFormat.format(temp.getAttendanceTime())); + if(mon ==null) { + monthlyAttendanceMapper.insert(temp); + } + else{ + temp.setMaId(mon.getMaId()); + monthlyAttendanceMapper.updateByPrimaryKeySelective(temp); + } + } + return "导入成功"; + } else { + return "只能导入Microsoft Excel 97-2003 工作表 (.xls),请检查文件是否正确"; + } + } + + public String insertReissueTable(MultipartFile excel) throws Exception { + String fileName = excel.getOriginalFilename(); + //限制上传文件的类型 + if (fileName.endsWith(".xls")) { + String path = "D://ESMSexcel/monthly_reissue_table/"; + File fileDirectory = new File(path); + if (!fileDirectory.exists()) + fileDirectory.mkdirs(); + File file = new File(path, fileName); + //完成上传 + try { + excel.transferTo(file); + } catch (IOException e) { + e.printStackTrace(); + } + + DateFormat dateFormat = new SimpleDateFormat("yyyy-MM"); + List salaryList = JXLUtils.readReissueTable(file); + //校验是否存在此员工工号 + for (Salary salary : salaryList) { + Integer count = employeeMapper.isExistEmployee(salary.getEmployee().geteAccount()); + //如果不存在此员工 + if (count == 0) { + return "不存在员工工号为" + salary.getEmployee().geteAccount() + ",请检查导入的excel文件"; + } + } + for (Salary salary : salaryList) { + int eId = employeeMapper.selectEidByEaccount(salary.getEmployee().geteAccount()); + salary.getEmployee().seteId(eId); + Salary temp = salaryMapper.selectByEidAndTimeAndStatus(salary.getEmployee().geteId(), dateFormat.format(salary.getsTime()),0); + //如果数据库表salay不存在此记录,直接插入 + if (temp == null) { + salary.setBasePay(0.00); + salary.setFoodPay(0.00); + salary.setPostPay(0.00); + salary.setWorkingYearPay(0.00); + salary.setRankPay(0.00); + salary.setTrafficPay(0.00); + salary.setOvertimePay(0.00); + salary.setBusinessTravelPay(0.00); + salary.setFullAttendancePay(0.00); +// salary.setRissuePay(0.00); 表格有 + + salary.setPersionPay(0.00); + salary.setMedicalPay(0.00); + salary.setUnemploymentPay(0.00); + salary.setInjuryPay(0.00); + salary.setBirthPay(0.00); + salary.setHousingPay(0.00); + + salary.setLatePay(0.00) ; + salary.setEarlyPay(0.00); + salary.setSickPay(0.00); + salary.setThingPay(0.00); + + salary.setIndividualIncomeTax(0.00); // 修改个人所得税 + salary.setShouldPay(0.00); // 修改应发工资 + salary.setActualPay(0.00); // 修改实发工资 + + salary.setsState(0); + salaryMapper.insertSelective(salary); + } else { + //如果数据库表salay已存在此记录,修改补发金额数据 + salary.setsId(temp.getsId()); + // shouldPay 应发金额 10项 + double shouldPay = temp.getBasePay() + + temp.getFoodPay() + + temp.getPostPay() + + temp.getWorkingYearPay() + + temp.getRankPay() + + temp.getTrafficPay() + + temp.getOvertimePay() + + temp.getBusinessTravelPay() + + temp.getFullAttendancePay() + + salary.getRissuePay(); + + // 五险一金扣费 6项 + double insurances = temp.getPersionPay() + + temp.getMedicalPay() + + temp.getUnemploymentPay() + + temp.getInjuryPay() + + temp.getBirthPay() + + temp.getHousingPay(); + + // 考勤扣费 4项 + double attendance = temp.getLatePay() + + temp.getEarlyPay() + + temp.getSickPay() + + temp.getThingPay(); + + double incomeTax = 0; + double taxable = shouldPay + insurances + attendance - temp.getBusinessTravelPay() - 3500; // 纳税超额部分 + if (0 findSelective(Position position) { + return positionMapper.findSelective(position); + } + + @Override + public int insertSelective(Position position) { + return positionMapper.insertSelective(position); + } + + @Override + public Position findByPrimaryKey(int id) { + return positionMapper.selectByPrimaryKey(id); + } + + @Override + public void updateByPrimaryKeySelective(Position position) { + positionMapper.updateByPrimaryKeySelective(position); + } + + @Override + public void deleteByPrimaryKey(int id) { + positionMapper.deleteByPrimaryKey(id); + } + + @Override + public void deleteByQuery(int[] ids) { + positionMapper.deleteByQuery(ids); + } + + @Override + public Position findByDname(String p_name) { + return positionMapper.findByDname(p_name); + } + + +} diff --git a/main/java/com/esms/service/impl/RankBonusServiceImpl.java b/main/java/com/esms/service/impl/RankBonusServiceImpl.java new file mode 100644 index 0000000..3cfa507 --- /dev/null +++ b/main/java/com/esms/service/impl/RankBonusServiceImpl.java @@ -0,0 +1,109 @@ +package com.esms.service.impl; + +import com.esms.dao.EmployeeMapper; +import com.esms.dao.RankBonusMapper; +import com.esms.dao.WorkingYearsBonusMapper; +import com.esms.po.RankBonus; +import com.esms.service.IRankBonusService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Service; +import java.util.List; + +/** + * @Description: + */ +@Service("rankBonusService") +public class RankBonusServiceImpl implements IRankBonusService { + @Autowired + private RankBonusMapper rankBonusMapper; + @Autowired + private EmployeeMapper employeeMapper; + + + + + + + public void setRankBonusMapper(RankBonusMapper rankBonusMapper) { + this.rankBonusMapper = rankBonusMapper; + } + @Override + public boolean moveRankBonus(int rbId) { + int n=rankBonusMapper.deleteByPrimaryKey(rbId); + if(n>0){ + return true; + } + else{ + return false; + } + + } + + //修改职称名 + @Override + public boolean modifyRankName(RankBonus rb) { + int n=rankBonusMapper.updateByPrimaryKey(rb); + if(n>0) + return true; + else + return false; + } + + @Override + public List findAll() { + + List rb = rankBonusMapper.selectAll(); + if(rb.size()>0){ + return rb; + } + else{ + return null; + } + } + + @Override + public RankBonus findById(int rbId) { + return null; + } + + @Override + public RankBonus findByName(String rankName) { + return rankBonusMapper.findByname(rankName); + } + + //插入新的职称 + @Override + public boolean addRankBonus(RankBonus rb) { + int count = rankBonusMapper.insert(rb); + if (count > 0) { + return true; + } + return false; + } + + @Override + public int CountByRbid(Integer rbid) { + int n=employeeMapper.countByRbid(rbid); + if(n>0) return n; + else return 0; + } + +// public int CountByRbid(int rbId){ +// int count =employeeMapper.countByRbid(rbId); +// if(count>0){ return count; } +// else{ return 0; } +// } + + @Override + public int CountByName(String name) { + int count=rankBonusMapper.CountByRankName(name); + if(count>0) return count; + else return 0; + } + + @Override + public List findSelective(RankBonus rb) { + return rankBonusMapper.findSelective(rb); + } +} diff --git a/main/java/com/esms/service/impl/SalaryServiceImpl.java b/main/java/com/esms/service/impl/SalaryServiceImpl.java new file mode 100644 index 0000000..15b5ec7 --- /dev/null +++ b/main/java/com/esms/service/impl/SalaryServiceImpl.java @@ -0,0 +1,344 @@ +package com.esms.service.impl; + +import com.esms.dao.*; +import com.esms.exception.CustomException; +import com.esms.po.Employee; +import com.esms.po.MonthlyAttendance; +import com.esms.po.Salary; +import com.esms.service.ISalaryService; +import com.esms.vo.SalaryPages; +import com.github.pagehelper.PageHelper; +import com.github.pagehelper.PageInfo; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @Description: + */ +@Service +public class SalaryServiceImpl implements ISalaryService { + @Autowired + private SalaryMapper salaryMapper = null; + @Autowired + private MonthlyAttendanceMapper monthlyAttendanceMapper = null; + @Autowired + private EmployeeMapper employeeMapper = null; + @Autowired + private RankBonusMapper rankBonusMapper = null; + @Autowired + private PositionMapper positionMapper = null; + @Autowired + private WorkingYearsBonusMapper workingYearsBonusMapper = null; + @Autowired + private KeyValueMapper keyValueMapper = null; + @Autowired + private DepartmentMapper departmentMapper = null; + + public void insertSalaryByAcountAndDate(String eAccount, String date) throws CustomException { + + Employee employee = null; + employee = employeeMapper.selectByAccount(eAccount); + if (employee == null) { + throw new CustomException("此工号不存在"); + } else { + if (salaryMapper.selectByEidAndTimeAndStatus(employee.geteId(), date, 1) != null) { + throw new CustomException("本工号的此月工资已发放,不能在结算"); + } else { + insertSalay(employee, date); + } + } + } + + @Override + public void insertSalaryAllByDate(String date) throws CustomException { + List salaryList = salaryMapper.selectByeTimeAndStatus(date, 1); + if (salaryList.size() >= 1) { + throw new CustomException("此月工资已发放,不能在进行工资结算"); + } else { + List employeeList = null; + employeeList = employeeMapper.selectAll(); + for (Employee employee : employeeList) { + insertSalay(employee, date); + } + } + } + + /** + * @Author: admin + * @Description: 通过提供员工和计算某年某月的工资时间,计算员工工资 + * @Date: 1:00 2020/02/12 + * @Param: [employee, date] + * @Return: int + **/ + private void insertSalay(Employee employee, String date) { + // 按照年月和工号查出这个员工的考勤记录, + // 1.考勤存在就计算考勤 + // 2.考勤不存在即为0 + + MonthlyAttendance monthlyAttendance = monthlyAttendanceMapper.selectByeIdAndDate(employee.geteId(), date); + // 判断数据库是否存在 + Salary salarySelect = salaryMapper.selectByEidAndTimeAndStatus(employee.geteId(), date, 0); + + Salary salary = new Salary(); + DateFormat format1 = new SimpleDateFormat("yyyy-MM"); + Date dateNew = null; +// 4 +// salary.seteId(employee.geteId()); // 员工号 + salary.setEmployee(employee); +// salary.setdId(employee.getdId()); // 部门号 + salary.setDepartment(departmentMapper.selectByPrimaryKey(employee.getdId())); + try { + dateNew = format1.parse(date); + } catch (ParseException e) { + e.printStackTrace(); + } + salary.setsTime(dateNew); // 工资日期 + Double basePay = employee.geteBasePay(); + salary.setBasePay(basePay); // 基本工资 +// 7 + if (monthlyAttendance != null) { + // 计算考勤 + double late_buckle_pay = keyValueMapper.selectBykvKey("late_buckle_pay").getKvValue(); // 迟到 + double early_buckle_pay = keyValueMapper.selectBykvKey("early_buckle_pay").getKvValue(); // 早退 + double missionallowance = keyValueMapper.selectBykvKey("missionallowance").getKvValue(); //出差不补贴 + double full_attendance_pay = keyValueMapper.selectBykvKey("full_attendance_pay").getKvValue(); // 全勤奖 + + salary.setLatePay(monthlyAttendance.getLateNum() * late_buckle_pay); // 迟到罚金 + salary.setEarlyPay(monthlyAttendance.getEarlyNum() * early_buckle_pay); // 早退罚金 + + double x = basePay / 21.75 / 8; + double overTimePay = x * monthlyAttendance.getOvertimeHour() * 1.5 + + x * monthlyAttendance.getWeekendHour() * 2 + + x * monthlyAttendance.getHolidayHour() * 3; + salary.setOvertimePay(overTimePay); // 加班补贴 + + if (monthlyAttendance.getSickLeaveNum() > 3) { // 大于3天扣钱 + salary.setSickPay((monthlyAttendance.getSickLeaveNum() - 3) * (-30.00)); // 病假扣钱 + } else { + salary.setSickPay(0.00); + } + if (monthlyAttendance.getSickLeaveNum() > 2) { // 大于3天扣钱 + salary.setThingPay((monthlyAttendance.getCompassionateLeaveNum() - 2) * (-50.00)); // 事假扣钱 + } else { + salary.setThingPay(0.00); + } + salary.setBusinessTravelPay(monthlyAttendance.getCompassionateLeaveNum() * missionallowance); // 出差补贴 + if (monthlyAttendance.getAbsenceNum() < 1){ + salary.setFullAttendancePay(full_attendance_pay); // 全勤奖励 + }else { + salary.setFullAttendancePay(0.00); // 全勤奖励 + } + } else { + // 不用计算考勤 + salary.setLatePay(0.00); // 迟到罚金 + salary.setEarlyPay(0.00); // 早退罚金 + salary.setOvertimePay(0.00); // 加班补贴 + salary.setSickPay(0.00); // 病假扣钱 + salary.setThingPay(0.00); // 事假扣钱 + salary.setBusinessTravelPay(0.00); // 出差补贴 + salary.setFullAttendancePay(0.00); // 全勤奖励 + } +// 16 + double food_pay = keyValueMapper.selectBykvKey("food_pay").getKvValue(); // 餐饮补贴 + double traffic_pay = keyValueMapper.selectBykvKey("traffic_pay").getKvValue(); // 交通补贴 + + salary.setFoodPay(food_pay); // 餐饮补贴 + salary.setTrafficPay(traffic_pay); // 交通补贴 + + salary.setPostPay(positionMapper.selectByPrimaryKey(employee.getpId()).getpPostPay()); // 岗位补贴 + salary.setRankPay((double) (rankBonusMapper.selectByPrimaryKey(employee.geteRank()).getRbBonus())); // 职称补贴 + + long datesum = (dateNew.getTime() - employee.geteEntryTime().getTime()); + int years = (int) (datesum / 31536000 / 1000); + try { + salary.setWorkingYearPay(workingYearsBonusMapper.findByYear(years).getWybBonus()); // 工龄补贴 + }catch (Exception e){ + salary.setWorkingYearPay(0.00); + } + + salary.setPersionPay(-(basePay * 0.08)); // 养老保险 + salary.setMedicalPay(-(basePay * 0.02 + 10)); // 医疗保险 + salary.setUnemploymentPay(-(basePay * 0.01)); // 失业保险 + salary.setInjuryPay(0.00); // 工伤保险 + salary.setBirthPay(0.00); // 生育保险 + salary.setHousingPay(-(basePay * 0.08)); // 住房公积金 + + if (salarySelect != null){ + salary.setRissuePay(salarySelect.getRissuePay()); //补发金额 + }else { + salary.setRissuePay(0.00); //补发金额 + } + + // shouldPay 应发金额 10项 + double shouldPay = salary.getBasePay() + + salary.getFoodPay() + + salary.getPostPay() + + salary.getWorkingYearPay() + + salary.getRankPay() + + salary.getTrafficPay() + + salary.getOvertimePay() + + salary.getBusinessTravelPay() + + salary.getFullAttendancePay() + + salary.getRissuePay(); + + // 五险一金扣费 6项 + double insurances = salary.getPersionPay() + + salary.getMedicalPay() + + salary.getUnemploymentPay() + + salary.getInjuryPay() + + salary.getBirthPay() + + salary.getHousingPay(); + + // 考勤扣费 4项 + double attendance = salary.getLatePay() + + salary.getEarlyPay() + + salary.getSickPay() + + salary.getThingPay(); + + double incomeTax = 0; + double taxable = shouldPay + insurances + attendance - salary.getBusinessTravelPay() - 3500; // 纳税超额部分 + if (0 map = new HashMap(); + map.put("eAccount", eAccount); + map.put("dId", dId); + map.put("sTime", sTime); + //pageNum:起始页面 pageSize:每页的大小 + PageHelper.startPage(pageNum, limit); + //查找条件,一定要紧跟在startPage后 + List salaries = salaryMapper.selectByEaccountDIdDate(map); //获取满足条件的数据 + PageInfo pageResult = new PageInfo(salaries); + //返回layui表格要求的格式数据 + return new SalaryPages(0, "", (int) pageResult.getTotal(), pageResult.getList()); + } + + @Override + public void deleteSalaryByEid(int[] ids) { + for (int i : ids) { + salaryMapper.deleteByPrimaryKey(i); + } + } + + @Override + public void updateSalaryBySid(int[] ids) { + Salary salary = new Salary(); + for (int i : ids) { + salary.setsId(i); + salary.setsState(1); + salaryMapper.updateByPrimaryKeySelective(salary); + } + } + + @Override + public SalaryPages selectSalaryByEaccountDIdDateState(int pageNum, int limit, String eAccount, Integer dId, String sTime) { + Map map = new HashMap(); + map.put("eAccount", eAccount); + map.put("dId", dId); + map.put("sTime", sTime); + //pageNum:起始页面 pageSize:每页的大小 + PageHelper.startPage(pageNum, limit); + //查找条件,一定要紧跟在startPage后 + List salaries = salaryMapper.selectByEaccountDIdDateState(map); //获取满足条件的数据 + PageInfo pageResult = new PageInfo(salaries); + //返回layui表格要求的格式数据 + return new SalaryPages(0, "", (int) pageResult.getTotal(), pageResult.getList()); + } + + @Override + public void updateSalaryByEidAndRissuePay(int sId, double rissuePay) { + Salary salary = salaryMapper.selectByPrimaryKey(sId); + salary.setRissuePay(rissuePay); + // shouldPay 应发金额 10项 + double shouldPay = salary.getBasePay() + + salary.getFoodPay() + + salary.getPostPay() + + salary.getWorkingYearPay() + + salary.getRankPay() + + salary.getTrafficPay() + + salary.getOvertimePay() + + salary.getBusinessTravelPay() + + salary.getFullAttendancePay() + + salary.getRissuePay(); + + // 五险一金扣费 6项 + double insurances = salary.getPersionPay() + + salary.getMedicalPay() + + salary.getUnemploymentPay() + + salary.getInjuryPay() + + salary.getBirthPay() + + salary.getHousingPay(); + + // 考勤扣费 4项 + double attendance = salary.getLatePay() + + salary.getEarlyPay() + + salary.getSickPay() + + salary.getThingPay(); + + double incomeTax = 0; + double taxable = shouldPay + insurances + attendance - salary.getBusinessTravelPay() - 3500; // 纳税超额部分 + if (0 getEmployeeSalaryList(int eId, String year) { + if ("1".equals(year)){ + return salaryMapper.selectByeId(eId); + }else { + return salaryMapper.selectEmployeeSalaryList(eId,year); + } + } +} diff --git a/main/java/com/esms/service/impl/WorkYearServiceImpl.java b/main/java/com/esms/service/impl/WorkYearServiceImpl.java new file mode 100644 index 0000000..0f83855 --- /dev/null +++ b/main/java/com/esms/service/impl/WorkYearServiceImpl.java @@ -0,0 +1,78 @@ +package com.esms.service.impl; +import com.esms.dao.WorkingYearsBonusMapper; +import com.esms.po.WorkingYearsBonus; +import com.esms.service.IWorkYearService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import java.util.List; + +/** + * @Description: + */ +@Service +public class WorkYearServiceImpl implements IWorkYearService { + @Autowired + private WorkingYearsBonusMapper workingYearsBonusMapper; + + @Override + public boolean addWorkYearBonus(WorkingYearsBonus wyb) { + int count = workingYearsBonusMapper.insert(wyb); + if (count > 0) { + return true; + } + return false; + } + + @Override + public boolean moveWorkYearBonus(int wybId) { + int n=workingYearsBonusMapper.deleteByPrimaryKey(wybId); + if(n>0){ + return true; + } + else{ + return false; + } + } + + @Override + public boolean modifyWorkYearBonus(WorkingYearsBonus wyb) { + int n=workingYearsBonusMapper.updateByPrimaryKey(wyb); //n代表影响的行数 + if(n>0) + return true; + else + return false; + } + + @Override + public List findAll() { + List wyb = workingYearsBonusMapper.selectAll(); + if(wyb.size()>0){ + return wyb; + } + else{ + return null; + } + } + + @Override + public WorkingYearsBonus findById(int wybId) { + return null; + } + + @Override + public int Count(int wybYear) { + int n=workingYearsBonusMapper.countByYear(wybYear); + if(n>0) return n; + else return 0; + } + + @Override + public List findSelective(WorkingYearsBonus record) { + return workingYearsBonusMapper.findSelective(record); + } + + @Override + public WorkingYearsBonus findByYear(int year) { + return workingYearsBonusMapper.findByYear(year); + } +} diff --git a/main/java/com/esms/utils/CaptchaUtil.java b/main/java/com/esms/utils/CaptchaUtil.java new file mode 100644 index 0000000..c14bc66 --- /dev/null +++ b/main/java/com/esms/utils/CaptchaUtil.java @@ -0,0 +1,92 @@ +package com.esms.utils; + +import javax.imageio.ImageIO; +import javax.servlet.ServletException; +import javax.servlet.ServletOutputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.util.Random; + +public final class CaptchaUtil +{ + private CaptchaUtil(){} + + /* + * 随机字符字典 + */ + private static final char[] CHARS = { '2', '3', '4', '5', '6', '7', '8', + '9' }; + + /* + * 随机数 + */ + private static Random random = new Random(); + + /* + * 获取4位随机数 + */ + private static String getRandomString() + { + StringBuffer buffer = new StringBuffer(); + for(int i = 0; i < 4; i++) + { + buffer.append(CHARS[random.nextInt(CHARS.length)]); + } + return buffer.toString(); + } + + /* + * 获取随机数颜色 + */ + private static Color getRandomColor() + { + return new Color(random.nextInt(255),random.nextInt(255), + random.nextInt(255)); + } + + /* + * 返回某颜色的反色 + */ + private static Color getReverseColor(Color c) + { + return new Color(255 - c.getRed(), 255 - c.getGreen(),255 - c.getBlue()); + } + + public static void outputCaptcha(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException + { + response.setContentType("image/jpeg"); + // 获取验证码字符串 + String identifyingCode = getRandomString(); + request.getSession(true).setAttribute("identifyingCode", identifyingCode); + + int width = 100; + int height = 30; + + Color color = getRandomColor(); + Color reverse = getReverseColor(color); + + BufferedImage bi = new BufferedImage(width, height, + BufferedImage.TYPE_INT_RGB); + Graphics2D g = bi.createGraphics(); + g.setFont(new Font(Font.SANS_SERIF, Font.BOLD, 16)); + g.setColor(color); + g.fillRect(0, 0, width, height); + g.setColor(reverse); + g.drawString(identifyingCode, 18, 20); + for (int i = 0, n = random.nextInt(100); i < n; i++) + { + g.drawRect(random.nextInt(width), random.nextInt(height), 1, 1); + } + + // 转成JPEG格式 + ServletOutputStream out = response.getOutputStream(); +// JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out); +// encoder.encode(bi); + ImageIO.write(bi, "jpeg", out); + out.flush(); + } +} diff --git a/main/java/com/esms/utils/JXLUtils.java b/main/java/com/esms/utils/JXLUtils.java new file mode 100644 index 0000000..0645667 --- /dev/null +++ b/main/java/com/esms/utils/JXLUtils.java @@ -0,0 +1,319 @@ +package com.esms.utils; + +import com.esms.po.Employee; +import com.esms.po.MonthlyAttendance; +import com.esms.po.Salary; +import jxl.*; +import jxl.format.CellFormat; +import jxl.read.biff.BiffException; +import jxl.write.*; +import jxl.write.Boolean; +import jxl.write.Number; + +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.List; + +public class JXLUtils { + + public static List readMonthlyAttendanceTable(File file) throws Exception{ + /* 1:创建workbook */ + Workbook workbook = null; + try { + workbook = Workbook.getWorkbook(file); + } catch (IOException e) { + e.printStackTrace(); + } catch (BiffException e) { + e.printStackTrace(); + } + //2:获取第一个工作表sheet + Sheet sheet = workbook.getSheet(0); + //3:获取数据 + /*System.out.println("行:" + sheet.getRows()); + System.out.println("列:" + sheet.getColumns());*/ + List monthlyAttendanceList = new ArrayList(); + for (int i = 1; i < sheet.getRows(); i++) { + MonthlyAttendance monthlyAttendance = new MonthlyAttendance(); + for (int j = 1; j < sheet.getColumns(); j++) { + Cell cell = sheet.getCell(j, i); + //System.out.print(cell.getContents() + " "); + switch (j) { + case 1: + Employee employee = new Employee(); + employee.seteAccount(cell.getContents()); + monthlyAttendance.setEmployee(employee); + break; + case 2: + monthlyAttendance.setSickLeaveNum(Integer.parseInt(cell.getContents())); + break; + case 3: + monthlyAttendance.setOvertimeHour(Double.parseDouble(cell.getContents())); + break; + case 4: + monthlyAttendance.setWeekendHour(Double.parseDouble(cell.getContents())); + break; + case 5: + monthlyAttendance.setHolidayHour(Double.parseDouble(cell.getContents())); + break; + case 6: + try { + monthlyAttendance.setLateNum(Integer.parseInt(cell.getContents().replace(" ", ""))); + } catch (Exception e) { + e.printStackTrace(); + monthlyAttendance.setLateNum(0); + } + break; + case 7: + try { + monthlyAttendance.setEarlyNum(Integer.parseInt(cell.getContents().replace(" ", ""))); + } catch (Exception e) { + e.printStackTrace(); + monthlyAttendance.setEarlyNum(0); + } + break; + case 8: + try { + monthlyAttendance.setAbsenceNum(Integer.parseInt(cell.getContents().replace(" ", ""))); + } catch (Exception e) { + e.printStackTrace(); + monthlyAttendance.setAbsenceNum(0); + } + break; + case 9: + try { + monthlyAttendance.setCompassionateLeaveNum(Integer.parseInt(cell.getContents().replace(" ", ""))); + } catch (Exception e) { + e.printStackTrace(); + monthlyAttendance.setCompassionateLeaveNum(0); + } + break; + case 10: + try { + monthlyAttendance.setBusinessTravelNum(Integer.parseInt(cell.getContents().replace(" ", ""))); + } catch (Exception e) { + e.printStackTrace(); + monthlyAttendance.setBusinessTravelNum(0); + } + break; + case 11: + DateCell dc = (DateCell) cell; + Date date = dc.getDate(); //获取单元格的date类型 + monthlyAttendance.setAttendanceTime(date); + break; + } + + } + //System.out.println(monthlyAttendance); + monthlyAttendanceList.add(monthlyAttendance); + } + //最后一步:关闭资源 + workbook.close(); + return monthlyAttendanceList; + } + + public static List readReissueTable(File file) { + /* 1:创建workbook */ + Workbook workbook = null; + try { + workbook = Workbook.getWorkbook(file); + } catch (IOException e) { + e.printStackTrace(); + } catch (BiffException e) { + e.printStackTrace(); + } + //2:获取第一个工作表sheet + Sheet sheet = workbook.getSheet(0); + //3:获取数据 + System.out.println("行:" + sheet.getRows()); + System.out.println("列:" + sheet.getColumns()); + List salaryList = new ArrayList(); + for (int i = 1; i < sheet.getRows(); i++) { + Salary salary = new Salary(); + for (int j = 1; j < sheet.getColumns(); j++) { + Cell cell = sheet.getCell(j, i); + System.out.print(cell.getContents() + " "); + switch (j) { + case 1: + Employee employee = new Employee(); + employee.seteAccount(cell.getContents()); + salary.setEmployee(employee); + break; + case 2: + salary.setRissuePay(Double.parseDouble(cell.getContents())); + break; + case 3: + DateCell dc = (DateCell) cell; + Date date = dc.getDate(); //获取单元格的date类型 + salary.setsTime(date); + break; + } + + } + System.out.println(salary); + salaryList.add(salary); + } + //最后一步:关闭资源 + workbook.close(); + return salaryList; + } + + //创建excel工资表格 + public static WritableWorkbook createSalaryExcel(OutputStream output,List salaries) throws IOException, WriteException { + WritableWorkbook writableWorkbook= Workbook.createWorkbook(output); + WritableSheet sheet = writableWorkbook.createSheet("工资表", 0); + + + CellFormat cf = writableWorkbook.getSheet(0).getCell(1, 0).getCellFormat(); + WritableCellFormat wc = new WritableCellFormat(); + // 设置水平居中 + wc.setAlignment(Alignment.CENTRE); + //设置垂直居中 + wc.setVerticalAlignment(VerticalAlignment.CENTRE); + // 设置边框线 + wc.setBorder(Border.ALL, BorderLineStyle.THIN); + wc.setBackground(jxl.format.Colour.WHITE); + //合并单元格,第一个参数:要合并的单元格最左上角的列号,第二个参数:要合并的单元格最左上角的行号, + //第三个参数:要合并的单元格最右角的列号,第四个参数:要合并的单元格最右下角的行号, + //前4个 + for(int i = 0;i< 5;i++){ + sheet.mergeCells(i, 0, i, 1); + } + sheet.mergeCells(5, 0, 8, 0); //合并补贴单元格 + sheet.mergeCells(9, 0, 12, 0); //合并奖金单元格 + sheet.mergeCells(13, 0, 18, 0); //合并五险一金单元格 + sheet.mergeCells(19, 0, 23, 0); //合并工资扣额单元格 + /*// 后4个 + for(int i = 23,j = 0;i< 25;i++,j++ ){ + sheet.mergeCells(i, 0, j, 1); + }*/ + //sheet.mergeCells(23, 0, 23, 1); //合并工资扣额单元格 + //sheet.mergeCells(1, 0, 1, 1); + /*Label nc0 = new Label(0, 0, "标题1",wc);//Label(x,y,z)其中x代表单元格的第x+1列,第y+1行, 单元格的内容是z + Label nc1 = new Label(1, 0, "标题2",wc); + Label nc2 = new Label(2, 0, "标题3",wc); + Label nc3 = new Label(0, 1, "dddd"); + Label nc4 = new Label(1, 1, "ffff"); + + + wsheet.addCell(nc0); + wsheet.addCell(nc1); + wsheet.addCell(nc2); + wsheet.addCell(nc3); + wsheet.addCell(nc4);*/ + + //创建要显示的具体内容 + Label eAccount = new Label(0,0,"员工工号",wc); + sheet.addCell(eAccount); + Label eName = new Label(1,0,"员工姓名",wc); + sheet.addCell(eName); + Label department = new Label(2,0,"所属部门",wc); + sheet.addCell(department); + Label date = new Label(3,0,"日期",wc); + sheet.addCell(date); + Label basePay = new Label(4,0,"基本工资",wc); + sheet.addCell(basePay); + Label subsidy = new Label(5,0,"补贴",wc); + sheet.addCell(subsidy); + Label foodPay = new Label(5,1,"餐饮补贴",wc); + sheet.addCell(foodPay); + Label postPay = new Label(6,1,"岗位补贴",wc); + sheet.addCell(postPay); + Label trafficPay = new Label(7,1,"交通补贴",wc); + sheet.addCell(trafficPay); + Label businessTravePay = new Label(8,1,"出差补贴",wc); + sheet.addCell(businessTravePay); + Label bonus = new Label(9,0,"奖金",wc); + sheet.addCell(bonus); + Label wokingYearsPay = new Label(9,1,"工龄奖金",wc); + sheet.addCell(wokingYearsPay); + Label rankPay = new Label(10,1,"职称奖金",wc); + sheet.addCell(rankPay); + Label overtimePay = new Label(11,1,"加班奖金",wc); + sheet.addCell(overtimePay); + Label fullAttendancePay = new Label(12,1,"全勤奖金",wc); + sheet.addCell(fullAttendancePay); + Label risks = new Label(13,0,"五险一金",wc); + sheet.addCell( risks); + Label persionPay = new Label(13,1,"养老保险",wc); + sheet.addCell(persionPay); + Label medicalPay = new Label(14,1,"医疗保险",wc); + sheet.addCell(medicalPay); + Label unemploymentPay = new Label(15,1,"失业保险",wc); + sheet.addCell(unemploymentPay); + Label injuryPay = new Label(16,1,"工伤保险",wc); + sheet.addCell(injuryPay); + Label birthPay = new Label(17,1,"生育保险",wc); + sheet.addCell(birthPay); + Label housingPay = new Label(18,1,"住房公积金",wc); + sheet.addCell(housingPay); + Label deduction = new Label(19,0,"工资扣额",wc); + sheet.addCell(deduction); + Label latePay = new Label(19,1,"迟到扣额",wc); + sheet.addCell(latePay); + Label earlyPay = new Label(20,1,"早退扣额",wc); + sheet.addCell(earlyPay); + Label sickPay = new Label(21,1,"病假扣额",wc); + sheet.addCell(sickPay); + Label thingPay = new Label(22,1,"事假扣额",wc); + sheet.addCell(thingPay); + Label individualIncomeTax = new Label(23,1,"个人所得税",wc); + sheet.addCell(individualIncomeTax); + Label rissuePay = new Label(24,0,"补发工资",wc); + sheet.addCell(rissuePay); + sheet.mergeCells(24, 0, 24, 1); //合并上下单元格 + Label shouldPay = new Label(25,0,"应发工资",wc); + sheet.addCell(shouldPay); + sheet.mergeCells(25, 0, 25, 1); //合并上下单元格 + Label actualPay = new Label(26,0,"实发工资",wc); + sheet.addCell(actualPay); + sheet.mergeCells(26, 0, 26, 1); //合并上下单元格 + Label sState = new Label(27,0,"状态",wc); + sheet.mergeCells(27, 0, 27, 1); //合并上下单元格 + sheet.addCell(sState); + + WritableCellFormat cf1 = new WritableCellFormat(DateFormats.FORMAT1); + int row = 1; + for (Salary salary:salaries){ + row++; + sheet.addCell(new Label(0,row,salary.getEmployee().geteAccount())); + sheet.addCell(new Label(1,row,salary.getEmployee().geteName())); + sheet.addCell(new Label(2,row,salary.getDepartment().getdName())); + sheet.addCell(new DateTime(3,row,salary.getsTime(),cf1)); + sheet.addCell(new Number(4,row,salary.getBasePay())); + sheet.addCell(new Number(5,row,salary.getFoodPay())); + sheet.addCell(new Number(6,row,salary.getPostPay())); + sheet.addCell(new Number(7,row,salary.getTrafficPay())); + sheet.addCell(new Number(8,row,salary.getBusinessTravelPay())); + sheet.addCell(new Number(9,row,salary.getWorkingYearPay())); + sheet.addCell(new Number(10,row,salary.getRankPay())); + sheet.addCell(new Number(11,row,salary.getOvertimePay())); + sheet.addCell(new Number(12,row,salary.getFullAttendancePay())); + sheet.addCell(new Number(13,row,salary.getPersionPay())); + sheet.addCell(new Number(14,row,salary.getMedicalPay())); + sheet.addCell(new Number(15,row,salary.getUnemploymentPay())); + sheet.addCell(new Number(16,row,salary.getInjuryPay())); + sheet.addCell(new Number(17,row,salary.getBirthPay())); + sheet.addCell(new Number(18,row,salary.getHousingPay())); + sheet.addCell(new Number(19,row,salary.getLatePay())); + sheet.addCell(new Number(20,row,salary.getEarlyPay())); + sheet.addCell(new Number(21,row,salary.getSickPay())); + sheet.addCell(new Number(22,row,salary.getThingPay())); + sheet.addCell(new Number(23,row,salary.getIndividualIncomeTax())); + sheet.addCell(new Number(24,row,salary.getRissuePay())); + sheet.addCell(new Number(25,row,salary.getShouldPay())); + sheet.addCell(new Number(26,row,salary.getActualPay())); + if(salary.getsState()==0) { + sheet.addCell(new Label(27, row, "未发")); + } + else{ + sheet.addCell(new Label(27,row,"已发")); + } + } + //output.close(); + return writableWorkbook; + } +} diff --git a/main/java/com/esms/utils/MD5Utils.java b/main/java/com/esms/utils/MD5Utils.java new file mode 100644 index 0000000..9be3c3b --- /dev/null +++ b/main/java/com/esms/utils/MD5Utils.java @@ -0,0 +1,67 @@ +package com.esms.utils; + +import java.security.MessageDigest; + +public class MD5Utils { + /** 十六进制下数字到字符的映射数组 */ + private final static String[] hexDigits = { "0", "1", "2", "3", "4", "5", + "6", "7", "8", "9", "a", "b", "c", "d", "e", "f" }; + + /** + * 对字符串进行MD5编码 + * @param originString + * @return + */ + + public static String encodeByMD5(String originString) { + if (originString != null){ + StringBuffer stringBuffer = new StringBuffer(); + stringBuffer.append(originString); + //BookSharingPlatform MD5加密后的32位数字作为秘钥 + stringBuffer.append("esms"); + //加入秘钥后的字符串在进行MD5加密 + originString = String.valueOf(stringBuffer); + try { + MessageDigest md = MessageDigest.getInstance("MD5"); + byte[] results = md.digest(originString .getBytes()); + String resultString = byteArrayToHexString(results); + return resultString.toUpperCase(); + } catch (Exception ex) { + ex.printStackTrace(); + } + } + return null; + } + + /** + * 转换字节数组为16进制字串 + * + * @param b 字节数组 + * @return 十六进制字串 + */ + private static String byteArrayToHexString(byte[] b) { + StringBuffer resultSb = new StringBuffer(); + for (int i = 0; i < b.length; i++) { + resultSb.append(byteToHexString(b[i])); + } + return resultSb.toString(); + } + + /** + * 将一个字节转化成16进制形式的字符串 + * @param b + * @return + */ + private static String byteToHexString(byte b) { + int n = b; + if (n < 0) + n = 256 + n; + int d1 = n / 16; + int d2 = n % 16; + return hexDigits[d1] + hexDigits[d2]; + } + + public static void main(String[] args) { + System.out.println(encodeByMD5("admin")); + } +} diff --git a/main/java/com/esms/utils/PageUtils.java b/main/java/com/esms/utils/PageUtils.java new file mode 100644 index 0000000..b4d2eb5 --- /dev/null +++ b/main/java/com/esms/utils/PageUtils.java @@ -0,0 +1,13 @@ +package com.esms.utils; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; + + +@Controller +public class PageUtils { + @RequestMapping("/toPage.do") + public String toPage(String url) { + return url; + } +} diff --git a/main/java/com/esms/vo/DepartmentPages.java b/main/java/com/esms/vo/DepartmentPages.java new file mode 100644 index 0000000..d94d715 --- /dev/null +++ b/main/java/com/esms/vo/DepartmentPages.java @@ -0,0 +1,56 @@ +package com.esms.vo; + +import com.esms.po.Department; + +import java.util.List; + + +public class DepartmentPages { + Integer code; + String msg; + Integer count; + List data; + + + public Integer getCode() { + return code; + } + + public void setCode(Integer code) { + this.code = code; + } + + public String getMsg() { + return msg; + } + + public void setMsg(String msg) { + this.msg = msg; + } + + public Integer getCount() { + return count; + } + + public void setCount(Integer count) { + this.count = count; + } + + public List getData() { + return data; + } + + public void setData(List data) { + this.data = data; + } + + @Override + public String toString() { + return "DepartmentPages{" + + "code=" + code + + ", msg='" + msg + '\'' + + ", count=" + count + + ", data=" + data + + '}'; + } +} diff --git a/main/java/com/esms/vo/EchDepartmentSalary.java b/main/java/com/esms/vo/EchDepartmentSalary.java new file mode 100644 index 0000000..147f6c2 --- /dev/null +++ b/main/java/com/esms/vo/EchDepartmentSalary.java @@ -0,0 +1,30 @@ +package com.esms.vo; + +import java.util.List; + +/** + * @Description:存放各部门,及其最低,平均,最高工资数值列表 + */ +public class EchDepartmentSalary { + //部门名称 + private String department; + + //工资列表:最低工资,平均工资,最高工资 + private List salary; + + public String getDepartment() { + return department; + } + + public void setDepartment(String department) { + this.department = department; + } + + public List getSalary() { + return salary; + } + + public void setSalary(List salary) { + this.salary = salary; + } +} diff --git a/main/java/com/esms/vo/EchEmployeeNums.java b/main/java/com/esms/vo/EchEmployeeNums.java new file mode 100644 index 0000000..2260dbc --- /dev/null +++ b/main/java/com/esms/vo/EchEmployeeNums.java @@ -0,0 +1,25 @@ +package com.esms.vo; + +/** + * @Description:存放各个部门,及其对应员工数 + */ +public class EchEmployeeNums { + private String department; + private Integer num; + + public String getDepartment() { + return department; + } + + public void setDepartment(String department) { + this.department = department; + } + + public Integer getNum() { + return num; + } + + public void setNum(Integer num) { + this.num = num; + } +} diff --git a/main/java/com/esms/vo/EchMonthSalary.java b/main/java/com/esms/vo/EchMonthSalary.java new file mode 100644 index 0000000..6d05b50 --- /dev/null +++ b/main/java/com/esms/vo/EchMonthSalary.java @@ -0,0 +1,33 @@ +package com.esms.vo; + +import java.util.List; + +/** + * @Description: + */ +public class EchMonthSalary { + private String year; + private List salary; + + public String getYear() { + return year; + } + + public void setYear(String year) { + this.year = year; + } + + public List getSalary() { + return salary; + } + + public void setSalary(List salary) { + this.salary = salary; + } + + public EchMonthSalary(){} + public EchMonthSalary(String year, List salary) { + this.year = year; + this.salary = salary; + } +} diff --git a/main/java/com/esms/vo/EchSalary.java b/main/java/com/esms/vo/EchSalary.java new file mode 100644 index 0000000..9d6eef2 --- /dev/null +++ b/main/java/com/esms/vo/EchSalary.java @@ -0,0 +1,44 @@ +package com.esms.vo; + +/** + * @Description:接收方法salaryMap.selectSalaryByDepartment()的返回值 + */ +public class EchSalary { + private Double minSalary= 0.00; + + private Double avgSalary = 0.00; + + private Double maxSalary = 0.00; + + public Double getMinSalary() { + return minSalary; + } + + public void setMinSalary(Double minSalary) { + this.minSalary = minSalary; + } + + public Double getAvgSalary() { + return avgSalary; + } + + public void setAvgSalary(Double avgSalary) { + this.avgSalary = avgSalary; + } + + public Double getMaxSalary() { + return maxSalary; + } + + public void setMaxSalary(Double maxSalary) { + this.maxSalary = maxSalary; + } + + public EchSalary() { } + + public EchSalary(Double minSalary, Double avgSalary, Double maxSalary) { + this.minSalary = minSalary; + this.avgSalary = avgSalary; + this.maxSalary = maxSalary; + } +} diff --git a/main/java/com/esms/vo/EchSalaryPercent.java b/main/java/com/esms/vo/EchSalaryPercent.java new file mode 100644 index 0000000..772de5d --- /dev/null +++ b/main/java/com/esms/vo/EchSalaryPercent.java @@ -0,0 +1,32 @@ +package com.esms.vo; + + +/** + * @Description: + */ +public class EchSalaryPercent { + private String Department = null; + private Double Salary = 0.00; + + public String getDepartment() { + return Department; + } + + public void setDepartment(String department) { + Department = department; + } + + public Double getSalary() { + return Salary; + } + + public void setSalary(Double salary) { + Salary = salary; + } + public EchSalaryPercent(){} + + public EchSalaryPercent(String department, Double salary) { + Department = department; + Salary = salary; + } +} diff --git a/main/java/com/esms/vo/EmployeeCustomVo.java b/main/java/com/esms/vo/EmployeeCustomVo.java new file mode 100644 index 0000000..078274d --- /dev/null +++ b/main/java/com/esms/vo/EmployeeCustomVo.java @@ -0,0 +1,58 @@ +package com.esms.vo; + +import com.esms.po.Department; +import com.esms.po.Employee; +import com.esms.po.Position; +import com.esms.po.RankBonus; + +/** + * @Description: + */ +public class EmployeeCustomVo { + private Employee employee; + private RankBonus rankBonus; + private Position position; + private Department department; + + public Employee getEmployee() { + return employee; + } + + public void setEmployee(Employee employee) { + this.employee = employee; + } + + public RankBonus getRankBonus() { + return rankBonus; + } + + public void setRankBonus(RankBonus rankBonus) { + this.rankBonus = rankBonus; + } + + public Position getPosition() { + return position; + } + + public void setPosition(Position position) { + this.position = position; + } + + public Department getDepartment() { + return department; + } + + public void setDepartment(Department department) { + this.department = department; + } + + @Override + public String toString() { + return "EmployeeCustomVo{" + + "employee=" + employee + + ", rankBonus=" + rankBonus + + ", position=" + position + + ", department=" + department + + '}'; + } +} diff --git a/main/java/com/esms/vo/EmployeeMonthlyAttendancePages.java b/main/java/com/esms/vo/EmployeeMonthlyAttendancePages.java new file mode 100644 index 0000000..77f965d --- /dev/null +++ b/main/java/com/esms/vo/EmployeeMonthlyAttendancePages.java @@ -0,0 +1,57 @@ +package com.esms.vo; + +import com.esms.po.MonthlyAttendance; + +import java.util.List; + +/** + * @Description: + */ +public class EmployeeMonthlyAttendancePages { + Integer code; + String msg; + Integer count; + List data; + + @Override + public String toString() { + return "EmployeeMonthlyAttendancePages{" + + "code=" + code + + ", msg='" + msg + '\'' + + ", count=" + count + + ", data=" + data + + '}'; + } + + public Integer getCode() { + return code; + } + + public void setCode(Integer code) { + this.code = code; + } + + public String getMsg() { + return msg; + } + + public void setMsg(String msg) { + this.msg = msg; + } + + public Integer getCount() { + return count; + } + + public void setCount(Integer count) { + this.count = count; + } + + public List getData() { + return data; + } + + public void setData(List data) { + this.data = data; + } +} diff --git a/main/java/com/esms/vo/EmployeePages.java b/main/java/com/esms/vo/EmployeePages.java new file mode 100644 index 0000000..7570a56 --- /dev/null +++ b/main/java/com/esms/vo/EmployeePages.java @@ -0,0 +1,55 @@ +package com.esms.vo; + +import java.util.List; + +/** + * @Description: + */ +public class EmployeePages { + Integer code; + String msg; + Integer count; + List data; + + @Override + public String toString() { + return "EmployeePages{" + + "code=" + code + + ", msg='" + msg + '\'' + + ", count=" + count + + ", data=" + data + + '}'; + } + + public Integer getCode() { + return code; + } + + public void setCode(Integer code) { + this.code = code; + } + + public String getMsg() { + return msg; + } + + public void setMsg(String msg) { + this.msg = msg; + } + + public Integer getCount() { + return count; + } + + public void setCount(Integer count) { + this.count = count; + } + + public List getData() { + return data; + } + + public void setData(List data) { + this.data = data; + } +} diff --git a/main/java/com/esms/vo/EmployeeSalaryVO.java b/main/java/com/esms/vo/EmployeeSalaryVO.java new file mode 100644 index 0000000..a4b3eae --- /dev/null +++ b/main/java/com/esms/vo/EmployeeSalaryVO.java @@ -0,0 +1,57 @@ +package com.esms.vo; + +import com.esms.po.Salary; + +import java.util.List; + +/** + * @Description: + */ +public class EmployeeSalaryVO { + Integer code; + String msg; + Integer count; + List data; + + public Integer getCode() { + return code; + } + + public void setCode(Integer code) { + this.code = code; + } + + public String getMsg() { + return msg; + } + + public void setMsg(String msg) { + this.msg = msg; + } + + public Integer getCount() { + return count; + } + + public void setCount(Integer count) { + this.count = count; + } + + public List getData() { + return data; + } + + public void setData(List data) { + this.data = data; + } + + @Override + public String toString() { + return "EmployeeSalaryVO{" + + "code=" + code + + ", msg='" + msg + '\'' + + ", count=" + count + + ", data=" + data + + '}'; + } +} diff --git a/main/java/com/esms/vo/MonthlyAttendanceCustomVo.java b/main/java/com/esms/vo/MonthlyAttendanceCustomVo.java new file mode 100644 index 0000000..aa497bf --- /dev/null +++ b/main/java/com/esms/vo/MonthlyAttendanceCustomVo.java @@ -0,0 +1,47 @@ +package com.esms.vo; + +import com.esms.po.Department; +import com.esms.po.Employee; +import com.esms.po.MonthlyAttendance; + +/** + * @Description: + */ +public class MonthlyAttendanceCustomVo { + private MonthlyAttendance monthlyAttendance = null; + private Employee employee = null; + private Department department = null; + + @Override + public String toString() { + return "MonthlyAttendanceCustomVo{" + + "monthlyAttendance=" + monthlyAttendance + + ", employee=" + employee + + ", department=" + department + + '}'; + } + + public MonthlyAttendance getMonthlyAttendance() { + return monthlyAttendance; + } + + public void setMonthlyAttendance(MonthlyAttendance monthlyAttendance) { + this.monthlyAttendance = monthlyAttendance; + } + + public Employee getEmployee() { + return employee; + } + + public void setEmployee(Employee employee) { + this.employee = employee; + } + + public Department getDepartment() { + return department; + } + + public void setDepartment(Department department) { + this.department = department; + } +} diff --git a/main/java/com/esms/vo/MonthlyAttendancePages.java b/main/java/com/esms/vo/MonthlyAttendancePages.java new file mode 100644 index 0000000..2360566 --- /dev/null +++ b/main/java/com/esms/vo/MonthlyAttendancePages.java @@ -0,0 +1,56 @@ +package com.esms.vo; + +import java.util.List; + +/** + * + * @Description: + */ +public class MonthlyAttendancePages { + Integer code; + String msg; + Integer count; + List data; + + public Integer getCode() { + return code; + } + + public void setCode(Integer code) { + this.code = code; + } + + public String getMsg() { + return msg; + } + + public void setMsg(String msg) { + this.msg = msg; + } + + public Integer getCount() { + return count; + } + + public void setCount(Integer count) { + this.count = count; + } + + public List getData() { + return data; + } + + public void setData(List data) { + this.data = data; + } + + @Override + public String toString() { + return "MonthlyAttendancePages{" + + "code=" + code + + ", msg='" + msg + '\'' + + ", count=" + count + + ", data=" + data + + '}'; + } +} diff --git a/main/java/com/esms/vo/PositionPages.java b/main/java/com/esms/vo/PositionPages.java new file mode 100644 index 0000000..3167337 --- /dev/null +++ b/main/java/com/esms/vo/PositionPages.java @@ -0,0 +1,57 @@ +package com.esms.vo; + +import com.esms.po.Position; + +import java.util.List; + +/** + * @Description: + */ +public class PositionPages { + Integer code; + String msg; + Integer count; + List data; + + public Integer getCode() { + return code; + } + + public void setCode(Integer code) { + this.code = code; + } + + public String getMsg() { + return msg; + } + + public void setMsg(String msg) { + this.msg = msg; + } + + public Integer getCount() { + return count; + } + + public void setCount(Integer count) { + this.count = count; + } + + public List getData() { + return data; + } + + public void setData(List data) { + this.data = data; + } + + @Override + public String toString() { + return "PositionPages{" + + "code=" + code + + ", msg='" + msg + '\'' + + ", count=" + count + + ", data=" + data + + '}'; + } +} diff --git a/main/java/com/esms/vo/RankBonusPages.java b/main/java/com/esms/vo/RankBonusPages.java new file mode 100644 index 0000000..0cea41d --- /dev/null +++ b/main/java/com/esms/vo/RankBonusPages.java @@ -0,0 +1,58 @@ +package com.esms.vo; + +import com.esms.po.Department; +import com.esms.po.RankBonus; + +import java.util.List; + +/** + * @Description: + */ +public class RankBonusPages { + Integer code; + String msg; + Integer count; + List data; + + @Override + public String toString() { + return "RankBonusPages{" + + "code=" + code + + ", msg='" + msg + '\'' + + ", count=" + count + + ", data=" + data + + '}'; + } + + public String getMsg() { + return msg; + } + + public void setMsg(String msg) { + this.msg = msg; + } + + public Integer getCount() { + return count; + } + + public void setCount(Integer count) { + this.count = count; + } + + public List getData() { + return data; + } + + public void setData(List data) { + this.data = data; + } + + public Integer getCode() { + return code; + } + + public void setCode(Integer code) { + this.code = code; + } +} diff --git a/main/java/com/esms/vo/SalaryPages.java b/main/java/com/esms/vo/SalaryPages.java new file mode 100644 index 0000000..f3369af --- /dev/null +++ b/main/java/com/esms/vo/SalaryPages.java @@ -0,0 +1,55 @@ +package com.esms.vo; + +import com.esms.po.Salary; + +import java.util.List; + +/** + * @Description: + */ +public class SalaryPages { + Integer code; + String msg; + Integer count; + List data; + public SalaryPages(){} + + public SalaryPages(Integer code, String msg, Integer count, List data) { + this.code = code; + this.msg = msg; + this.count = count; + this.data = data; + } + + public Integer getCode() { + return code; + } + + public void setCode(Integer code) { + this.code = code; + } + + public String getMsg() { + return msg; + } + + public void setMsg(String msg) { + this.msg = msg; + } + + public Integer getCount() { + return count; + } + + public void setCount(Integer count) { + this.count = count; + } + + public List getData() { + return data; + } + + public void setData(List data) { + this.data = data; + } +} diff --git a/main/java/com/esms/vo/WokingYearsBonusPages.java b/main/java/com/esms/vo/WokingYearsBonusPages.java new file mode 100644 index 0000000..0279667 --- /dev/null +++ b/main/java/com/esms/vo/WokingYearsBonusPages.java @@ -0,0 +1,57 @@ +package com.esms.vo; + +import com.esms.po.RankBonus; + +import java.util.List; + +/** + * @Description: + */ +public class WokingYearsBonusPages { + Integer code; + String msg; + Integer count; + List data; + + public Integer getCode() { + return code; + } + + public void setCode(Integer code) { + this.code = code; + } + + public String getMsg() { + return msg; + } + + public void setMsg(String msg) { + this.msg = msg; + } + + public Integer getCount() { + return count; + } + + public void setCount(Integer count) { + this.count = count; + } + + public List getData() { + return data; + } + + public void setData(List data) { + this.data = data; + } + + @Override + public String toString() { + return "WokingYearsBonusPages{" + + "code=" + code + + ", msg='" + msg + '\'' + + ", count=" + count + + ", data=" + data + + '}'; + } +} diff --git a/main/resources/applicationContext.xml b/main/resources/applicationContext.xml new file mode 100644 index 0000000..51f2929 --- /dev/null +++ b/main/resources/applicationContext.xml @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/main/resources/dbconfig.properties b/main/resources/dbconfig.properties new file mode 100644 index 0000000..a3862c7 --- /dev/null +++ b/main/resources/dbconfig.properties @@ -0,0 +1,4 @@ +jdbc.jdbcUrl=jdbc:mysql://127.0.0.1:3306/ssm_esms?useSSL=false&useUnicode=true&characterEncoding=utf8&serverTimezone=UTC +jdbc.driverClass=com.mysql.jdbc.Driver +jdbc.user=root +jdbc.password=123456 \ No newline at end of file diff --git a/main/resources/log4j.properties b/main/resources/log4j.properties new file mode 100644 index 0000000..4e9a92e --- /dev/null +++ b/main/resources/log4j.properties @@ -0,0 +1,14 @@ +#1.\u539F\u7248\uFF08\u8BE6\u7EC6\uFF09 +#log4j.rootLogger=DEBUG,myConsole +#log4j.appender.myConsole=org.apache.log4j.ConsoleAppender +#log4j.appender.myConsole.ImmediateFlush=true +#log4j.appender.myConsole.Target=System.out +#log4j.appender.myConsole.layout=org.apache.log4j.PatternLayout +#log4j.appender.myConsole.layout.ConversionPattern=[%-5p] %d(%r) --> [%t] %l: %m %x %n +# +#log4j.logger.com.mchange.v2=ERROR +#2.\u7B80\u7565\u7248 +log4j.rootLogger=DEBUG, stdout +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n diff --git a/main/resources/mappers/DepartmentMapper.xml b/main/resources/mappers/DepartmentMapper.xml new file mode 100644 index 0000000..ee981de --- /dev/null +++ b/main/resources/mappers/DepartmentMapper.xml @@ -0,0 +1,117 @@ + + + + + + + + + + + d_id, d_name, d_remark, d_isdel + + + + + + + + update department set d_isdel = 0 where d_id in + + #{id} + + + + + + + delete from department + where d_id = #{dId,jdbcType=INTEGER} + + + + SELECT LAST_INSERT_ID() + + insert into department (d_name, d_remark, d_isdel + ) + values (#{dName,jdbcType=VARCHAR}, #{dRemark,jdbcType=VARCHAR}, #{dIsdel,jdbcType=INTEGER} + ) + + + + SELECT LAST_INSERT_ID() + + insert into department + + + d_name, + + + d_remark, + + + d_isdel, + + + + + #{dName,jdbcType=VARCHAR}, + + + #{dRemark,jdbcType=VARCHAR}, + + + #{dIsdel,jdbcType=INTEGER}, + + + + + update department + + + d_name = #{dName,jdbcType=VARCHAR}, + + + d_remark = #{dRemark,jdbcType=VARCHAR}, + + + d_isdel = #{dIsdel,jdbcType=INTEGER}, + + + where d_id = #{dId,jdbcType=INTEGER} + + + update department + set d_name = #{dName,jdbcType=VARCHAR}, + d_remark = #{dRemark,jdbcType=VARCHAR}, + d_isdel = #{dIsdel,jdbcType=INTEGER} + where d_id = #{dId,jdbcType=INTEGER} + + \ No newline at end of file diff --git a/main/resources/mappers/EmployeeCustomVoMapper.xml b/main/resources/mappers/EmployeeCustomVoMapper.xml new file mode 100644 index 0000000..a03c4e5 --- /dev/null +++ b/main/resources/mappers/EmployeeCustomVoMapper.xml @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/main/resources/mappers/EmployeeMapper.xml b/main/resources/mappers/EmployeeMapper.xml new file mode 100644 index 0000000..8b51824 --- /dev/null +++ b/main/resources/mappers/EmployeeMapper.xml @@ -0,0 +1,348 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + e_id, e_account, e_password, e_name, e_idcard, e_sex, e_dagree, e_birthday, e_email, + e_phone, e_hometown, e_rank, e_head_path, e_urgent_person, e_urgent_phone, p_id, + d_id, e_base_pay, e_isdel, e_entry_time, e_leave_time + + + + + update employee set e_isdel = 0 where e_id = #{eId,jdbcType=INTEGER} + + + + + + + + + + + + + + + + + + + + + + + SELECT LAST_INSERT_ID() + + insert into employee (e_account, e_password, e_name, + e_idcard, e_sex, e_dagree, + e_birthday, e_email, e_phone, + e_hometown, e_rank, e_head_path, + e_urgent_person, e_urgent_phone, p_id, + d_id, e_base_pay, e_isdel, + e_entry_time, e_leave_time) + values (#{eAccount,jdbcType=VARCHAR}, #{ePassword,jdbcType=VARCHAR}, #{eName,jdbcType=VARCHAR}, + #{eIdcard,jdbcType=VARCHAR}, #{eSex,jdbcType=VARCHAR}, #{eDagree,jdbcType=VARCHAR}, + #{eBirthday,jdbcType=DATE}, #{eEmail,jdbcType=VARCHAR}, #{ePhone,jdbcType=VARCHAR}, + #{eHometown,jdbcType=VARCHAR}, #{eRank,jdbcType=INTEGER}, #{eHeadPath,jdbcType=VARCHAR}, + #{eUrgentPerson,jdbcType=VARCHAR}, #{eUrgentPhone,jdbcType=VARCHAR}, #{pId,jdbcType=INTEGER}, + #{dId,jdbcType=INTEGER}, #{eBasePay,jdbcType=DOUBLE}, #{eIsdel,jdbcType=INTEGER}, + #{eEntryTime,jdbcType=DATE}, #{eLeaveTime,jdbcType=DATE}) + + + + SELECT LAST_INSERT_ID() + + insert into employee + + + e_account, + + + e_password, + + + e_name, + + + e_idcard, + + + e_sex, + + + e_dagree, + + + e_birthday, + + + e_email, + + + e_phone, + + + e_hometown, + + + e_rank, + + + e_head_path, + + + e_urgent_person, + + + e_urgent_phone, + + + p_id, + + + d_id, + + + e_base_pay, + + + e_isdel, + + + e_entry_time, + + + e_leave_time, + + + + + #{eAccount,jdbcType=VARCHAR}, + + + #{ePassword,jdbcType=VARCHAR}, + + + #{eName,jdbcType=VARCHAR}, + + + #{eIdcard,jdbcType=VARCHAR}, + + + #{eSex,jdbcType=INTEGER}, + + + #{eDagree,jdbcType=VARCHAR}, + + + #{eBirthday,jdbcType=DATE}, + + + #{eEmail,jdbcType=VARCHAR}, + + + #{ePhone,jdbcType=VARCHAR}, + + + #{eHometown,jdbcType=VARCHAR}, + + + #{eRank,jdbcType=INTEGER}, + + + #{eHeadPath,jdbcType=VARCHAR}, + + + #{eUrgentPerson,jdbcType=VARCHAR}, + + + #{eUrgentPhone,jdbcType=VARCHAR}, + + + #{pId,jdbcType=INTEGER}, + + + #{dId,jdbcType=INTEGER}, + + + #{eBasePay,jdbcType=DOUBLE}, + + + #{eIsdel,jdbcType=INTEGER}, + + + #{eEntryTime,jdbcType=DATE}, + + + #{eLeaveTime,jdbcType=DATE}, + + + + + update employee + + + e_account = #{eAccount,jdbcType=VARCHAR}, + + + e_password = #{ePassword,jdbcType=VARCHAR}, + + + e_name = #{eName,jdbcType=VARCHAR}, + + + e_idcard = #{eIdcard,jdbcType=VARCHAR}, + + + e_sex = #{eSex,jdbcType=VARCHAR}, + + + e_dagree = #{eDagree,jdbcType=VARCHAR}, + + + e_birthday = #{eBirthday,jdbcType=DATE}, + + + e_email = #{eEmail,jdbcType=VARCHAR}, + + + e_phone = #{ePhone,jdbcType=VARCHAR}, + + + e_hometown = #{eHometown,jdbcType=VARCHAR}, + + + e_rank = #{eRank,jdbcType=INTEGER}, + + + e_head_path = #{eHeadPath,jdbcType=VARCHAR}, + + + e_urgent_person = #{eUrgentPerson,jdbcType=VARCHAR}, + + + e_urgent_phone = #{eUrgentPhone,jdbcType=VARCHAR}, + + + p_id = #{pId,jdbcType=INTEGER}, + + + d_id = #{dId,jdbcType=INTEGER}, + + + e_base_pay = #{eBasePay,jdbcType=DOUBLE}, + + + e_isdel = #{eIsdel,jdbcType=INTEGER}, + + + e_entry_time = #{eEntryTime,jdbcType=DATE}, + + + e_leave_time = #{eLeaveTime,jdbcType=DATE}, + + + where e_id = #{eId,jdbcType=INTEGER} + + + update employee + set e_account = #{eAccount,jdbcType=VARCHAR}, + e_password = #{ePassword,jdbcType=VARCHAR}, + e_name = #{eName,jdbcType=VARCHAR}, + e_idcard = #{eIdcard,jdbcType=VARCHAR}, + e_sex = #{eSex,jdbcType=VARCHAR}, + e_dagree = #{eDagree,jdbcType=VARCHAR}, + e_birthday = #{eBirthday,jdbcType=DATE}, + e_email = #{eEmail,jdbcType=VARCHAR}, + e_phone = #{ePhone,jdbcType=VARCHAR}, + e_hometown = #{eHometown,jdbcType=VARCHAR}, + e_rank = #{eRank,jdbcType=INTEGER}, + e_head_path = #{eHeadPath,jdbcType=VARCHAR}, + e_urgent_person = #{eUrgentPerson,jdbcType=VARCHAR}, + e_urgent_phone = #{eUrgentPhone,jdbcType=VARCHAR}, + p_id = #{pId,jdbcType=INTEGER}, + d_id = #{dId,jdbcType=INTEGER}, + e_base_pay = #{eBasePay,jdbcType=DOUBLE}, + e_isdel = #{eIsdel,jdbcType=INTEGER}, + e_entry_time = #{eEntryTime,jdbcType=DATE}, + e_leave_time = #{eLeaveTime,jdbcType=DATE} + where e_id = #{eId,jdbcType=INTEGER} + + + + + + + + + + + + + \ No newline at end of file diff --git a/main/resources/mappers/KeyValueMapper.xml b/main/resources/mappers/KeyValueMapper.xml new file mode 100644 index 0000000..760c143 --- /dev/null +++ b/main/resources/mappers/KeyValueMapper.xml @@ -0,0 +1,75 @@ + + + + + + + + + + kv_id, kv_key, kv_value + + + + + + delete from key_value + where kv_id = #{kvId,jdbcType=INTEGER} + + + + SELECT LAST_INSERT_ID() + + insert into key_value (kv_key, kv_value) + values (#{kvKey,jdbcType=VARCHAR}, #{kvValue,jdbcType=DOUBLE}) + + + + SELECT LAST_INSERT_ID() + + insert into key_value + + + kv_key, + + + kv_value, + + + + + #{kvKey,jdbcType=VARCHAR}, + + + #{kvValue,jdbcType=DOUBLE}, + + + + + update key_value + + + kv_key = #{kvKey,jdbcType=VARCHAR}, + + + kv_value = #{kvValue,jdbcType=DOUBLE}, + + + where kv_id = #{kvId,jdbcType=INTEGER} + + + update key_value + set kv_key = #{kvKey,jdbcType=VARCHAR}, + kv_value = #{kvValue,jdbcType=DOUBLE} + where kv_id = #{kvId,jdbcType=INTEGER} + + \ No newline at end of file diff --git a/main/resources/mappers/MonthlyAttendanceCustomVoMapper.xml b/main/resources/mappers/MonthlyAttendanceCustomVoMapper.xml new file mode 100644 index 0000000..05262ae --- /dev/null +++ b/main/resources/mappers/MonthlyAttendanceCustomVoMapper.xml @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/main/resources/mappers/MonthlyAttendanceMapper.xml b/main/resources/mappers/MonthlyAttendanceMapper.xml new file mode 100644 index 0000000..e4e6afa --- /dev/null +++ b/main/resources/mappers/MonthlyAttendanceMapper.xml @@ -0,0 +1,207 @@ + + + + + + + + + + + + + + + + + + + + + + delete from monthly_attendance where ma_id in + + #{id} + + + + + ma_id, e_id, attendance_time, sick_leave_num, overtime_hour, weekend_hour, holiday_hour, + late_num, early_num, absence_num, business_travel_num, compassionate_leave_num + + + + + + delete from monthly_attendance + where ma_id = #{maId,jdbcType=INTEGER} + + + + SELECT LAST_INSERT_ID() + + insert into monthly_attendance (e_id, attendance_time, sick_leave_num, + overtime_hour, weekend_hour, holiday_hour, + late_num, early_num, absence_num, + business_travel_num, compassionate_leave_num + ) + values (#{employee.eId,jdbcType=INTEGER}, #{attendanceTime,jdbcType=DATE}, #{sickLeaveNum,jdbcType=INTEGER}, + #{overtimeHour,jdbcType=DOUBLE}, #{weekendHour,jdbcType=DOUBLE}, #{holidayHour,jdbcType=DOUBLE}, + #{lateNum,jdbcType=INTEGER}, #{earlyNum,jdbcType=INTEGER}, #{absenceNum,jdbcType=INTEGER}, + #{businessTravelNum,jdbcType=INTEGER}, #{compassionateLeaveNum,jdbcType=INTEGER} + ) + + + + SELECT LAST_INSERT_ID() + + insert into monthly_attendance + + + e_id, + + + attendance_time, + + + sick_leave_num, + + + overtime_hour, + + + weekend_hour, + + + holiday_hour, + + + late_num, + + + early_num, + + + absence_num, + + + business_travel_num, + + + compassionate_leave_num, + + + + + #{employee.eId,jdbcType=INTEGER}, + + + #{attendanceTime,jdbcType=DATE}, + + + #{sickLeaveNum,jdbcType=INTEGER}, + + + #{overtimeHour,jdbcType=DOUBLE}, + + + #{weekendHour,jdbcType=DOUBLE}, + + + #{holidayHour,jdbcType=DOUBLE}, + + + #{lateNum,jdbcType=INTEGER}, + + + #{earlyNum,jdbcType=INTEGER}, + + + #{absenceNum,jdbcType=INTEGER}, + + + #{businessTravelNum,jdbcType=INTEGER}, + + + #{compassionateLeaveNum,jdbcType=INTEGER}, + + + + + update monthly_attendance + + + e_id = #{employee.eId,jdbcType=INTEGER}, + + + attendance_time = #{attendanceTime,jdbcType=DATE}, + + + sick_leave_num = #{sickLeaveNum,jdbcType=INTEGER}, + + + overtime_hour = #{overtimeHour,jdbcType=DOUBLE}, + + + weekend_hour = #{weekendHour,jdbcType=DOUBLE}, + + + holiday_hour = #{holidayHour,jdbcType=DOUBLE}, + + + late_num = #{lateNum,jdbcType=INTEGER}, + + + early_num = #{earlyNum,jdbcType=INTEGER}, + + + absence_num = #{absenceNum,jdbcType=INTEGER}, + + + business_travel_num = #{businessTravelNum,jdbcType=INTEGER}, + + + compassionate_leave_num = #{compassionateLeaveNum,jdbcType=INTEGER}, + + + where ma_id = #{maId,jdbcType=INTEGER} + + + update monthly_attendance + set e_id = #{employee.eId,jdbcType=INTEGER}, + attendance_time = #{attendanceTime,jdbcType=DATE}, + sick_leave_num = #{sickLeaveNum,jdbcType=INTEGER}, + overtime_hour = #{overtimeHour,jdbcType=DOUBLE}, + weekend_hour = #{weekendHour,jdbcType=DOUBLE}, + holiday_hour = #{holidayHour,jdbcType=DOUBLE}, + late_num = #{lateNum,jdbcType=INTEGER}, + early_num = #{earlyNum,jdbcType=INTEGER}, + absence_num = #{absenceNum,jdbcType=INTEGER}, + business_travel_num = #{businessTravelNum,jdbcType=INTEGER}, + compassionate_leave_num = #{compassionateLeaveNum,jdbcType=INTEGER} + where ma_id = #{maId,jdbcType=INTEGER} + + + + \ No newline at end of file diff --git a/main/resources/mappers/PositionMapper.xml b/main/resources/mappers/PositionMapper.xml new file mode 100644 index 0000000..e34c4bc --- /dev/null +++ b/main/resources/mappers/PositionMapper.xml @@ -0,0 +1,125 @@ + + + + + + + + + + + + p_id, p_name, p_duty, p_post_pay, p_isdel + + + + + + + + update position set p_isdel = 0 where p_id = #{pId,jdbcType=INTEGER} + + + + update position set p_isdel = 0 where p_id in + + #{id} + + + + + + + + + SELECT LAST_INSERT_ID() + + insert into position (p_name, p_duty, p_post_pay, + p_isdel) + values (#{pName,jdbcType=VARCHAR}, #{pDuty,jdbcType=VARCHAR}, #{pPostPay,jdbcType=DOUBLE}, + #{pIsdel,jdbcType=INTEGER}) + + + + SELECT LAST_INSERT_ID() + + insert into position + + + p_name, + + + p_duty, + + + p_post_pay, + + + p_isdel, + + + + + #{pName,jdbcType=VARCHAR}, + + + #{pDuty,jdbcType=VARCHAR}, + + + #{pPostPay,jdbcType=DOUBLE}, + + + #{pIsdel,jdbcType=INTEGER}, + + + + + update position + + + p_name = #{pName,jdbcType=VARCHAR}, + + + p_duty = #{pDuty,jdbcType=VARCHAR}, + + + p_post_pay = #{pPostPay,jdbcType=DOUBLE}, + + + p_isdel = #{pIsdel,jdbcType=INTEGER}, + + + where p_id = #{pId,jdbcType=INTEGER} + + + update position + set p_name = #{pName,jdbcType=VARCHAR}, + p_duty = #{pDuty,jdbcType=VARCHAR}, + p_post_pay = #{pPostPay,jdbcType=DOUBLE}, + p_isdel = #{pIsdel,jdbcType=INTEGER} + where p_id = #{pId,jdbcType=INTEGER} + + \ No newline at end of file diff --git a/main/resources/mappers/RankBonusMapper.xml b/main/resources/mappers/RankBonusMapper.xml new file mode 100644 index 0000000..14cbd82 --- /dev/null +++ b/main/resources/mappers/RankBonusMapper.xml @@ -0,0 +1,117 @@ + + + + + + + + + + rb_id, rank_name, rb_bonus + + + + + + + + + + + + delete from rank_bonus + where rb_id = #{rbId,jdbcType=INTEGER} + + + + + + SELECT LAST_INSERT_ID() + + insert into rank_bonus (rank_name, rb_bonus) + values (#{rankName,jdbcType=VARCHAR}, #{rbBonus,jdbcType=INTEGER}) + + + + + + SELECT LAST_INSERT_ID() + + insert into rank_bonus + + + rank_name, + + + rb_bonus, + + + + + #{rankName,jdbcType=VARCHAR}, + + + #{rbBonus,jdbcType=INTEGER}, + + + + + + + + + update rank_bonus + + + rank_name = #{rankName,jdbcType=VARCHAR}, + + + rb_bonus = #{rbBonus,jdbcType=INTEGER}, + + + where rb_id = #{rbId,jdbcType=INTEGER} + + + + + update rank_bonus + set rank_name = #{rankName,jdbcType=VARCHAR}, + rb_bonus = #{rbBonus,jdbcType=INTEGER} + where rb_id = #{rbId,jdbcType=INTEGER} + + + + + + + + + + + + \ No newline at end of file diff --git a/main/resources/mappers/SalaryMapper.xml b/main/resources/mappers/SalaryMapper.xml new file mode 100644 index 0000000..fc7d44a --- /dev/null +++ b/main/resources/mappers/SalaryMapper.xml @@ -0,0 +1,476 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s_id, e_id, d_id, s_time, s_state, base_pay, food_pay, post_pay, working_year_pay, + rank_pay, traffic_pay, persion_pay, medical_pay, unemployment_pay, injury_pay, birth_pay, + housing_pay, late_pay, early_pay, overtime_pay, sick_pay, thing_pay, business_travel_pay, + full_attendance_pay, rissue_pay, individual_income_tax, should_pay, actual_pay + + + + delete from salary + where s_id = #{sId,jdbcType=INTEGER} + + + + SELECT LAST_INSERT_ID() + + insert into salary (e_id, d_id, s_time, + s_state, base_pay, food_pay, + post_pay, working_year_pay, rank_pay, + traffic_pay, persion_pay, medical_pay, + unemployment_pay, injury_pay, birth_pay, + housing_pay, late_pay, early_pay, + overtime_pay, sick_pay, thing_pay, + business_travel_pay, full_attendance_pay, rissue_pay, + individual_income_tax, should_pay, actual_pay + ) + values (#{employee.eId,jdbcType=INTEGER}, #{department.dId,jdbcType=INTEGER}, #{sTime,jdbcType=DATE}, + #{sState,jdbcType=INTEGER}, #{basePay,jdbcType=DOUBLE}, #{foodPay,jdbcType=DOUBLE}, + #{postPay,jdbcType=DOUBLE}, #{workingYearPay,jdbcType=DOUBLE}, #{rankPay,jdbcType=DOUBLE}, + #{trafficPay,jdbcType=DOUBLE}, #{persionPay,jdbcType=DOUBLE}, #{medicalPay,jdbcType=DOUBLE}, + #{unemploymentPay,jdbcType=DOUBLE}, #{injuryPay,jdbcType=DOUBLE}, #{birthPay,jdbcType=DOUBLE}, + #{housingPay,jdbcType=DOUBLE}, #{latePay,jdbcType=DOUBLE}, #{earlyPay,jdbcType=DOUBLE}, + #{overtimePay,jdbcType=DOUBLE}, #{sickPay,jdbcType=DOUBLE}, #{thingPay,jdbcType=DOUBLE}, + #{businessTravelPay,jdbcType=DOUBLE}, #{fullAttendancePay,jdbcType=DOUBLE}, #{rissuePay,jdbcType=DOUBLE}, + #{individualIncomeTax,jdbcType=DOUBLE}, #{shouldPay,jdbcType=DOUBLE}, #{actualPay,jdbcType=DOUBLE} + ) + + + + SELECT LAST_INSERT_ID() + + insert into salary + + + e_id, + + + d_id, + + + s_time, + + + s_state, + + + base_pay, + + + food_pay, + + + post_pay, + + + working_year_pay, + + + rank_pay, + + + traffic_pay, + + + persion_pay, + + + medical_pay, + + + unemployment_pay, + + + injury_pay, + + + birth_pay, + + + housing_pay, + + + late_pay, + + + early_pay, + + + overtime_pay, + + + sick_pay, + + + thing_pay, + + + business_travel_pay, + + + full_attendance_pay, + + + rissue_pay, + + + individual_income_tax, + + + should_pay, + + + actual_pay, + + + + + #{employee.eId,jdbcType=INTEGER}, + + + #{department.dId,jdbcType=INTEGER}, + + + #{sTime,jdbcType=DATE}, + + + #{sState,jdbcType=INTEGER}, + + + #{basePay,jdbcType=DOUBLE}, + + + #{foodPay,jdbcType=DOUBLE}, + + + #{postPay,jdbcType=DOUBLE}, + + + #{workingYearPay,jdbcType=DOUBLE}, + + + #{rankPay,jdbcType=DOUBLE}, + + + #{trafficPay,jdbcType=DOUBLE}, + + + #{persionPay,jdbcType=DOUBLE}, + + + #{medicalPay,jdbcType=DOUBLE}, + + + #{unemploymentPay,jdbcType=DOUBLE}, + + + #{injuryPay,jdbcType=DOUBLE}, + + + #{birthPay,jdbcType=DOUBLE}, + + + #{housingPay,jdbcType=DOUBLE}, + + + #{latePay,jdbcType=DOUBLE}, + + + #{earlyPay,jdbcType=DOUBLE}, + + + #{overtimePay,jdbcType=DOUBLE}, + + + #{sickPay,jdbcType=DOUBLE}, + + + #{thingPay,jdbcType=DOUBLE}, + + + #{businessTravelPay,jdbcType=DOUBLE}, + + + #{fullAttendancePay,jdbcType=DOUBLE}, + + + #{rissuePay,jdbcType=DOUBLE}, + + + #{individualIncomeTax,jdbcType=DOUBLE}, + + + #{shouldPay,jdbcType=DOUBLE}, + + + #{actualPay,jdbcType=DOUBLE}, + + + + + update salary + + + e_id = #{employee.eId,jdbcType=INTEGER}, + + + d_id = #{department.dId,jdbcType=INTEGER}, + + + s_time = #{sTime,jdbcType=DATE}, + + + s_state = #{sState,jdbcType=INTEGER}, + + + base_pay = #{basePay,jdbcType=DOUBLE}, + + + food_pay = #{foodPay,jdbcType=DOUBLE}, + + + post_pay = #{postPay,jdbcType=DOUBLE}, + + + working_year_pay = #{workingYearPay,jdbcType=DOUBLE}, + + + rank_pay = #{rankPay,jdbcType=DOUBLE}, + + + traffic_pay = #{trafficPay,jdbcType=DOUBLE}, + + + persion_pay = #{persionPay,jdbcType=DOUBLE}, + + + medical_pay = #{medicalPay,jdbcType=DOUBLE}, + + + unemployment_pay = #{unemploymentPay,jdbcType=DOUBLE}, + + + injury_pay = #{injuryPay,jdbcType=DOUBLE}, + + + birth_pay = #{birthPay,jdbcType=DOUBLE}, + + + housing_pay = #{housingPay,jdbcType=DOUBLE}, + + + late_pay = #{latePay,jdbcType=DOUBLE}, + + + early_pay = #{earlyPay,jdbcType=DOUBLE}, + + + overtime_pay = #{overtimePay,jdbcType=DOUBLE}, + + + sick_pay = #{sickPay,jdbcType=DOUBLE}, + + + thing_pay = #{thingPay,jdbcType=DOUBLE}, + + + business_travel_pay = #{businessTravelPay,jdbcType=DOUBLE}, + + + full_attendance_pay = #{fullAttendancePay,jdbcType=DOUBLE}, + + + rissue_pay = #{rissuePay,jdbcType=DOUBLE}, + + + individual_income_tax = #{individualIncomeTax,jdbcType=DOUBLE}, + + + should_pay = #{shouldPay,jdbcType=DOUBLE}, + + + actual_pay = #{actualPay,jdbcType=DOUBLE}, + + + where s_id = #{sId,jdbcType=INTEGER} + + + update salary + set e_id = #{employee.eId,jdbcType=INTEGER}, + d_id = #{department.dId,jdbcType=INTEGER}, + s_time = #{sTime,jdbcType=DATE}, + s_state = #{sState,jdbcType=INTEGER}, + base_pay = #{basePay,jdbcType=DOUBLE}, + food_pay = #{foodPay,jdbcType=DOUBLE}, + post_pay = #{postPay,jdbcType=DOUBLE}, + working_year_pay = #{workingYearPay,jdbcType=DOUBLE}, + rank_pay = #{rankPay,jdbcType=DOUBLE}, + traffic_pay = #{trafficPay,jdbcType=DOUBLE}, + persion_pay = #{persionPay,jdbcType=DOUBLE}, + medical_pay = #{medicalPay,jdbcType=DOUBLE}, + unemployment_pay = #{unemploymentPay,jdbcType=DOUBLE}, + injury_pay = #{injuryPay,jdbcType=DOUBLE}, + birth_pay = #{birthPay,jdbcType=DOUBLE}, + housing_pay = #{housingPay,jdbcType=DOUBLE}, + late_pay = #{latePay,jdbcType=DOUBLE}, + early_pay = #{earlyPay,jdbcType=DOUBLE}, + overtime_pay = #{overtimePay,jdbcType=DOUBLE}, + sick_pay = #{sickPay,jdbcType=DOUBLE}, + thing_pay = #{thingPay,jdbcType=DOUBLE}, + business_travel_pay = #{businessTravelPay,jdbcType=DOUBLE}, + full_attendance_pay = #{fullAttendancePay,jdbcType=DOUBLE}, + rissue_pay = #{rissuePay,jdbcType=DOUBLE}, + individual_income_tax = #{individualIncomeTax,jdbcType=DOUBLE}, + should_pay = #{shouldPay,jdbcType=DOUBLE}, + actual_pay = #{actualPay,jdbcType=DOUBLE} + where s_id = #{sId,jdbcType=INTEGER} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/main/resources/mappers/SystemManagerMapper.xml b/main/resources/mappers/SystemManagerMapper.xml new file mode 100644 index 0000000..939c5c4 --- /dev/null +++ b/main/resources/mappers/SystemManagerMapper.xml @@ -0,0 +1,74 @@ + + + + + + + + + + sm_id, sm_account, sm_password + + + + + + + delete from system_manager + where sm_id = #{smId,jdbcType=INTEGER} + + + + SELECT LAST_INSERT_ID() + + insert into system_manager (sm_account, sm_password) + values (#{smAccount,jdbcType=VARCHAR}, #{smPassword,jdbcType=VARCHAR}) + + + + SELECT LAST_INSERT_ID() + + insert into system_manager + + + sm_account, + + + sm_password, + + + + + #{smAccount,jdbcType=VARCHAR}, + + + #{smPassword,jdbcType=VARCHAR}, + + + + + update system_manager + + + sm_account = #{smAccount,jdbcType=VARCHAR}, + + + sm_password = #{smPassword,jdbcType=VARCHAR}, + + + where sm_id = #{smId,jdbcType=INTEGER} + + + update system_manager + set sm_account = #{smAccount,jdbcType=VARCHAR}, + sm_password = #{smPassword,jdbcType=VARCHAR} + where sm_id = #{smId,jdbcType=INTEGER} + + \ No newline at end of file diff --git a/main/resources/mappers/WorkingYearsBonusMapper.xml b/main/resources/mappers/WorkingYearsBonusMapper.xml new file mode 100644 index 0000000..58c4790 --- /dev/null +++ b/main/resources/mappers/WorkingYearsBonusMapper.xml @@ -0,0 +1,108 @@ + + + + + + + + + + wyb_id, wyb_year, wyb_bonus + + + + + + + + + + + + + + delete from working_years_bonus + where wyb_id = #{wybId,jdbcType=INTEGER} + + + + + + SELECT LAST_INSERT_ID() + + insert into working_years_bonus (wyb_year, wyb_bonus) + values (#{wybYear,jdbcType=INTEGER}, #{wybBonus,jdbcType=DOUBLE}) + + + + + + SELECT LAST_INSERT_ID() + + insert into working_years_bonus + + + wyb_year, + + + wyb_bonus, + + + + + #{wybYear,jdbcType=INTEGER}, + + + #{wybBonus,jdbcType=DOUBLE}, + + + + + + update working_years_bonus + + + wyb_year = #{wybYear,jdbcType=INTEGER}, + + + wyb_bonus = #{wybBonus,jdbcType=DOUBLE}, + + + where wyb_id = #{wybId,jdbcType=INTEGER} + + + + update working_years_bonus + set wyb_year = #{wybYear,jdbcType=INTEGER}, + wyb_bonus = #{wybBonus,jdbcType=DOUBLE} + where wyb_id = #{wybId,jdbcType=INTEGER} + + + + + \ No newline at end of file diff --git a/main/resources/mybatis-config.xml b/main/resources/mybatis-config.xml new file mode 100644 index 0000000..075a86c --- /dev/null +++ b/main/resources/mybatis-config.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/main/resources/springmvc-servlet.xml b/main/resources/springmvc-servlet.xml new file mode 100644 index 0000000..6e49739 --- /dev/null +++ b/main/resources/springmvc-servlet.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/main/webapp/META-INF/MANIFEST.MF b/main/webapp/META-INF/MANIFEST.MF new file mode 100644 index 0000000..254272e --- /dev/null +++ b/main/webapp/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Class-Path: + diff --git a/main/webapp/WEB-INF/page/admin/adminLogin.html b/main/webapp/WEB-INF/page/admin/adminLogin.html new file mode 100644 index 0000000..a28554f --- /dev/null +++ b/main/webapp/WEB-INF/page/admin/adminLogin.html @@ -0,0 +1,107 @@ + + + + + + ESMS | 人力资源管理系统 + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/main/webapp/WEB-INF/page/admin/department-add.html b/main/webapp/WEB-INF/page/admin/department-add.html new file mode 100644 index 0000000..559010c --- /dev/null +++ b/main/webapp/WEB-INF/page/admin/department-add.html @@ -0,0 +1,133 @@ + + + + + + ESMS + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+ +
+
+
+
+ + + + + \ No newline at end of file diff --git a/main/webapp/WEB-INF/page/admin/department-edit.html b/main/webapp/WEB-INF/page/admin/department-edit.html new file mode 100644 index 0000000..1d77acb --- /dev/null +++ b/main/webapp/WEB-INF/page/admin/department-edit.html @@ -0,0 +1,166 @@ + + + + + + ESMS + + + + + + + + + + + + + + + + + + + +
+
+ + +
+ +
+ +
+
+
+ +
+ +
+
+
+
+ +
+
+
+
+ + + + + + \ No newline at end of file diff --git a/main/webapp/WEB-INF/page/admin/department-list.html b/main/webapp/WEB-INF/page/admin/department-list.html new file mode 100644 index 0000000..8a71c84 --- /dev/null +++ b/main/webapp/WEB-INF/page/admin/department-list.html @@ -0,0 +1,151 @@ + + + + + + ESMS + + + + + + + + + + + + + + + + + + + + +
+
+
+ 部门名称: +
+ +
+ +
+ + + +
+
+ +
+
+
+ + + +
+ + + + + + + + +
部门名称部门备注
+
+ +
+ + + + + + \ No newline at end of file diff --git a/main/webapp/WEB-INF/page/admin/employee-add.html b/main/webapp/WEB-INF/page/admin/employee-add.html new file mode 100644 index 0000000..4850663 --- /dev/null +++ b/main/webapp/WEB-INF/page/admin/employee-add.html @@ -0,0 +1,465 @@ + + + + +ESMS + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+
+ +
+
+
+ +
+
  + +
 
+ +
+
+ +
+
+
+
+ +
+
+
+
+ +
+
+
 
+ + +
+
+ +
+
+
+ + +
+
+ +
+
+
+
+
+ +
+
+
 
+
+ +
+ +
+
+
+ +
+
+
+
+ +
+
  +
+ +
+
 
+
+
+   联系方式: +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+ +
+ +
+ +
+
+ +
+
+ + +
+ +
+ +
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/main/webapp/WEB-INF/page/admin/employee-edit.html b/main/webapp/WEB-INF/page/admin/employee-edit.html new file mode 100644 index 0000000..e82b9c0 --- /dev/null +++ b/main/webapp/WEB-INF/page/admin/employee-edit.html @@ -0,0 +1,526 @@ + + + + + ESMS + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+ + +
+ +
+
+ + +
+ +
+
  + + + +
 
+ + + +
+
+ + +
+
+
+ + +
+ +
+ +
+ + +
+
+ +
+
+
 
+ + + + +
+
+ +
+
+
+ + + + +
+
+ +
+
+
+ + +
+
+ +
+
+
 
+ + +
+
+ +
+
+
+ + +
+ +
+
+ + + +   +
+ +
+ +
 
+
+
+   联系方式: +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+ +
+ +
+ +
+
+ +
+
+ + +
+ +
+ +
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/main/webapp/WEB-INF/page/admin/employee-list.html b/main/webapp/WEB-INF/page/admin/employee-list.html new file mode 100644 index 0000000..5c9f8d8 --- /dev/null +++ b/main/webapp/WEB-INF/page/admin/employee-list.html @@ -0,0 +1,344 @@ + + + + + + ESMS + + + + + + + + + + + + + + + +
+
+
+ + 员工工号: +
+ +
+     + 员工姓名: +
+ +
+     + 部门: + + + + + + + + +
+ +
+ +     + + +
+
+
+ +
+
+
+
+ +
+
+
+ +
+ + + + + + + + +
+ + + + + + + + + + + + + + + + + +
+ 工号 + + 姓名 + 性别 + + 学历 + + 电话 + + 职称 + + 紧急联系人 + + 岗位 + + 部门 + + 基本工资 +
+
+ + + + + + + + \ No newline at end of file diff --git a/main/webapp/WEB-INF/page/admin/importData/download.html b/main/webapp/WEB-INF/page/admin/importData/download.html new file mode 100644 index 0000000..2c6561d --- /dev/null +++ b/main/webapp/WEB-INF/page/admin/importData/download.html @@ -0,0 +1,18 @@ + + + + + 下载excel + + + + + + + \ No newline at end of file diff --git a/main/webapp/WEB-INF/page/admin/importData/importAttendance.html b/main/webapp/WEB-INF/page/admin/importData/importAttendance.html new file mode 100644 index 0000000..b0c3293 --- /dev/null +++ b/main/webapp/WEB-INF/page/admin/importData/importAttendance.html @@ -0,0 +1,81 @@ + + + + + ESMS + + + + + + + + + + + + +
+ +
+ + + +
+ 点击选择月考勤文件: + + +
+
+ + + \ No newline at end of file diff --git a/main/webapp/WEB-INF/page/admin/importData/importReissue.html b/main/webapp/WEB-INF/page/admin/importData/importReissue.html new file mode 100644 index 0000000..d5cb70d --- /dev/null +++ b/main/webapp/WEB-INF/page/admin/importData/importReissue.html @@ -0,0 +1,83 @@ + + + + + ESMS + + + + + + + + + + + + +
+ +
+ + +
+ 点击选择其他工资项文件: + + +
+ +
+ + + \ No newline at end of file diff --git a/main/webapp/WEB-INF/page/admin/index.html b/main/webapp/WEB-INF/page/admin/index.html new file mode 100644 index 0000000..935b2bb --- /dev/null +++ b/main/webapp/WEB-INF/page/admin/index.html @@ -0,0 +1,231 @@ + + + + + ESMS | 人力资源管理系统 + + + + + + + + + + + + + + + +
+ +
+ +
+ + +
+ + + + + + + +
+
+
    +
  • 首页
  • +
+
+
+ +
+
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/main/webapp/WEB-INF/page/admin/infor/changePassword.html b/main/webapp/WEB-INF/page/admin/infor/changePassword.html new file mode 100644 index 0000000..dc06f7b --- /dev/null +++ b/main/webapp/WEB-INF/page/admin/infor/changePassword.html @@ -0,0 +1,123 @@ + + + + + + ESMS + + + + + + + + + + + + + + + + + +
+
+
+ +
+ +
+
+
+
+
+ +
+ +
+
+ 至少8位 +
+
+
+ +
+ +
+
+ 至少8位 +
+
+
+ + +
+
+
+ + + + + \ No newline at end of file diff --git a/main/webapp/WEB-INF/page/admin/monthlyattendance-edit.html b/main/webapp/WEB-INF/page/admin/monthlyattendance-edit.html new file mode 100644 index 0000000..375c2b0 --- /dev/null +++ b/main/webapp/WEB-INF/page/admin/monthlyattendance-edit.html @@ -0,0 +1,237 @@ + + + + + + ESMS + + + + + + + + + + + + + +
+
+
+ +
+ 时间 +
+
+ + +
+ +
+
+
+ 员工信息 +
+
+ +
+ +
+ +
+ +
+
+ +
+ 请假天数 +
+
+ +
+ +
+ +
+ +
+
+ +
+ 加班小时 +
+
+ +
+ +
+ +
+ +
+ +
+ +
+
+ +
+ 考勤情况 +
+
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+
+ +
+
+ +
+
+
+ +
+ + + + + + + + \ No newline at end of file diff --git a/main/webapp/WEB-INF/page/admin/monthlyattendance-list.html b/main/webapp/WEB-INF/page/admin/monthlyattendance-list.html new file mode 100644 index 0000000..257ea82 --- /dev/null +++ b/main/webapp/WEB-INF/page/admin/monthlyattendance-list.html @@ -0,0 +1,321 @@ + + + + + + ESMS + + + + + + + + + + + + + + + +
+
+
+ 员工工号: +
+ +
+     + 部门: +
+ + + + + + +
+
+ +
+
+
+     + 考勤月份: +
+ +
+     + +
+
+
+ +
+
+
+ +
+ + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
月份 + 姓名 + 员工信息请假天数加班小时考勤情况
工号 + 部门 + + 病假 + + 事假 + + 平时加班 + + 周末加班 + + 节假日加班 + + 迟到次数 + + 早退次数 + + 缺勤天数 + + 出差天数 +
+
+ + + + + + + + \ No newline at end of file diff --git a/main/webapp/WEB-INF/page/admin/position-add.html b/main/webapp/WEB-INF/page/admin/position-add.html new file mode 100644 index 0000000..0558911 --- /dev/null +++ b/main/webapp/WEB-INF/page/admin/position-add.html @@ -0,0 +1,158 @@ + + + + + + ESMS + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+ +
+
+
+
+ + + + + \ No newline at end of file diff --git a/main/webapp/WEB-INF/page/admin/position-edit.html b/main/webapp/WEB-INF/page/admin/position-edit.html new file mode 100644 index 0000000..56d1c16 --- /dev/null +++ b/main/webapp/WEB-INF/page/admin/position-edit.html @@ -0,0 +1,191 @@ + + + + + + ESMS + + + + + + + + + + + + + + + + + + + +
+
+ + +
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+ +
+
+
+
+ + + + + \ No newline at end of file diff --git a/main/webapp/WEB-INF/page/admin/position-list.html b/main/webapp/WEB-INF/page/admin/position-list.html new file mode 100644 index 0000000..6431db6 --- /dev/null +++ b/main/webapp/WEB-INF/page/admin/position-list.html @@ -0,0 +1,150 @@ + + + + + + ESMS + + + + + + + + + + + + + + + +
+
+
+ 岗位名称: +
+ +
+ +
+ + + +
+
+ +
+
+
+ + +
+ + + + + + + + + +
岗位名称岗位描述岗位补贴
+
+ +
+ + + + + + \ No newline at end of file diff --git a/main/webapp/WEB-INF/page/admin/rank-add.html b/main/webapp/WEB-INF/page/admin/rank-add.html new file mode 100644 index 0000000..1f0eb66 --- /dev/null +++ b/main/webapp/WEB-INF/page/admin/rank-add.html @@ -0,0 +1,136 @@ + + + + + + ESMS + + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ + +
+
+
+ + + + + \ No newline at end of file diff --git a/main/webapp/WEB-INF/page/admin/rank-edit.html b/main/webapp/WEB-INF/page/admin/rank-edit.html new file mode 100644 index 0000000..8cece4e --- /dev/null +++ b/main/webapp/WEB-INF/page/admin/rank-edit.html @@ -0,0 +1,172 @@ + + + + + + ESMS + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+ + +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ + +
+
+
+ + + + + \ No newline at end of file diff --git a/main/webapp/WEB-INF/page/admin/rank-list.jsp b/main/webapp/WEB-INF/page/admin/rank-list.jsp new file mode 100644 index 0000000..030d188 --- /dev/null +++ b/main/webapp/WEB-INF/page/admin/rank-list.jsp @@ -0,0 +1,250 @@ +<%@ page language="java" import="java.util.*" pageEncoding="utf-8" %> +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> + + + + + + ESMS + + + + + + + + + + + + + + + + + + + + +
+
+
+ + + +
+
+ + + + 共有数据:${pageInfo.total} 条 + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
#职称名称补贴操作
+
+
${rb.rbId}${rb.rankName}${rb.rbBonus} + + + + + + + + +
+ +
+ <%--分页文字信息--%> +
+ 当前为第${pageInfo.pageNum}页,总共有${pageInfo.pages}页. +
+ <%-- 分页条--%> +
+ +
+ +
+ <%--
+
+ + 1 + 2 + 3 + 489 + +
+
--%> + +
+ ${test} + + + + + \ No newline at end of file diff --git a/main/webapp/WEB-INF/page/admin/rankBonusEdit.html b/main/webapp/WEB-INF/page/admin/rankBonusEdit.html new file mode 100644 index 0000000..94b3880 --- /dev/null +++ b/main/webapp/WEB-INF/page/admin/rankBonusEdit.html @@ -0,0 +1,167 @@ + + + + + + ESMS + + + + + + + + + + + + + + +
+
+ + +
+ +
+ +
+
+
+ +
+ +
+
+
+
+ +
+
+
+
+ + + + + + \ No newline at end of file diff --git a/main/webapp/WEB-INF/page/admin/rankBonusList.html b/main/webapp/WEB-INF/page/admin/rankBonusList.html new file mode 100644 index 0000000..d406faf --- /dev/null +++ b/main/webapp/WEB-INF/page/admin/rankBonusList.html @@ -0,0 +1,135 @@ + + + + + + ESMS + + + + + + + + + + + + + + +
+
+
+ 职称: +
+ +
+ +
+
+
+ +
+
+
+ +
+ + + + + + + + +
职称奖金操作
+
+ +
+ + + + + + \ No newline at end of file diff --git a/main/webapp/WEB-INF/page/admin/rankBonusadd.html b/main/webapp/WEB-INF/page/admin/rankBonusadd.html new file mode 100644 index 0000000..487baa0 --- /dev/null +++ b/main/webapp/WEB-INF/page/admin/rankBonusadd.html @@ -0,0 +1,140 @@ + + + + + + ESMS + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+ +
+
+
+
+ + + + + \ No newline at end of file diff --git a/main/webapp/WEB-INF/page/admin/salary/salary-list.html b/main/webapp/WEB-INF/page/admin/salary/salary-list.html new file mode 100644 index 0000000..1858ecd --- /dev/null +++ b/main/webapp/WEB-INF/page/admin/salary/salary-list.html @@ -0,0 +1,230 @@ + + + + + + ESMS + + + + + + + + + + + + + + + +
+
+ 员工工号: +
+ +
    + 部门: +
+ +
+ +
+
     + 月份: +
+ +
    + + +
+ +
+ + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
月份姓名 + 员工信息应发工资基本工资其他工资补贴奖金五险一金工资扣额实发工资发放
工号 + 部门 + 餐饮岗位交通出差工龄职称加班全勤养老医疗失业工伤生育住房公积金迟到早退病假事假个人所得税
+
+ + + + + + + + + \ No newline at end of file diff --git a/main/webapp/WEB-INF/page/admin/salary/salaryIssueList.html b/main/webapp/WEB-INF/page/admin/salary/salaryIssueList.html new file mode 100644 index 0000000..6be6537 --- /dev/null +++ b/main/webapp/WEB-INF/page/admin/salary/salaryIssueList.html @@ -0,0 +1,365 @@ + + + + + + ESMS + + + + + + + + + + + + + + + +
+
+
+ 员工工号: +
+ +
+     + 部门: +
+ +
+ +
+
+     + 月份: +
+ +
+      + +
+
+
+ +
+      +
+ +
+
+
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
月份姓名 + 员工信息实发工资应发工资基本工资补贴奖金五险一金工资扣额 + 其他工资 +
工号 + 部门 + 餐饮岗位交通出差工龄职称加班全勤养老医疗失业工伤生育住房公积金迟到早退病假事假个人所得税
+
+ + + + + + + + + \ No newline at end of file diff --git a/main/webapp/WEB-INF/page/admin/salary/salarySettlement.html b/main/webapp/WEB-INF/page/admin/salary/salarySettlement.html new file mode 100644 index 0000000..5ef0d7d --- /dev/null +++ b/main/webapp/WEB-INF/page/admin/salary/salarySettlement.html @@ -0,0 +1,147 @@ + + + + + + ESMS + + + + + + + + + + + + + +
+ 按月份结算所有员工工资:
+
+
+
+ +
+ +
+ +
+ +
+
+
+
+
+
+
+ 结算指定员工工资:

+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/main/webapp/WEB-INF/page/admin/wageManagement.html b/main/webapp/WEB-INF/page/admin/wageManagement.html new file mode 100644 index 0000000..9ae8ac5 --- /dev/null +++ b/main/webapp/WEB-INF/page/admin/wageManagement.html @@ -0,0 +1,340 @@ + + + + + + ESMS + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ 迟到罚金: +
+ +
+ 元/次 +
+
+
+ 早退罚金: +
+ +
+ 元/次 +
+
+
+ 出差补贴: +
+ +
+ 元/天 +
+
+
+ 全勤奖金: +
+ +
+ 元/月 +
+
+
+ 餐饮补贴: +
+ +
+ 元/月 +
+
+
+ 交通补贴: +
+ +
+ 元/月 +
+
+
+
+ +
+
+ + + + + +
+
+
+ + + + + + \ No newline at end of file diff --git a/main/webapp/WEB-INF/page/admin/welcome.html b/main/webapp/WEB-INF/page/admin/welcome.html new file mode 100644 index 0000000..91144b0 --- /dev/null +++ b/main/webapp/WEB-INF/page/admin/welcome.html @@ -0,0 +1,30 @@ + + + + + + ESMS + + + + + + + + +
+ +
欢迎!管理员 登录《人员管理系统》 + 当前时间: +
+
+ + + \ No newline at end of file diff --git a/main/webapp/WEB-INF/page/admin/workingYearsBonusAdd.html b/main/webapp/WEB-INF/page/admin/workingYearsBonusAdd.html new file mode 100644 index 0000000..c1d794b --- /dev/null +++ b/main/webapp/WEB-INF/page/admin/workingYearsBonusAdd.html @@ -0,0 +1,142 @@ + + + + + + ESMS + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+ +
+
+
+
+ + + + + \ No newline at end of file diff --git a/main/webapp/WEB-INF/page/admin/workingYearsBonusEdit.html b/main/webapp/WEB-INF/page/admin/workingYearsBonusEdit.html new file mode 100644 index 0000000..603dcf7 --- /dev/null +++ b/main/webapp/WEB-INF/page/admin/workingYearsBonusEdit.html @@ -0,0 +1,175 @@ + + + + + + ESMS + + + + + + + + + + + + + + + + + + + +
+
+ + +
+ +
+ +
+
+
+ +
+ +
+
+
+
+ +
+
+
+
+ + + + + + \ No newline at end of file diff --git a/main/webapp/WEB-INF/page/admin/workingYearsBonusList.html b/main/webapp/WEB-INF/page/admin/workingYearsBonusList.html new file mode 100644 index 0000000..97ce5ed --- /dev/null +++ b/main/webapp/WEB-INF/page/admin/workingYearsBonusList.html @@ -0,0 +1,139 @@ + + + + + + ESMS + + + + + + + + + + + + + + + + + + + + +
+
+
+ 工龄: +
+ +
+ +
+ + + +
+
+ +
+
+
+ + +
+ + + + + + + + +
工龄补贴
+
+ +
+ + + + + + \ No newline at end of file diff --git a/main/webapp/WEB-INF/page/employee/attendanceList.html b/main/webapp/WEB-INF/page/employee/attendanceList.html new file mode 100644 index 0000000..e87918a --- /dev/null +++ b/main/webapp/WEB-INF/page/employee/attendanceList.html @@ -0,0 +1,130 @@ + + + + + + ESMS + + + + + + + + + + + + + + + +
+
+ 年度查询: +
+ +
+ +
+
+ + +
+ + + + + + + + + + + + + + + + + + + + + + +
月份 + 请假天数加班小时考勤情况
+ 病假 + + 事假 + + 平时加班 + + 周末加班 + + 节假日加班 + + 迟到次数 + + 早退次数 + + 缺勤天数 + + 出差天数 +
+
+ + + + + + \ No newline at end of file diff --git a/main/webapp/WEB-INF/page/employee/changePassword.html b/main/webapp/WEB-INF/page/employee/changePassword.html new file mode 100644 index 0000000..ad7bacf --- /dev/null +++ b/main/webapp/WEB-INF/page/employee/changePassword.html @@ -0,0 +1,123 @@ + + + + + + ESMS + + + + + + + + + + + + + + +
+
+
+ +
+ +
+
+
+
+
+ +
+ +
+
+ 至少8位 +
+
+
+ +
+ +
+
+ 至少8位 +
+
+
+ + +
+
+
+ + + + + \ No newline at end of file diff --git a/main/webapp/WEB-INF/page/employee/employeeSalaryList.html b/main/webapp/WEB-INF/page/employee/employeeSalaryList.html new file mode 100644 index 0000000..43dff1e --- /dev/null +++ b/main/webapp/WEB-INF/page/employee/employeeSalaryList.html @@ -0,0 +1,182 @@ + + + + + + ESMS + + + + + + + + + + + + + + +
+
+ 年度查询: +
+ +
+ +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
月份应发工资基本工资其他工资补贴奖金五险一金工资扣额实发工资发放
餐饮岗位交通出差工龄职称加班全勤养老医疗失业工伤生育住房公积金迟到早退病假事假个人所得税
+
+ + +
+ +
+ + + \ No newline at end of file diff --git a/main/webapp/WEB-INF/page/employee/index.html b/main/webapp/WEB-INF/page/employee/index.html new file mode 100644 index 0000000..a814916 --- /dev/null +++ b/main/webapp/WEB-INF/page/employee/index.html @@ -0,0 +1,125 @@ + + + + + ESMS | 人力资源管理系统 + + + + + + + + + + + + + + + + +
+ +
+ +
+ + +
+ + + + + + + +
+
+
    +
  • 首页
  • +
+
+
+ +
+
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/main/webapp/WEB-INF/page/employee/inforEmployee.html b/main/webapp/WEB-INF/page/employee/inforEmployee.html new file mode 100644 index 0000000..a6c954e --- /dev/null +++ b/main/webapp/WEB-INF/page/employee/inforEmployee.html @@ -0,0 +1,321 @@ + + + + + ESMS + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+ + +
+ +
+
+ + +
+ +
+
  + +
 
+ + +
+ +
+
+ + +
+ +
+
+ + +
+ +
+
 
+ + +
+ +
+
+ + +
+ +
+
+ + +
+ +
+
 
+ + +
+ +
+
+ + +
+ +
+
+ + + +
 
+
+
+  联系方式: +
+
+ + +
+ +
+ +
+ +
+
+ +
+ +
+ +
+ +
+ +
+
+ +
+
+ + +
+ +
+ +
+
+
+ + + + + + + + \ No newline at end of file diff --git a/main/webapp/WEB-INF/page/employee/login.html b/main/webapp/WEB-INF/page/employee/login.html new file mode 100644 index 0000000..4923579 --- /dev/null +++ b/main/webapp/WEB-INF/page/employee/login.html @@ -0,0 +1,106 @@ + + + + + + ESMS | 人力资源管理系统 + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/main/webapp/WEB-INF/page/employee/welcome.html b/main/webapp/WEB-INF/page/employee/welcome.html new file mode 100644 index 0000000..56fabb7 --- /dev/null +++ b/main/webapp/WEB-INF/page/employee/welcome.html @@ -0,0 +1,30 @@ + + + + + + ESMS + + + + + + + + +
+ +
欢迎!员工 登录《人员管理系统》 + 当前时间: +
+
+ + + + \ No newline at end of file diff --git a/main/webapp/WEB-INF/page/error/error.jsp b/main/webapp/WEB-INF/page/error/error.jsp new file mode 100644 index 0000000..9c1b1b2 --- /dev/null +++ b/main/webapp/WEB-INF/page/error/error.jsp @@ -0,0 +1,17 @@ +<%-- + Created by IntelliJ IDEA. + User: wjb + Date: 2020/02/4 + Time: 2:31 + To change this template use File | Settings | File Templates. +--%> +<%@ page contentType="text/html;charset=UTF-8" language="java" %> +<%@taglib prefix="c" uri="http://java.sun.com/jstl/core"%> + + + 错误信息 + + +${requestScope.message} + + diff --git a/main/webapp/WEB-INF/web.xml b/main/webapp/WEB-INF/web.xml new file mode 100644 index 0000000..43e180e --- /dev/null +++ b/main/webapp/WEB-INF/web.xml @@ -0,0 +1,79 @@ + + + + log4jConfigLocation + classpath:log4j.properties + + + + + org.springframework.web.util.Log4jConfigListener + + + + + + contextConfigLocation + classpath:applicationContext.xml + + + + org.springframework.web.context.ContextLoaderListener + + + + dispatcherServlet + org.springframework.web.servlet.DispatcherServlet + + contextConfigLocation + classpath:springmvc-servlet.xml + + + + + dispatcherServlet + / + + + CharacterEncodingFilter + org.springframework.web.filter.CharacterEncodingFilter + + encoding + utf-8 + + + forceRequestEncoding + true + + + forceResponseEncoding + true + + + + CharacterEncodingFilter + /* + + + HiddenHttpMethodFilter + org.springframework.web.filter.HiddenHttpMethodFilter + + + HiddenHttpMethodFilter + /* + + + HttpPutFormContentFilter + org.springframework.web.filter.HttpPutFormContentFilter + + + HttpPutFormContentFilter + /* + + + + + 30 + + + \ No newline at end of file diff --git a/main/webapp/img/aiwrap.png b/main/webapp/img/aiwrap.png new file mode 100644 index 0000000..36fd481 Binary files /dev/null and b/main/webapp/img/aiwrap.png differ diff --git a/main/webapp/img/bg.png b/main/webapp/img/bg.png new file mode 100644 index 0000000..c518e9d Binary files /dev/null and b/main/webapp/img/bg.png differ diff --git a/main/webapp/img/head/2018-08-13112.36718408230372.PNG b/main/webapp/img/head/2018-08-13112.36718408230372.PNG new file mode 100644 index 0000000..6adcdba Binary files /dev/null and b/main/webapp/img/head/2018-08-13112.36718408230372.PNG differ diff --git a/main/webapp/img/head/2018-08-1324.13466224751659.png b/main/webapp/img/head/2018-08-1324.13466224751659.png new file mode 100644 index 0000000..9a86d12 Binary files /dev/null and b/main/webapp/img/head/2018-08-1324.13466224751659.png differ diff --git a/main/webapp/img/head/2018-08-13241.89169805219058.jpg b/main/webapp/img/head/2018-08-13241.89169805219058.jpg new file mode 100644 index 0000000..a0862be Binary files /dev/null and b/main/webapp/img/head/2018-08-13241.89169805219058.jpg differ diff --git a/main/webapp/img/head/2018-08-13244.7691710588793.PNG b/main/webapp/img/head/2018-08-13244.7691710588793.PNG new file mode 100644 index 0000000..cb52953 Binary files /dev/null and b/main/webapp/img/head/2018-08-13244.7691710588793.PNG differ diff --git a/main/webapp/img/head/2018-08-13248.65940464556468.jpg b/main/webapp/img/head/2018-08-13248.65940464556468.jpg new file mode 100644 index 0000000..a9b136d Binary files /dev/null and b/main/webapp/img/head/2018-08-13248.65940464556468.jpg differ diff --git a/main/webapp/img/head/2018-08-1325.681911954282487.jpg b/main/webapp/img/head/2018-08-1325.681911954282487.jpg new file mode 100644 index 0000000..f04aa87 Binary files /dev/null and b/main/webapp/img/head/2018-08-1325.681911954282487.jpg differ diff --git a/main/webapp/img/head/2018-08-13259.4837096893765.jpeg b/main/webapp/img/head/2018-08-13259.4837096893765.jpeg new file mode 100644 index 0000000..b3569cb Binary files /dev/null and b/main/webapp/img/head/2018-08-13259.4837096893765.jpeg differ diff --git a/main/webapp/img/head/2018-08-13285.30487470714127.jpg b/main/webapp/img/head/2018-08-13285.30487470714127.jpg new file mode 100644 index 0000000..0806715 Binary files /dev/null and b/main/webapp/img/head/2018-08-13285.30487470714127.jpg differ diff --git a/main/webapp/img/head/2018-08-13321.15582664686036.jpg b/main/webapp/img/head/2018-08-13321.15582664686036.jpg new file mode 100644 index 0000000..499c816 Binary files /dev/null and b/main/webapp/img/head/2018-08-13321.15582664686036.jpg differ diff --git a/main/webapp/img/head/2018-08-13369.2875781231495.jpg b/main/webapp/img/head/2018-08-13369.2875781231495.jpg new file mode 100644 index 0000000..c7fcdf1 Binary files /dev/null and b/main/webapp/img/head/2018-08-13369.2875781231495.jpg differ diff --git a/main/webapp/img/head/2018-08-13374.70783358169035.jpg b/main/webapp/img/head/2018-08-13374.70783358169035.jpg new file mode 100644 index 0000000..442db18 Binary files /dev/null and b/main/webapp/img/head/2018-08-13374.70783358169035.jpg differ diff --git a/main/webapp/img/head/2018-08-13385.0469940398732.jpg b/main/webapp/img/head/2018-08-13385.0469940398732.jpg new file mode 100644 index 0000000..ef5fde9 Binary files /dev/null and b/main/webapp/img/head/2018-08-13385.0469940398732.jpg differ diff --git a/main/webapp/img/head/2018-08-13386.5402238473168.PNG b/main/webapp/img/head/2018-08-13386.5402238473168.PNG new file mode 100644 index 0000000..fde16d5 Binary files /dev/null and b/main/webapp/img/head/2018-08-13386.5402238473168.PNG differ diff --git a/main/webapp/img/head/2018-08-13435.8427919928054.PNG b/main/webapp/img/head/2018-08-13435.8427919928054.PNG new file mode 100644 index 0000000..cc7bd77 Binary files /dev/null and b/main/webapp/img/head/2018-08-13435.8427919928054.PNG differ diff --git a/main/webapp/img/head/2018-08-13442.7875916394904.jpg b/main/webapp/img/head/2018-08-13442.7875916394904.jpg new file mode 100644 index 0000000..fb58756 Binary files /dev/null and b/main/webapp/img/head/2018-08-13442.7875916394904.jpg differ diff --git a/main/webapp/img/head/2018-08-13448.30988559556863.jpg b/main/webapp/img/head/2018-08-13448.30988559556863.jpg new file mode 100644 index 0000000..b89e584 Binary files /dev/null and b/main/webapp/img/head/2018-08-13448.30988559556863.jpg differ diff --git a/main/webapp/img/head/2018-08-13469.806953403241.jpg b/main/webapp/img/head/2018-08-13469.806953403241.jpg new file mode 100644 index 0000000..8f6b0a0 Binary files /dev/null and b/main/webapp/img/head/2018-08-13469.806953403241.jpg differ diff --git a/main/webapp/img/head/2018-08-13517.0042615005489.jpg b/main/webapp/img/head/2018-08-13517.0042615005489.jpg new file mode 100644 index 0000000..f1e9735 Binary files /dev/null and b/main/webapp/img/head/2018-08-13517.0042615005489.jpg differ diff --git a/main/webapp/img/head/2018-08-1353.41618548555738.jpg b/main/webapp/img/head/2018-08-1353.41618548555738.jpg new file mode 100644 index 0000000..1cc28c7 Binary files /dev/null and b/main/webapp/img/head/2018-08-1353.41618548555738.jpg differ diff --git a/main/webapp/img/head/2018-08-1353.49652735465327.jpg b/main/webapp/img/head/2018-08-1353.49652735465327.jpg new file mode 100644 index 0000000..bfe29fe Binary files /dev/null and b/main/webapp/img/head/2018-08-1353.49652735465327.jpg differ diff --git a/main/webapp/img/head/2018-08-1356.60479626373882.jpg b/main/webapp/img/head/2018-08-1356.60479626373882.jpg new file mode 100644 index 0000000..36c7e42 Binary files /dev/null and b/main/webapp/img/head/2018-08-1356.60479626373882.jpg differ diff --git a/main/webapp/img/head/2018-08-13561.4357317981364.jpg b/main/webapp/img/head/2018-08-13561.4357317981364.jpg new file mode 100644 index 0000000..714c605 Binary files /dev/null and b/main/webapp/img/head/2018-08-13561.4357317981364.jpg differ diff --git a/main/webapp/img/head/2018-08-13564.7927499681208.jpg b/main/webapp/img/head/2018-08-13564.7927499681208.jpg new file mode 100644 index 0000000..89b1900 Binary files /dev/null and b/main/webapp/img/head/2018-08-13564.7927499681208.jpg differ diff --git a/main/webapp/img/head/2018-08-13578.5935285696738.jpg b/main/webapp/img/head/2018-08-13578.5935285696738.jpg new file mode 100644 index 0000000..eab65b5 Binary files /dev/null and b/main/webapp/img/head/2018-08-13578.5935285696738.jpg differ diff --git a/main/webapp/img/head/2018-08-13581.7745264147674.jpg b/main/webapp/img/head/2018-08-13581.7745264147674.jpg new file mode 100644 index 0000000..42112ad Binary files /dev/null and b/main/webapp/img/head/2018-08-13581.7745264147674.jpg differ diff --git a/main/webapp/img/head/2018-08-13601.5432148751685.jpg b/main/webapp/img/head/2018-08-13601.5432148751685.jpg new file mode 100644 index 0000000..b18a8cd Binary files /dev/null and b/main/webapp/img/head/2018-08-13601.5432148751685.jpg differ diff --git a/main/webapp/img/head/2018-08-1361.16631540820339.jpg b/main/webapp/img/head/2018-08-1361.16631540820339.jpg new file mode 100644 index 0000000..442db18 Binary files /dev/null and b/main/webapp/img/head/2018-08-1361.16631540820339.jpg differ diff --git a/main/webapp/img/head/2018-08-13696.7509101680499.jpg b/main/webapp/img/head/2018-08-13696.7509101680499.jpg new file mode 100644 index 0000000..1cc28c7 Binary files /dev/null and b/main/webapp/img/head/2018-08-13696.7509101680499.jpg differ diff --git a/main/webapp/img/head/2018-08-13701.7836576620281.jpg b/main/webapp/img/head/2018-08-13701.7836576620281.jpg new file mode 100644 index 0000000..cbb950e Binary files /dev/null and b/main/webapp/img/head/2018-08-13701.7836576620281.jpg differ diff --git a/main/webapp/img/head/2018-08-13711.7142963226189.jpg b/main/webapp/img/head/2018-08-13711.7142963226189.jpg new file mode 100644 index 0000000..b789029 Binary files /dev/null and b/main/webapp/img/head/2018-08-13711.7142963226189.jpg differ diff --git a/main/webapp/img/head/2018-08-13722.5758658452411.jpg b/main/webapp/img/head/2018-08-13722.5758658452411.jpg new file mode 100644 index 0000000..9e0f55b Binary files /dev/null and b/main/webapp/img/head/2018-08-13722.5758658452411.jpg differ diff --git a/main/webapp/img/head/2018-08-13722.8659148692414.jpg b/main/webapp/img/head/2018-08-13722.8659148692414.jpg new file mode 100644 index 0000000..a9b136d Binary files /dev/null and b/main/webapp/img/head/2018-08-13722.8659148692414.jpg differ diff --git a/main/webapp/img/head/2018-08-13723.1731034732911.jpg b/main/webapp/img/head/2018-08-13723.1731034732911.jpg new file mode 100644 index 0000000..714c605 Binary files /dev/null and b/main/webapp/img/head/2018-08-13723.1731034732911.jpg differ diff --git a/main/webapp/img/head/2018-08-13724.6964111060594.jpg b/main/webapp/img/head/2018-08-13724.6964111060594.jpg new file mode 100644 index 0000000..af189c0 Binary files /dev/null and b/main/webapp/img/head/2018-08-13724.6964111060594.jpg differ diff --git a/main/webapp/img/head/2018-08-13735.3421510445431.jpg b/main/webapp/img/head/2018-08-13735.3421510445431.jpg new file mode 100644 index 0000000..eab65b5 Binary files /dev/null and b/main/webapp/img/head/2018-08-13735.3421510445431.jpg differ diff --git a/main/webapp/img/head/2018-08-13736.7609357343375.PNG b/main/webapp/img/head/2018-08-13736.7609357343375.PNG new file mode 100644 index 0000000..aec951b Binary files /dev/null and b/main/webapp/img/head/2018-08-13736.7609357343375.PNG differ diff --git a/main/webapp/img/head/2018-08-13737.783349514442.PNG b/main/webapp/img/head/2018-08-13737.783349514442.PNG new file mode 100644 index 0000000..0abd4d8 Binary files /dev/null and b/main/webapp/img/head/2018-08-13737.783349514442.PNG differ diff --git a/main/webapp/img/head/2018-08-13755.2230895952057.jpg b/main/webapp/img/head/2018-08-13755.2230895952057.jpg new file mode 100644 index 0000000..cd4c4a2 Binary files /dev/null and b/main/webapp/img/head/2018-08-13755.2230895952057.jpg differ diff --git a/main/webapp/img/head/2018-08-13766.7890696781377.jpg b/main/webapp/img/head/2018-08-13766.7890696781377.jpg new file mode 100644 index 0000000..714c605 Binary files /dev/null and b/main/webapp/img/head/2018-08-13766.7890696781377.jpg differ diff --git a/main/webapp/img/head/2018-08-13766.982056183347.jpeg b/main/webapp/img/head/2018-08-13766.982056183347.jpeg new file mode 100644 index 0000000..a8ec626 Binary files /dev/null and b/main/webapp/img/head/2018-08-13766.982056183347.jpeg differ diff --git a/main/webapp/img/head/2018-08-13784.3667171342782.jpg b/main/webapp/img/head/2018-08-13784.3667171342782.jpg new file mode 100644 index 0000000..60ed025 Binary files /dev/null and b/main/webapp/img/head/2018-08-13784.3667171342782.jpg differ diff --git a/main/webapp/img/head/2018-08-13800.0985428174017.jpg b/main/webapp/img/head/2018-08-13800.0985428174017.jpg new file mode 100644 index 0000000..c6337e9 Binary files /dev/null and b/main/webapp/img/head/2018-08-13800.0985428174017.jpg differ diff --git a/main/webapp/img/head/2018-08-13864.6213865616921.jpg b/main/webapp/img/head/2018-08-13864.6213865616921.jpg new file mode 100644 index 0000000..f4eba60 Binary files /dev/null and b/main/webapp/img/head/2018-08-13864.6213865616921.jpg differ diff --git a/main/webapp/img/head/2018-08-13867.5861345849022.jpg b/main/webapp/img/head/2018-08-13867.5861345849022.jpg new file mode 100644 index 0000000..6c1ca30 Binary files /dev/null and b/main/webapp/img/head/2018-08-13867.5861345849022.jpg differ diff --git a/main/webapp/img/head/2018-08-13903.8879192749371.PNG b/main/webapp/img/head/2018-08-13903.8879192749371.PNG new file mode 100644 index 0000000..204d242 Binary files /dev/null and b/main/webapp/img/head/2018-08-13903.8879192749371.PNG differ diff --git a/main/webapp/img/head/2018-08-13905.9935000567011.jpg b/main/webapp/img/head/2018-08-13905.9935000567011.jpg new file mode 100644 index 0000000..ceaacc1 Binary files /dev/null and b/main/webapp/img/head/2018-08-13905.9935000567011.jpg differ diff --git a/main/webapp/img/head/2018-08-13915.8155639988832.jpg b/main/webapp/img/head/2018-08-13915.8155639988832.jpg new file mode 100644 index 0000000..6c1ca30 Binary files /dev/null and b/main/webapp/img/head/2018-08-13915.8155639988832.jpg differ diff --git a/main/webapp/img/head/2018-08-13940.8291472604774.jpg b/main/webapp/img/head/2018-08-13940.8291472604774.jpg new file mode 100644 index 0000000..bf83ccd Binary files /dev/null and b/main/webapp/img/head/2018-08-13940.8291472604774.jpg differ diff --git a/main/webapp/img/head/2018-08-13943.4731208995166.PNG b/main/webapp/img/head/2018-08-13943.4731208995166.PNG new file mode 100644 index 0000000..67601bd Binary files /dev/null and b/main/webapp/img/head/2018-08-13943.4731208995166.PNG differ diff --git a/main/webapp/img/head/2018-08-13960.0548434935005.jpg b/main/webapp/img/head/2018-08-13960.0548434935005.jpg new file mode 100644 index 0000000..c6337e9 Binary files /dev/null and b/main/webapp/img/head/2018-08-13960.0548434935005.jpg differ diff --git a/main/webapp/img/head/2018-08-13967.5213973314058.jpg b/main/webapp/img/head/2018-08-13967.5213973314058.jpg new file mode 100644 index 0000000..6c6cffd Binary files /dev/null and b/main/webapp/img/head/2018-08-13967.5213973314058.jpg differ diff --git a/main/webapp/img/head/2018-08-13979.5486498219902.PNG b/main/webapp/img/head/2018-08-13979.5486498219902.PNG new file mode 100644 index 0000000..8170df1 Binary files /dev/null and b/main/webapp/img/head/2018-08-13979.5486498219902.PNG differ diff --git a/main/webapp/img/head/defalut.jpg b/main/webapp/img/head/defalut.jpg new file mode 100644 index 0000000..03489d0 Binary files /dev/null and b/main/webapp/img/head/defalut.jpg differ diff --git a/main/webapp/img/icon/列表.png b/main/webapp/img/icon/列表.png new file mode 100644 index 0000000..89aa82c Binary files /dev/null and b/main/webapp/img/icon/列表.png differ diff --git a/main/webapp/img/icon/员工.png b/main/webapp/img/icon/员工.png new file mode 100644 index 0000000..8a1df17 Binary files /dev/null and b/main/webapp/img/icon/员工.png differ diff --git a/main/webapp/img/icon/员工管理.png b/main/webapp/img/icon/员工管理.png new file mode 100644 index 0000000..c1d72df Binary files /dev/null and b/main/webapp/img/icon/员工管理.png differ diff --git a/main/webapp/img/icon/岗位.png b/main/webapp/img/icon/岗位.png new file mode 100644 index 0000000..234bc28 Binary files /dev/null and b/main/webapp/img/icon/岗位.png differ diff --git a/main/webapp/img/icon/工资 .png b/main/webapp/img/icon/工资 .png new file mode 100644 index 0000000..7e251bc Binary files /dev/null and b/main/webapp/img/icon/工资 .png differ diff --git a/main/webapp/img/icon/工资项管理.png b/main/webapp/img/icon/工资项管理.png new file mode 100644 index 0000000..665f815 Binary files /dev/null and b/main/webapp/img/icon/工资项管理.png differ diff --git a/main/webapp/img/icon/工龄奖金.png b/main/webapp/img/icon/工龄奖金.png new file mode 100644 index 0000000..65b3dda Binary files /dev/null and b/main/webapp/img/icon/工龄奖金.png differ diff --git a/main/webapp/img/icon/添加.png b/main/webapp/img/icon/添加.png new file mode 100644 index 0000000..5e698ff Binary files /dev/null and b/main/webapp/img/icon/添加.png differ diff --git a/main/webapp/img/icon/管理员.png b/main/webapp/img/icon/管理员.png new file mode 100644 index 0000000..6d167e8 Binary files /dev/null and b/main/webapp/img/icon/管理员.png differ diff --git a/main/webapp/img/icon/结算.png b/main/webapp/img/icon/结算.png new file mode 100644 index 0000000..f2b75f1 Binary files /dev/null and b/main/webapp/img/icon/结算.png differ diff --git a/main/webapp/img/icon/职称奖金.png b/main/webapp/img/icon/职称奖金.png new file mode 100644 index 0000000..e925a3d Binary files /dev/null and b/main/webapp/img/icon/职称奖金.png differ diff --git a/main/webapp/img/icon/部门.png b/main/webapp/img/icon/部门.png new file mode 100644 index 0000000..b5c1226 Binary files /dev/null and b/main/webapp/img/icon/部门.png differ diff --git a/main/webapp/img/icon/项目.png b/main/webapp/img/icon/项目.png new file mode 100644 index 0000000..9ac877b Binary files /dev/null and b/main/webapp/img/icon/项目.png differ diff --git a/main/webapp/img/icon/首页.png b/main/webapp/img/icon/首页.png new file mode 100644 index 0000000..a72f3ad Binary files /dev/null and b/main/webapp/img/icon/首页.png differ diff --git a/main/webapp/static/css/bootstrap-responsive.min.css b/main/webapp/static/css/bootstrap-responsive.min.css new file mode 100644 index 0000000..2269019 --- /dev/null +++ b/main/webapp/static/css/bootstrap-responsive.min.css @@ -0,0 +1,9 @@ +/*! + * Bootstrap Responsive v2.2.1 + * + * Copyright 2012 Twitter, Inc + * Licensed under the Apache License v2.0 + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Designed and built with all the love in the world @twitter by @mdo and @fat. + */.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;line-height:0;content:""}.clearfix:after{clear:both}.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.input-block-level{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.hidden{display:none;visibility:hidden}.visible-phone{display:none!important}.visible-tablet{display:none!important}.hidden-desktop{display:none!important}.visible-desktop{display:inherit!important}@media(min-width:768px) and (max-width:979px){.hidden-desktop{display:inherit!important}.visible-desktop{display:none!important}.visible-tablet{display:inherit!important}.hidden-tablet{display:none!important}}@media(max-width:767px){.hidden-desktop{display:inherit!important}.visible-desktop{display:none!important}.visible-phone{display:inherit!important}.hidden-phone{display:none!important}}@media(min-width:1200px){.row{margin-left:-30px;*zoom:1}.row:before,.row:after{display:table;line-height:0;content:""}.row:after{clear:both}[class*="span"]{float:left;min-height:1px;margin-left:30px}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:1170px}.span12{width:1170px}.span11{width:1070px}.span10{width:970px}.span9{width:870px}.span8{width:770px}.span7{width:670px}.span6{width:570px}.span5{width:470px}.span4{width:370px}.span3{width:270px}.span2{width:170px}.span1{width:70px}.offset12{margin-left:1230px}.offset11{margin-left:1130px}.offset10{margin-left:1030px}.offset9{margin-left:930px}.offset8{margin-left:830px}.offset7{margin-left:730px}.offset6{margin-left:630px}.offset5{margin-left:530px}.offset4{margin-left:430px}.offset3{margin-left:330px}.offset2{margin-left:230px}.offset1{margin-left:130px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;line-height:0;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:2.564102564102564%;*margin-left:2.5109110747408616%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .controls-row [class*="span"]+[class*="span"]{margin-left:2.564102564102564%}.row-fluid .span12{width:100%;*width:99.94680851063829%}.row-fluid .span11{width:91.45299145299145%;*width:91.39979996362975%}.row-fluid .span10{width:82.90598290598291%;*width:82.8527914166212%}.row-fluid .span9{width:74.35897435897436%;*width:74.30578286961266%}.row-fluid .span8{width:65.81196581196582%;*width:65.75877432260411%}.row-fluid .span7{width:57.26495726495726%;*width:57.21176577559556%}.row-fluid .span6{width:48.717948717948715%;*width:48.664757228587014%}.row-fluid .span5{width:40.17094017094017%;*width:40.11774868157847%}.row-fluid .span4{width:31.623931623931625%;*width:31.570740134569924%}.row-fluid .span3{width:23.076923076923077%;*width:23.023731587561375%}.row-fluid .span2{width:14.52991452991453%;*width:14.476723040552828%}.row-fluid .span1{width:5.982905982905983%;*width:5.929714493544281%}.row-fluid .offset12{margin-left:105.12820512820512%;*margin-left:105.02182214948171%}.row-fluid .offset12:first-child{margin-left:102.56410256410257%;*margin-left:102.45771958537915%}.row-fluid .offset11{margin-left:96.58119658119658%;*margin-left:96.47481360247316%}.row-fluid .offset11:first-child{margin-left:94.01709401709402%;*margin-left:93.91071103837061%}.row-fluid .offset10{margin-left:88.03418803418803%;*margin-left:87.92780505546462%}.row-fluid .offset10:first-child{margin-left:85.47008547008548%;*margin-left:85.36370249136206%}.row-fluid .offset9{margin-left:79.48717948717949%;*margin-left:79.38079650845607%}.row-fluid .offset9:first-child{margin-left:76.92307692307693%;*margin-left:76.81669394435352%}.row-fluid .offset8{margin-left:70.94017094017094%;*margin-left:70.83378796144753%}.row-fluid .offset8:first-child{margin-left:68.37606837606839%;*margin-left:68.26968539734497%}.row-fluid .offset7{margin-left:62.393162393162385%;*margin-left:62.28677941443899%}.row-fluid .offset7:first-child{margin-left:59.82905982905982%;*margin-left:59.72267685033642%}.row-fluid .offset6{margin-left:53.84615384615384%;*margin-left:53.739770867430444%}.row-fluid .offset6:first-child{margin-left:51.28205128205128%;*margin-left:51.175668303327875%}.row-fluid .offset5{margin-left:45.299145299145295%;*margin-left:45.1927623204219%}.row-fluid .offset5:first-child{margin-left:42.73504273504273%;*margin-left:42.62865975631933%}.row-fluid .offset4{margin-left:36.75213675213675%;*margin-left:36.645753773413354%}.row-fluid .offset4:first-child{margin-left:34.18803418803419%;*margin-left:34.081651209310785%}.row-fluid .offset3{margin-left:28.205128205128204%;*margin-left:28.0987452264048%}.row-fluid .offset3:first-child{margin-left:25.641025641025642%;*margin-left:25.53464266230224%}.row-fluid .offset2{margin-left:19.65811965811966%;*margin-left:19.551736679396257%}.row-fluid .offset2:first-child{margin-left:17.094017094017094%;*margin-left:16.98763411529369%}.row-fluid .offset1{margin-left:11.11111111111111%;*margin-left:11.004728132387708%}.row-fluid .offset1:first-child{margin-left:8.547008547008547%;*margin-left:8.440625568285142%}input,textarea,.uneditable-input{margin-left:0}.controls-row [class*="span"]+[class*="span"]{margin-left:30px}input.span12,textarea.span12,.uneditable-input.span12{width:1156px}input.span11,textarea.span11,.uneditable-input.span11{width:1056px}input.span10,textarea.span10,.uneditable-input.span10{width:956px}input.span9,textarea.span9,.uneditable-input.span9{width:856px}input.span8,textarea.span8,.uneditable-input.span8{width:756px}input.span7,textarea.span7,.uneditable-input.span7{width:656px}input.span6,textarea.span6,.uneditable-input.span6{width:556px}input.span5,textarea.span5,.uneditable-input.span5{width:456px}input.span4,textarea.span4,.uneditable-input.span4{width:356px}input.span3,textarea.span3,.uneditable-input.span3{width:256px}input.span2,textarea.span2,.uneditable-input.span2{width:156px}input.span1,textarea.span1,.uneditable-input.span1{width:56px}.thumbnails{margin-left:-30px}.thumbnails>li{margin-left:30px}.row-fluid .thumbnails{margin-left:0}}@media(min-width:768px) and (max-width:979px){.row{margin-left:-20px;*zoom:1}.row:before,.row:after{display:table;line-height:0;content:""}.row:after{clear:both}[class*="span"]{float:left;min-height:1px;margin-left:20px}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:724px}.span12{width:724px}.span11{width:662px}.span10{width:600px}.span9{width:538px}.span8{width:476px}.span7{width:414px}.span6{width:352px}.span5{width:290px}.span4{width:228px}.span3{width:166px}.span2{width:104px}.span1{width:42px}.offset12{margin-left:764px}.offset11{margin-left:702px}.offset10{margin-left:640px}.offset9{margin-left:578px}.offset8{margin-left:516px}.offset7{margin-left:454px}.offset6{margin-left:392px}.offset5{margin-left:330px}.offset4{margin-left:268px}.offset3{margin-left:206px}.offset2{margin-left:144px}.offset1{margin-left:82px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;line-height:0;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:2.7624309392265194%;*margin-left:2.709239449864817%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .controls-row [class*="span"]+[class*="span"]{margin-left:2.7624309392265194%}.row-fluid .span12{width:100%;*width:99.94680851063829%}.row-fluid .span11{width:91.43646408839778%;*width:91.38327259903608%}.row-fluid .span10{width:82.87292817679558%;*width:82.81973668743387%}.row-fluid .span9{width:74.30939226519337%;*width:74.25620077583166%}.row-fluid .span8{width:65.74585635359117%;*width:65.69266486422946%}.row-fluid .span7{width:57.18232044198895%;*width:57.12912895262725%}.row-fluid .span6{width:48.61878453038674%;*width:48.56559304102504%}.row-fluid .span5{width:40.05524861878453%;*width:40.00205712942283%}.row-fluid .span4{width:31.491712707182323%;*width:31.43852121782062%}.row-fluid .span3{width:22.92817679558011%;*width:22.87498530621841%}.row-fluid .span2{width:14.3646408839779%;*width:14.311449394616199%}.row-fluid .span1{width:5.801104972375691%;*width:5.747913483013988%}.row-fluid .offset12{margin-left:105.52486187845304%;*margin-left:105.41847889972962%}.row-fluid .offset12:first-child{margin-left:102.76243093922652%;*margin-left:102.6560479605031%}.row-fluid .offset11{margin-left:96.96132596685082%;*margin-left:96.8549429881274%}.row-fluid .offset11:first-child{margin-left:94.1988950276243%;*margin-left:94.09251204890089%}.row-fluid .offset10{margin-left:88.39779005524862%;*margin-left:88.2914070765252%}.row-fluid .offset10:first-child{margin-left:85.6353591160221%;*margin-left:85.52897613729868%}.row-fluid .offset9{margin-left:79.8342541436464%;*margin-left:79.72787116492299%}.row-fluid .offset9:first-child{margin-left:77.07182320441989%;*margin-left:76.96544022569647%}.row-fluid .offset8{margin-left:71.2707182320442%;*margin-left:71.16433525332079%}.row-fluid .offset8:first-child{margin-left:68.50828729281768%;*margin-left:68.40190431409427%}.row-fluid .offset7{margin-left:62.70718232044199%;*margin-left:62.600799341718584%}.row-fluid .offset7:first-child{margin-left:59.94475138121547%;*margin-left:59.838368402492065%}.row-fluid .offset6{margin-left:54.14364640883978%;*margin-left:54.037263430116376%}.row-fluid .offset6:first-child{margin-left:51.38121546961326%;*margin-left:51.27483249088986%}.row-fluid .offset5{margin-left:45.58011049723757%;*margin-left:45.47372751851417%}.row-fluid .offset5:first-child{margin-left:42.81767955801105%;*margin-left:42.71129657928765%}.row-fluid .offset4{margin-left:37.01657458563536%;*margin-left:36.91019160691196%}.row-fluid .offset4:first-child{margin-left:34.25414364640884%;*margin-left:34.14776066768544%}.row-fluid .offset3{margin-left:28.45303867403315%;*margin-left:28.346655695309746%}.row-fluid .offset3:first-child{margin-left:25.69060773480663%;*margin-left:25.584224756083227%}.row-fluid .offset2{margin-left:19.88950276243094%;*margin-left:19.783119783707537%}.row-fluid .offset2:first-child{margin-left:17.12707182320442%;*margin-left:17.02068884448102%}.row-fluid .offset1{margin-left:11.32596685082873%;*margin-left:11.219583872105325%}.row-fluid .offset1:first-child{margin-left:8.56353591160221%;*margin-left:8.457152932878806%}input,textarea,.uneditable-input{margin-left:0}.controls-row [class*="span"]+[class*="span"]{margin-left:20px}input.span12,textarea.span12,.uneditable-input.span12{width:710px}input.span11,textarea.span11,.uneditable-input.span11{width:648px}input.span10,textarea.span10,.uneditable-input.span10{width:586px}input.span9,textarea.span9,.uneditable-input.span9{width:524px}input.span8,textarea.span8,.uneditable-input.span8{width:462px}input.span7,textarea.span7,.uneditable-input.span7{width:400px}input.span6,textarea.span6,.uneditable-input.span6{width:338px}input.span5,textarea.span5,.uneditable-input.span5{width:276px}input.span4,textarea.span4,.uneditable-input.span4{width:214px}input.span3,textarea.span3,.uneditable-input.span3{width:152px}input.span2,textarea.span2,.uneditable-input.span2{width:90px}input.span1,textarea.span1,.uneditable-input.span1{width:28px}}@media(max-width:767px){body{padding-right:20px;padding-left:20px}.navbar-fixed-top,.navbar-fixed-bottom,.navbar-static-top{margin-right:-20px;margin-left:-20px}.container-fluid{padding:0}.dl-horizontal dt{float:none;width:auto;clear:none;text-align:left}.dl-horizontal dd{margin-left:0}.container{width:auto}.row-fluid{width:100%}.row,.thumbnails{margin-left:0}.thumbnails>li{float:none;margin-left:0}[class*="span"],.uneditable-input[class*="span"],.row-fluid [class*="span"]{display:block;float:none;width:100%;margin-left:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.span12,.row-fluid .span12{width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="offset"]:first-child{margin-left:0}.input-large,.input-xlarge,.input-xxlarge,input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.input-prepend input,.input-append input,.input-prepend input[class*="span"],.input-append input[class*="span"]{display:inline-block;width:auto}.controls-row [class*="span"]+[class*="span"]{margin-left:0}.modal{position:fixed;top:20px;right:20px;left:20px;width:auto;margin:0}.modal.fade{top:-100px}.modal.fade.in{top:20px}}@media(max-width:480px){.nav-collapse{-webkit-transform:translate3d(0,0,0)}.page-header h1 small{display:block;line-height:20px}input[type="checkbox"],input[type="radio"]{border:1px solid #ccc}.form-horizontal .control-label{float:none;width:auto;padding-top:0;text-align:left}.form-horizontal .controls{margin-left:0}.form-horizontal .control-list{padding-top:0}.form-horizontal .form-actions{padding-right:10px;padding-left:10px}.media .pull-left,.media .pull-right{display:block;float:none;margin-bottom:10px}.media-object{margin-right:0;margin-left:0}.modal{top:10px;right:10px;left:10px}.modal-header .close{padding:10px;margin:-10px}.carousel-caption{position:static}}@media(max-width:979px){body{padding-top:0}.navbar-fixed-top,.navbar-fixed-bottom{position:static}.navbar-fixed-top{margin-bottom:20px}.navbar-fixed-bottom{margin-top:20px}.navbar-fixed-top .navbar-inner,.navbar-fixed-bottom .navbar-inner{padding:5px}.navbar .container{width:auto;padding:0}.navbar .brand{padding-right:10px;padding-left:10px;margin:0 0 0 -5px}.nav-collapse{clear:both}.nav-collapse .nav{float:none;margin:0 0 10px}.nav-collapse .nav>li{float:none}.nav-collapse .nav>li>a{margin-bottom:2px}.nav-collapse .nav>.divider-vertical{display:none}.nav-collapse .nav .nav-header{color:#777;text-shadow:none}.nav-collapse .nav>li>a,.nav-collapse .dropdown-menu a{padding:9px 15px;font-weight:bold;color:#777;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.nav-collapse .btn{padding:4px 10px 4px;font-weight:normal;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.nav-collapse .dropdown-menu li+li a{margin-bottom:2px}.nav-collapse .nav>li>a:hover,.nav-collapse .dropdown-menu a:hover{background-color:#f2f2f2}.navbar-inverse .nav-collapse .nav>li>a,.navbar-inverse .nav-collapse .dropdown-menu a{color:#999}.navbar-inverse .nav-collapse .nav>li>a:hover,.navbar-inverse .nav-collapse .dropdown-menu a:hover{background-color:#111}.nav-collapse.in .btn-group{padding:0;margin-top:5px}.nav-collapse .dropdown-menu{position:static;top:auto;left:auto;display:none;float:none;max-width:none;padding:0;margin:0 15px;background-color:transparent;border:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.nav-collapse .open>.dropdown-menu{display:block}.nav-collapse .dropdown-menu:before,.nav-collapse .dropdown-menu:after{display:none}.nav-collapse .dropdown-menu .divider{display:none}.nav-collapse .nav>li>.dropdown-menu:before,.nav-collapse .nav>li>.dropdown-menu:after{display:none}.nav-collapse .navbar-form,.nav-collapse .navbar-search{float:none;padding:10px 15px;margin:10px 0;border-top:1px solid #f2f2f2;border-bottom:1px solid #f2f2f2;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1)}.navbar-inverse .nav-collapse .navbar-form,.navbar-inverse .nav-collapse .navbar-search{border-top-color:#111;border-bottom-color:#111}.navbar .nav-collapse .nav.pull-right{float:none;margin-left:0}.nav-collapse,.nav-collapse.collapse{height:0;overflow:hidden}.navbar .btn-navbar{display:block}.navbar-static .navbar-inner{padding-right:10px;padding-left:10px}}@media(min-width:980px){.nav-collapse.collapse{height:auto!important;overflow:visible!important}} diff --git a/main/webapp/static/css/bootstrap.css b/main/webapp/static/css/bootstrap.css new file mode 100644 index 0000000..6167622 --- /dev/null +++ b/main/webapp/static/css/bootstrap.css @@ -0,0 +1,6757 @@ +/*! + * Bootstrap v3.3.7 (http://getbootstrap.com) + * Copyright 2011-2016 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */ +/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */ +html { + font-family: sans-serif; + -webkit-text-size-adjust: 100%; + -ms-text-size-adjust: 100%; +} +body { + margin: 0; +} +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +main, +menu, +nav, +section, +summary { + display: block; +} +audio, +canvas, +progress, +video { + display: inline-block; + vertical-align: baseline; +} +audio:not([controls]) { + display: none; + height: 0; +} +[hidden], +template { + display: none; +} +a { + background-color: transparent; +} +a:active, +a:hover { + outline: 0; +} +abbr[title] { + border-bottom: 1px dotted; +} +b, +strong { + font-weight: bold; +} +dfn { + font-style: italic; +} +h1 { + margin: .67em 0; + font-size: 2em; +} +mark { + color: #000; + background: #ff0; +} +small { + font-size: 80%; +} +sub, +sup { + position: relative; + font-size: 75%; + line-height: 0; + vertical-align: baseline; +} +sup { + top: -.5em; +} +sub { + bottom: -.25em; +} +img { + border: 0; +} +svg:not(:root) { + overflow: hidden; +} +figure { + margin: 1em 40px; +} +hr { + height: 0; + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; +} +pre { + overflow: auto; +} +code, +kbd, +pre, +samp { + font-family: monospace, monospace; + font-size: 1em; +} +button, +input, +optgroup, +select, +textarea { + margin: 0; + font: inherit; + color: inherit; +} +button { + overflow: visible; +} +button, +select { + text-transform: none; +} +button, +html input[type="button"], +input[type="reset"], +input[type="submit"] { + -webkit-appearance: button; + cursor: pointer; +} +button[disabled], +html input[disabled] { + cursor: default; +} +button::-moz-focus-inner, +input::-moz-focus-inner { + padding: 0; + border: 0; +} +input { + line-height: normal; +} +input[type="checkbox"], +input[type="radio"] { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + padding: 0; +} +input[type="number"]::-webkit-inner-spin-button, +input[type="number"]::-webkit-outer-spin-button { + height: auto; +} +input[type="search"] { + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; + -webkit-appearance: textfield; +} +input[type="search"]::-webkit-search-cancel-button, +input[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} +fieldset { + padding: .35em .625em .75em; + margin: 0 2px; + border: 1px solid #c0c0c0; +} +legend { + padding: 0; + border: 0; +} +textarea { + overflow: auto; +} +optgroup { + font-weight: bold; +} +table { + border-spacing: 0; + border-collapse: collapse; +} +td, +th { + padding: 0; +} +/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */ +@media print { + *, + *:before, + *:after { + color: #000 !important; + text-shadow: none !important; + background: transparent !important; + -webkit-box-shadow: none !important; + box-shadow: none !important; + } + a, + a:visited { + text-decoration: underline; + } + a[href]:after { + content: " (" attr(href) ")"; + } + abbr[title]:after { + content: " (" attr(title) ")"; + } + a[href^="#"]:after, + a[href^="javascript:"]:after { + content: ""; + } + pre, + blockquote { + border: 1px solid #999; + + page-break-inside: avoid; + } + thead { + display: table-header-group; + } + tr, + img { + page-break-inside: avoid; + } + img { + max-width: 100% !important; + } + p, + h2, + h3 { + orphans: 3; + widows: 3; + } + h2, + h3 { + page-break-after: avoid; + } + .navbar { + display: none; + } + .btn > .caret, + .dropup > .btn > .caret { + border-top-color: #000 !important; + } + .label { + border: 1px solid #000; + } + .table { + border-collapse: collapse !important; + } + .table td, + .table th { + background-color: #fff !important; + } + .table-bordered th, + .table-bordered td { + border: 1px solid #ddd !important; + } +} +@font-face { + font-family: 'Glyphicons Halflings'; + + src: url('../fonts/glyphicons-halflings-regular.eot'); + src: url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('../fonts/glyphicons-halflings-regular.woff2') format('woff2'), url('../fonts/glyphicons-halflings-regular.woff') format('woff'), url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg'); +} +.glyphicon { + position: relative; + top: 1px; + display: inline-block; + font-family: 'Glyphicons Halflings'; + font-style: normal; + font-weight: normal; + line-height: 1; + + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} +.glyphicon-asterisk:before { + content: "\002a"; +} +.glyphicon-plus:before { + content: "\002b"; +} +.glyphicon-euro:before, +.glyphicon-eur:before { + content: "\20ac"; +} +.glyphicon-minus:before { + content: "\2212"; +} +.glyphicon-cloud:before { + content: "\2601"; +} +.glyphicon-envelope:before { + content: "\2709"; +} +.glyphicon-pencil:before { + content: "\270f"; +} +.glyphicon-glass:before { + content: "\e001"; +} +.glyphicon-music:before { + content: "\e002"; +} +.glyphicon-search:before { + content: "\e003"; +} +.glyphicon-heart:before { + content: "\e005"; +} +.glyphicon-star:before { + content: "\e006"; +} +.glyphicon-star-empty:before { + content: "\e007"; +} +.glyphicon-user:before { + content: "\e008"; +} +.glyphicon-film:before { + content: "\e009"; +} +.glyphicon-th-large:before { + content: "\e010"; +} +.glyphicon-th:before { + content: "\e011"; +} +.glyphicon-th-list:before { + content: "\e012"; +} +.glyphicon-ok:before { + content: "\e013"; +} +.glyphicon-remove:before { + content: "\e014"; +} +.glyphicon-zoom-in:before { + content: "\e015"; +} +.glyphicon-zoom-out:before { + content: "\e016"; +} +.glyphicon-off:before { + content: "\e017"; +} +.glyphicon-signal:before { + content: "\e018"; +} +.glyphicon-cog:before { + content: "\e019"; +} +.glyphicon-trash:before { + content: "\e020"; +} +.glyphicon-home:before { + content: "\e021"; +} +.glyphicon-file:before { + content: "\e022"; +} +.glyphicon-time:before { + content: "\e023"; +} +.glyphicon-road:before { + content: "\e024"; +} +.glyphicon-download-alt:before { + content: "\e025"; +} +.glyphicon-download:before { + content: "\e026"; +} +.glyphicon-upload:before { + content: "\e027"; +} +.glyphicon-inbox:before { + content: "\e028"; +} +.glyphicon-play-circle:before { + content: "\e029"; +} +.glyphicon-repeat:before { + content: "\e030"; +} +.glyphicon-refresh:before { + content: "\e031"; +} +.glyphicon-list-alt:before { + content: "\e032"; +} +.glyphicon-lock:before { + content: "\e033"; +} +.glyphicon-flag:before { + content: "\e034"; +} +.glyphicon-headphones:before { + content: "\e035"; +} +.glyphicon-volume-off:before { + content: "\e036"; +} +.glyphicon-volume-down:before { + content: "\e037"; +} +.glyphicon-volume-up:before { + content: "\e038"; +} +.glyphicon-qrcode:before { + content: "\e039"; +} +.glyphicon-barcode:before { + content: "\e040"; +} +.glyphicon-tag:before { + content: "\e041"; +} +.glyphicon-tags:before { + content: "\e042"; +} +.glyphicon-book:before { + content: "\e043"; +} +.glyphicon-bookmark:before { + content: "\e044"; +} +.glyphicon-print:before { + content: "\e045"; +} +.glyphicon-camera:before { + content: "\e046"; +} +.glyphicon-font:before { + content: "\e047"; +} +.glyphicon-bold:before { + content: "\e048"; +} +.glyphicon-italic:before { + content: "\e049"; +} +.glyphicon-text-height:before { + content: "\e050"; +} +.glyphicon-text-width:before { + content: "\e051"; +} +.glyphicon-align-left:before { + content: "\e052"; +} +.glyphicon-align-center:before { + content: "\e053"; +} +.glyphicon-align-right:before { + content: "\e054"; +} +.glyphicon-align-justify:before { + content: "\e055"; +} +.glyphicon-list:before { + content: "\e056"; +} +.glyphicon-indent-left:before { + content: "\e057"; +} +.glyphicon-indent-right:before { + content: "\e058"; +} +.glyphicon-facetime-video:before { + content: "\e059"; +} +.glyphicon-picture:before { + content: "\e060"; +} +.glyphicon-map-marker:before { + content: "\e062"; +} +.glyphicon-adjust:before { + content: "\e063"; +} +.glyphicon-tint:before { + content: "\e064"; +} +.glyphicon-edit:before { + content: "\e065"; +} +.glyphicon-share:before { + content: "\e066"; +} +.glyphicon-check:before { + content: "\e067"; +} +.glyphicon-move:before { + content: "\e068"; +} +.glyphicon-step-backward:before { + content: "\e069"; +} +.glyphicon-fast-backward:before { + content: "\e070"; +} +.glyphicon-backward:before { + content: "\e071"; +} +.glyphicon-play:before { + content: "\e072"; +} +.glyphicon-pause:before { + content: "\e073"; +} +.glyphicon-stop:before { + content: "\e074"; +} +.glyphicon-forward:before { + content: "\e075"; +} +.glyphicon-fast-forward:before { + content: "\e076"; +} +.glyphicon-step-forward:before { + content: "\e077"; +} +.glyphicon-eject:before { + content: "\e078"; +} +.glyphicon-chevron-left:before { + content: "\e079"; +} +.glyphicon-chevron-right:before { + content: "\e080"; +} +.glyphicon-plus-sign:before { + content: "\e081"; +} +.glyphicon-minus-sign:before { + content: "\e082"; +} +.glyphicon-remove-sign:before { + content: "\e083"; +} +.glyphicon-ok-sign:before { + content: "\e084"; +} +.glyphicon-question-sign:before { + content: "\e085"; +} +.glyphicon-info-sign:before { + content: "\e086"; +} +.glyphicon-screenshot:before { + content: "\e087"; +} +.glyphicon-remove-circle:before { + content: "\e088"; +} +.glyphicon-ok-circle:before { + content: "\e089"; +} +.glyphicon-ban-circle:before { + content: "\e090"; +} +.glyphicon-arrow-left:before { + content: "\e091"; +} +.glyphicon-arrow-right:before { + content: "\e092"; +} +.glyphicon-arrow-up:before { + content: "\e093"; +} +.glyphicon-arrow-down:before { + content: "\e094"; +} +.glyphicon-share-alt:before { + content: "\e095"; +} +.glyphicon-resize-full:before { + content: "\e096"; +} +.glyphicon-resize-small:before { + content: "\e097"; +} +.glyphicon-exclamation-sign:before { + content: "\e101"; +} +.glyphicon-gift:before { + content: "\e102"; +} +.glyphicon-leaf:before { + content: "\e103"; +} +.glyphicon-fire:before { + content: "\e104"; +} +.glyphicon-eye-open:before { + content: "\e105"; +} +.glyphicon-eye-close:before { + content: "\e106"; +} +.glyphicon-warning-sign:before { + content: "\e107"; +} +.glyphicon-plane:before { + content: "\e108"; +} +.glyphicon-calendar:before { + content: "\e109"; +} +.glyphicon-random:before { + content: "\e110"; +} +.glyphicon-comment:before { + content: "\e111"; +} +.glyphicon-magnet:before { + content: "\e112"; +} +.glyphicon-chevron-up:before { + content: "\e113"; +} +.glyphicon-chevron-down:before { + content: "\e114"; +} +.glyphicon-retweet:before { + content: "\e115"; +} +.glyphicon-shopping-cart:before { + content: "\e116"; +} +.glyphicon-folder-close:before { + content: "\e117"; +} +.glyphicon-folder-open:before { + content: "\e118"; +} +.glyphicon-resize-vertical:before { + content: "\e119"; +} +.glyphicon-resize-horizontal:before { + content: "\e120"; +} +.glyphicon-hdd:before { + content: "\e121"; +} +.glyphicon-bullhorn:before { + content: "\e122"; +} +.glyphicon-bell:before { + content: "\e123"; +} +.glyphicon-certificate:before { + content: "\e124"; +} +.glyphicon-thumbs-up:before { + content: "\e125"; +} +.glyphicon-thumbs-down:before { + content: "\e126"; +} +.glyphicon-hand-right:before { + content: "\e127"; +} +.glyphicon-hand-left:before { + content: "\e128"; +} +.glyphicon-hand-up:before { + content: "\e129"; +} +.glyphicon-hand-down:before { + content: "\e130"; +} +.glyphicon-circle-arrow-right:before { + content: "\e131"; +} +.glyphicon-circle-arrow-left:before { + content: "\e132"; +} +.glyphicon-circle-arrow-up:before { + content: "\e133"; +} +.glyphicon-circle-arrow-down:before { + content: "\e134"; +} +.glyphicon-globe:before { + content: "\e135"; +} +.glyphicon-wrench:before { + content: "\e136"; +} +.glyphicon-tasks:before { + content: "\e137"; +} +.glyphicon-filter:before { + content: "\e138"; +} +.glyphicon-briefcase:before { + content: "\e139"; +} +.glyphicon-fullscreen:before { + content: "\e140"; +} +.glyphicon-dashboard:before { + content: "\e141"; +} +.glyphicon-paperclip:before { + content: "\e142"; +} +.glyphicon-heart-empty:before { + content: "\e143"; +} +.glyphicon-link:before { + content: "\e144"; +} +.glyphicon-phone:before { + content: "\e145"; +} +.glyphicon-pushpin:before { + content: "\e146"; +} +.glyphicon-usd:before { + content: "\e148"; +} +.glyphicon-gbp:before { + content: "\e149"; +} +.glyphicon-sort:before { + content: "\e150"; +} +.glyphicon-sort-by-alphabet:before { + content: "\e151"; +} +.glyphicon-sort-by-alphabet-alt:before { + content: "\e152"; +} +.glyphicon-sort-by-order:before { + content: "\e153"; +} +.glyphicon-sort-by-order-alt:before { + content: "\e154"; +} +.glyphicon-sort-by-attributes:before { + content: "\e155"; +} +.glyphicon-sort-by-attributes-alt:before { + content: "\e156"; +} +.glyphicon-unchecked:before { + content: "\e157"; +} +.glyphicon-expand:before { + content: "\e158"; +} +.glyphicon-collapse-down:before { + content: "\e159"; +} +.glyphicon-collapse-up:before { + content: "\e160"; +} +.glyphicon-log-in:before { + content: "\e161"; +} +.glyphicon-flash:before { + content: "\e162"; +} +.glyphicon-log-out:before { + content: "\e163"; +} +.glyphicon-new-window:before { + content: "\e164"; +} +.glyphicon-record:before { + content: "\e165"; +} +.glyphicon-save:before { + content: "\e166"; +} +.glyphicon-open:before { + content: "\e167"; +} +.glyphicon-saved:before { + content: "\e168"; +} +.glyphicon-import:before { + content: "\e169"; +} +.glyphicon-export:before { + content: "\e170"; +} +.glyphicon-send:before { + content: "\e171"; +} +.glyphicon-floppy-disk:before { + content: "\e172"; +} +.glyphicon-floppy-saved:before { + content: "\e173"; +} +.glyphicon-floppy-remove:before { + content: "\e174"; +} +.glyphicon-floppy-save:before { + content: "\e175"; +} +.glyphicon-floppy-open:before { + content: "\e176"; +} +.glyphicon-credit-card:before { + content: "\e177"; +} +.glyphicon-transfer:before { + content: "\e178"; +} +.glyphicon-cutlery:before { + content: "\e179"; +} +.glyphicon-header:before { + content: "\e180"; +} +.glyphicon-compressed:before { + content: "\e181"; +} +.glyphicon-earphone:before { + content: "\e182"; +} +.glyphicon-phone-alt:before { + content: "\e183"; +} +.glyphicon-tower:before { + content: "\e184"; +} +.glyphicon-stats:before { + content: "\e185"; +} +.glyphicon-sd-video:before { + content: "\e186"; +} +.glyphicon-hd-video:before { + content: "\e187"; +} +.glyphicon-subtitles:before { + content: "\e188"; +} +.glyphicon-sound-stereo:before { + content: "\e189"; +} +.glyphicon-sound-dolby:before { + content: "\e190"; +} +.glyphicon-sound-5-1:before { + content: "\e191"; +} +.glyphicon-sound-6-1:before { + content: "\e192"; +} +.glyphicon-sound-7-1:before { + content: "\e193"; +} +.glyphicon-copyright-mark:before { + content: "\e194"; +} +.glyphicon-registration-mark:before { + content: "\e195"; +} +.glyphicon-cloud-download:before { + content: "\e197"; +} +.glyphicon-cloud-upload:before { + content: "\e198"; +} +.glyphicon-tree-conifer:before { + content: "\e199"; +} +.glyphicon-tree-deciduous:before { + content: "\e200"; +} +.glyphicon-cd:before { + content: "\e201"; +} +.glyphicon-save-file:before { + content: "\e202"; +} +.glyphicon-open-file:before { + content: "\e203"; +} +.glyphicon-level-up:before { + content: "\e204"; +} +.glyphicon-copy:before { + content: "\e205"; +} +.glyphicon-paste:before { + content: "\e206"; +} +.glyphicon-alert:before { + content: "\e209"; +} +.glyphicon-equalizer:before { + content: "\e210"; +} +.glyphicon-king:before { + content: "\e211"; +} +.glyphicon-queen:before { + content: "\e212"; +} +.glyphicon-pawn:before { + content: "\e213"; +} +.glyphicon-bishop:before { + content: "\e214"; +} +.glyphicon-knight:before { + content: "\e215"; +} +.glyphicon-baby-formula:before { + content: "\e216"; +} +.glyphicon-tent:before { + content: "\26fa"; +} +.glyphicon-blackboard:before { + content: "\e218"; +} +.glyphicon-bed:before { + content: "\e219"; +} +.glyphicon-apple:before { + content: "\f8ff"; +} +.glyphicon-erase:before { + content: "\e221"; +} +.glyphicon-hourglass:before { + content: "\231b"; +} +.glyphicon-lamp:before { + content: "\e223"; +} +.glyphicon-duplicate:before { + content: "\e224"; +} +.glyphicon-piggy-bank:before { + content: "\e225"; +} +.glyphicon-scissors:before { + content: "\e226"; +} +.glyphicon-bitcoin:before { + content: "\e227"; +} +.glyphicon-btc:before { + content: "\e227"; +} +.glyphicon-xbt:before { + content: "\e227"; +} +.glyphicon-yen:before { + content: "\00a5"; +} +.glyphicon-jpy:before { + content: "\00a5"; +} +.glyphicon-ruble:before { + content: "\20bd"; +} +.glyphicon-rub:before { + content: "\20bd"; +} +.glyphicon-scale:before { + content: "\e230"; +} +.glyphicon-ice-lolly:before { + content: "\e231"; +} +.glyphicon-ice-lolly-tasted:before { + content: "\e232"; +} +.glyphicon-education:before { + content: "\e233"; +} +.glyphicon-option-horizontal:before { + content: "\e234"; +} +.glyphicon-option-vertical:before { + content: "\e235"; +} +.glyphicon-menu-hamburger:before { + content: "\e236"; +} +.glyphicon-modal-window:before { + content: "\e237"; +} +.glyphicon-oil:before { + content: "\e238"; +} +.glyphicon-grain:before { + content: "\e239"; +} +.glyphicon-sunglasses:before { + content: "\e240"; +} +.glyphicon-text-size:before { + content: "\e241"; +} +.glyphicon-text-color:before { + content: "\e242"; +} +.glyphicon-text-background:before { + content: "\e243"; +} +.glyphicon-object-align-top:before { + content: "\e244"; +} +.glyphicon-object-align-bottom:before { + content: "\e245"; +} +.glyphicon-object-align-horizontal:before { + content: "\e246"; +} +.glyphicon-object-align-left:before { + content: "\e247"; +} +.glyphicon-object-align-vertical:before { + content: "\e248"; +} +.glyphicon-object-align-right:before { + content: "\e249"; +} +.glyphicon-triangle-right:before { + content: "\e250"; +} +.glyphicon-triangle-left:before { + content: "\e251"; +} +.glyphicon-triangle-bottom:before { + content: "\e252"; +} +.glyphicon-triangle-top:before { + content: "\e253"; +} +.glyphicon-console:before { + content: "\e254"; +} +.glyphicon-superscript:before { + content: "\e255"; +} +.glyphicon-subscript:before { + content: "\e256"; +} +.glyphicon-menu-left:before { + content: "\e257"; +} +.glyphicon-menu-right:before { + content: "\e258"; +} +.glyphicon-menu-down:before { + content: "\e259"; +} +.glyphicon-menu-up:before { + content: "\e260"; +} +* { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +*:before, +*:after { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +html { + font-size: 10px; + + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); +} +body { + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 14px; + line-height: 1.42857143; + color: #333; + background-color: #fff; +} +input, +button, +select, +textarea { + font-family: inherit; + font-size: inherit; + line-height: inherit; +} +a { + color: #337ab7; + text-decoration: none; +} +a:hover, +a:focus { + color: #23527c; + text-decoration: underline; +} +a:focus { + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +figure { + margin: 0; +} +img { + vertical-align: middle; +} +.img-responsive, +.thumbnail > img, +.thumbnail a > img, +.carousel-inner > .item > img, +.carousel-inner > .item > a > img { + display: block; + max-width: 100%; + height: auto; +} +.img-rounded { + border-radius: 6px; +} +.img-thumbnail { + display: inline-block; + max-width: 100%; + height: auto; + padding: 4px; + line-height: 1.42857143; + background-color: #fff; + border: 1px solid #ddd; + border-radius: 4px; + -webkit-transition: all .2s ease-in-out; + -o-transition: all .2s ease-in-out; + transition: all .2s ease-in-out; +} +.img-circle { + border-radius: 50%; +} +hr { + margin-top: 20px; + margin-bottom: 20px; + border: 0; + border-top: 1px solid #eee; +} +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; +} +.sr-only-focusable:active, +.sr-only-focusable:focus { + position: static; + width: auto; + height: auto; + margin: 0; + overflow: visible; + clip: auto; +} +[role="button"] { + cursor: pointer; +} +h1, +h2, +h3, +h4, +h5, +h6, +.h1, +.h2, +.h3, +.h4, +.h5, +.h6 { + font-family: inherit; + font-weight: 500; + line-height: 1.1; + color: inherit; +} +h1 small, +h2 small, +h3 small, +h4 small, +h5 small, +h6 small, +.h1 small, +.h2 small, +.h3 small, +.h4 small, +.h5 small, +.h6 small, +h1 .small, +h2 .small, +h3 .small, +h4 .small, +h5 .small, +h6 .small, +.h1 .small, +.h2 .small, +.h3 .small, +.h4 .small, +.h5 .small, +.h6 .small { + font-weight: normal; + line-height: 1; + color: #777; +} +h1, +.h1, +h2, +.h2, +h3, +.h3 { + margin-top: 20px; + margin-bottom: 10px; +} +h1 small, +.h1 small, +h2 small, +.h2 small, +h3 small, +.h3 small, +h1 .small, +.h1 .small, +h2 .small, +.h2 .small, +h3 .small, +.h3 .small { + font-size: 65%; +} +h4, +.h4, +h5, +.h5, +h6, +.h6 { + margin-top: 10px; + margin-bottom: 10px; +} +h4 small, +.h4 small, +h5 small, +.h5 small, +h6 small, +.h6 small, +h4 .small, +.h4 .small, +h5 .small, +.h5 .small, +h6 .small, +.h6 .small { + font-size: 75%; +} +h1, +.h1 { + font-size: 36px; +} +h2, +.h2 { + font-size: 30px; +} +h3, +.h3 { + font-size: 24px; +} +h4, +.h4 { + font-size: 18px; +} +h5, +.h5 { + font-size: 14px; +} +h6, +.h6 { + font-size: 12px; +} +p { + margin: 0 0 10px; +} +.lead { + margin-bottom: 20px; + font-size: 16px; + font-weight: 300; + line-height: 1.4; +} +@media (min-width: 768px) { + .lead { + font-size: 21px; + } +} +small, +.small { + font-size: 85%; +} +mark, +.mark { + padding: .2em; + background-color: #fcf8e3; +} +.text-left { + text-align: left; +} +.text-right { + text-align: right; +} +.text-center { + text-align: center; +} +.text-justify { + text-align: justify; +} +.text-nowrap { + white-space: nowrap; +} +.text-lowercase { + text-transform: lowercase; +} +.text-uppercase { + text-transform: uppercase; +} +.text-capitalize { + text-transform: capitalize; +} +.text-muted { + color: #777; +} +.text-primary { + color: #337ab7; +} +a.text-primary:hover, +a.text-primary:focus { + color: #286090; +} +.text-success { + color: #3c763d; +} +a.text-success:hover, +a.text-success:focus { + color: #2b542c; +} +.text-info { + color: #31708f; +} +a.text-info:hover, +a.text-info:focus { + color: #245269; +} +.text-warning { + color: #8a6d3b; +} +a.text-warning:hover, +a.text-warning:focus { + color: #66512c; +} +.text-danger { + color: #a94442; +} +a.text-danger:hover, +a.text-danger:focus { + color: #843534; +} +.bg-primary { + color: #fff; + background-color: #337ab7; +} +a.bg-primary:hover, +a.bg-primary:focus { + background-color: #286090; +} +.bg-success { + background-color: #dff0d8; +} +a.bg-success:hover, +a.bg-success:focus { + background-color: #c1e2b3; +} +.bg-info { + background-color: #d9edf7; +} +a.bg-info:hover, +a.bg-info:focus { + background-color: #afd9ee; +} +.bg-warning { + background-color: #fcf8e3; +} +a.bg-warning:hover, +a.bg-warning:focus { + background-color: #f7ecb5; +} +.bg-danger { + background-color: #f2dede; +} +a.bg-danger:hover, +a.bg-danger:focus { + background-color: #e4b9b9; +} +.page-header { + padding-bottom: 9px; + margin: 40px 0 20px; + border-bottom: 1px solid #eee; +} +ul, +ol { + margin-top: 0; + margin-bottom: 10px; +} +ul ul, +ol ul, +ul ol, +ol ol { + margin-bottom: 0; +} +.list-unstyled { + padding-left: 0; + list-style: none; +} +.list-inline { + padding-left: 0; + margin-left: -5px; + list-style: none; +} +.list-inline > li { + display: inline-block; + padding-right: 5px; + padding-left: 5px; +} +dl { + margin-top: 0; + margin-bottom: 20px; +} +dt, +dd { + line-height: 1.42857143; +} +dt { + font-weight: bold; +} +dd { + margin-left: 0; +} +@media (min-width: 768px) { + .dl-horizontal dt { + float: left; + width: 160px; + overflow: hidden; + clear: left; + text-align: right; + text-overflow: ellipsis; + white-space: nowrap; + } + .dl-horizontal dd { + margin-left: 180px; + } +} +abbr[title], +abbr[data-original-title] { + cursor: help; + border-bottom: 1px dotted #777; +} +.initialism { + font-size: 90%; + text-transform: uppercase; +} +blockquote { + padding: 10px 20px; + margin: 0 0 20px; + font-size: 17.5px; + border-left: 5px solid #eee; +} +blockquote p:last-child, +blockquote ul:last-child, +blockquote ol:last-child { + margin-bottom: 0; +} +blockquote footer, +blockquote small, +blockquote .small { + display: block; + font-size: 80%; + line-height: 1.42857143; + color: #777; +} +blockquote footer:before, +blockquote small:before, +blockquote .small:before { + content: '\2014 \00A0'; +} +.blockquote-reverse, +blockquote.pull-right { + padding-right: 15px; + padding-left: 0; + text-align: right; + border-right: 5px solid #eee; + border-left: 0; +} +.blockquote-reverse footer:before, +blockquote.pull-right footer:before, +.blockquote-reverse small:before, +blockquote.pull-right small:before, +.blockquote-reverse .small:before, +blockquote.pull-right .small:before { + content: ''; +} +.blockquote-reverse footer:after, +blockquote.pull-right footer:after, +.blockquote-reverse small:after, +blockquote.pull-right small:after, +.blockquote-reverse .small:after, +blockquote.pull-right .small:after { + content: '\00A0 \2014'; +} +address { + margin-bottom: 20px; + font-style: normal; + line-height: 1.42857143; +} +code, +kbd, +pre, +samp { + font-family: Menlo, Monaco, Consolas, "Courier New", monospace; +} +code { + padding: 2px 4px; + font-size: 90%; + color: #c7254e; + background-color: #f9f2f4; + border-radius: 4px; +} +kbd { + padding: 2px 4px; + font-size: 90%; + color: #fff; + background-color: #333; + border-radius: 3px; + -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .25); + box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .25); +} +kbd kbd { + padding: 0; + font-size: 100%; + font-weight: bold; + -webkit-box-shadow: none; + box-shadow: none; +} +pre { + display: block; + padding: 9.5px; + margin: 0 0 10px; + font-size: 13px; + line-height: 1.42857143; + color: #333; + word-break: break-all; + word-wrap: break-word; + background-color: #f5f5f5; + border: 1px solid #ccc; + border-radius: 4px; +} +pre code { + padding: 0; + font-size: inherit; + color: inherit; + white-space: pre-wrap; + background-color: transparent; + border-radius: 0; +} +.pre-scrollable { + max-height: 340px; + overflow-y: scroll; +} +.container { + padding-right: 15px; + padding-left: 15px; + margin-right: auto; + margin-left: auto; +} +@media (min-width: 768px) { + .container { + width: 750px; + } +} +@media (min-width: 992px) { + .container { + width: 970px; + } +} +@media (min-width: 1200px) { + .container { + width: 1170px; + } +} +.container-fluid { + padding-right: 15px; + padding-left: 15px; + margin-right: auto; + margin-left: auto; +} +.row { + margin-right: -15px; + margin-left: -15px; +} +.col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12 { + position: relative; + min-height: 1px; + padding-right: 15px; + padding-left: 15px; +} +.col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12 { + float: left; +} +.col-xs-12 { + width: 100%; +} +.col-xs-11 { + width: 91.66666667%; +} +.col-xs-10 { + width: 83.33333333%; +} +.col-xs-9 { + width: 75%; +} +.col-xs-8 { + width: 66.66666667%; +} +.col-xs-7 { + width: 58.33333333%; +} +.col-xs-6 { + width: 50%; +} +.col-xs-5 { + width: 41.66666667%; +} +.col-xs-4 { + width: 33.33333333%; +} +.col-xs-3 { + width: 25%; +} +.col-xs-2 { + width: 16.66666667%; +} +.col-xs-1 { + width: 8.33333333%; +} +.col-xs-pull-12 { + right: 100%; +} +.col-xs-pull-11 { + right: 91.66666667%; +} +.col-xs-pull-10 { + right: 83.33333333%; +} +.col-xs-pull-9 { + right: 75%; +} +.col-xs-pull-8 { + right: 66.66666667%; +} +.col-xs-pull-7 { + right: 58.33333333%; +} +.col-xs-pull-6 { + right: 50%; +} +.col-xs-pull-5 { + right: 41.66666667%; +} +.col-xs-pull-4 { + right: 33.33333333%; +} +.col-xs-pull-3 { + right: 25%; +} +.col-xs-pull-2 { + right: 16.66666667%; +} +.col-xs-pull-1 { + right: 8.33333333%; +} +.col-xs-pull-0 { + right: auto; +} +.col-xs-push-12 { + left: 100%; +} +.col-xs-push-11 { + left: 91.66666667%; +} +.col-xs-push-10 { + left: 83.33333333%; +} +.col-xs-push-9 { + left: 75%; +} +.col-xs-push-8 { + left: 66.66666667%; +} +.col-xs-push-7 { + left: 58.33333333%; +} +.col-xs-push-6 { + left: 50%; +} +.col-xs-push-5 { + left: 41.66666667%; +} +.col-xs-push-4 { + left: 33.33333333%; +} +.col-xs-push-3 { + left: 25%; +} +.col-xs-push-2 { + left: 16.66666667%; +} +.col-xs-push-1 { + left: 8.33333333%; +} +.col-xs-push-0 { + left: auto; +} +.col-xs-offset-12 { + margin-left: 100%; +} +.col-xs-offset-11 { + margin-left: 91.66666667%; +} +.col-xs-offset-10 { + margin-left: 83.33333333%; +} +.col-xs-offset-9 { + margin-left: 75%; +} +.col-xs-offset-8 { + margin-left: 66.66666667%; +} +.col-xs-offset-7 { + margin-left: 58.33333333%; +} +.col-xs-offset-6 { + margin-left: 50%; +} +.col-xs-offset-5 { + margin-left: 41.66666667%; +} +.col-xs-offset-4 { + margin-left: 33.33333333%; +} +.col-xs-offset-3 { + margin-left: 25%; +} +.col-xs-offset-2 { + margin-left: 16.66666667%; +} +.col-xs-offset-1 { + margin-left: 8.33333333%; +} +.col-xs-offset-0 { + margin-left: 0; +} +@media (min-width: 768px) { + .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12 { + float: left; + } + .col-sm-12 { + width: 100%; + } + .col-sm-11 { + width: 91.66666667%; + } + .col-sm-10 { + width: 83.33333333%; + } + .col-sm-9 { + width: 75%; + } + .col-sm-8 { + width: 66.66666667%; + } + .col-sm-7 { + width: 58.33333333%; + } + .col-sm-6 { + width: 50%; + } + .col-sm-5 { + width: 41.66666667%; + } + .col-sm-4 { + width: 33.33333333%; + } + .col-sm-3 { + width: 25%; + } + .col-sm-2 { + width: 16.66666667%; + } + .col-sm-1 { + width: 8.33333333%; + } + .col-sm-pull-12 { + right: 100%; + } + .col-sm-pull-11 { + right: 91.66666667%; + } + .col-sm-pull-10 { + right: 83.33333333%; + } + .col-sm-pull-9 { + right: 75%; + } + .col-sm-pull-8 { + right: 66.66666667%; + } + .col-sm-pull-7 { + right: 58.33333333%; + } + .col-sm-pull-6 { + right: 50%; + } + .col-sm-pull-5 { + right: 41.66666667%; + } + .col-sm-pull-4 { + right: 33.33333333%; + } + .col-sm-pull-3 { + right: 25%; + } + .col-sm-pull-2 { + right: 16.66666667%; + } + .col-sm-pull-1 { + right: 8.33333333%; + } + .col-sm-pull-0 { + right: auto; + } + .col-sm-push-12 { + left: 100%; + } + .col-sm-push-11 { + left: 91.66666667%; + } + .col-sm-push-10 { + left: 83.33333333%; + } + .col-sm-push-9 { + left: 75%; + } + .col-sm-push-8 { + left: 66.66666667%; + } + .col-sm-push-7 { + left: 58.33333333%; + } + .col-sm-push-6 { + left: 50%; + } + .col-sm-push-5 { + left: 41.66666667%; + } + .col-sm-push-4 { + left: 33.33333333%; + } + .col-sm-push-3 { + left: 25%; + } + .col-sm-push-2 { + left: 16.66666667%; + } + .col-sm-push-1 { + left: 8.33333333%; + } + .col-sm-push-0 { + left: auto; + } + .col-sm-offset-12 { + margin-left: 100%; + } + .col-sm-offset-11 { + margin-left: 91.66666667%; + } + .col-sm-offset-10 { + margin-left: 83.33333333%; + } + .col-sm-offset-9 { + margin-left: 75%; + } + .col-sm-offset-8 { + margin-left: 66.66666667%; + } + .col-sm-offset-7 { + margin-left: 58.33333333%; + } + .col-sm-offset-6 { + margin-left: 50%; + } + .col-sm-offset-5 { + margin-left: 41.66666667%; + } + .col-sm-offset-4 { + margin-left: 33.33333333%; + } + .col-sm-offset-3 { + margin-left: 25%; + } + .col-sm-offset-2 { + margin-left: 16.66666667%; + } + .col-sm-offset-1 { + margin-left: 8.33333333%; + } + .col-sm-offset-0 { + margin-left: 0; + } +} +@media (min-width: 992px) { + .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12 { + float: left; + } + .col-md-12 { + width: 100%; + } + .col-md-11 { + width: 91.66666667%; + } + .col-md-10 { + width: 83.33333333%; + } + .col-md-9 { + width: 75%; + } + .col-md-8 { + width: 66.66666667%; + } + .col-md-7 { + width: 58.33333333%; + } + .col-md-6 { + width: 50%; + } + .col-md-5 { + width: 41.66666667%; + } + .col-md-4 { + width: 33.33333333%; + } + .col-md-3 { + width: 25%; + } + .col-md-2 { + width: 16.66666667%; + } + .col-md-1 { + width: 8.33333333%; + } + .col-md-pull-12 { + right: 100%; + } + .col-md-pull-11 { + right: 91.66666667%; + } + .col-md-pull-10 { + right: 83.33333333%; + } + .col-md-pull-9 { + right: 75%; + } + .col-md-pull-8 { + right: 66.66666667%; + } + .col-md-pull-7 { + right: 58.33333333%; + } + .col-md-pull-6 { + right: 50%; + } + .col-md-pull-5 { + right: 41.66666667%; + } + .col-md-pull-4 { + right: 33.33333333%; + } + .col-md-pull-3 { + right: 25%; + } + .col-md-pull-2 { + right: 16.66666667%; + } + .col-md-pull-1 { + right: 8.33333333%; + } + .col-md-pull-0 { + right: auto; + } + .col-md-push-12 { + left: 100%; + } + .col-md-push-11 { + left: 91.66666667%; + } + .col-md-push-10 { + left: 83.33333333%; + } + .col-md-push-9 { + left: 75%; + } + .col-md-push-8 { + left: 66.66666667%; + } + .col-md-push-7 { + left: 58.33333333%; + } + .col-md-push-6 { + left: 50%; + } + .col-md-push-5 { + left: 41.66666667%; + } + .col-md-push-4 { + left: 33.33333333%; + } + .col-md-push-3 { + left: 25%; + } + .col-md-push-2 { + left: 16.66666667%; + } + .col-md-push-1 { + left: 8.33333333%; + } + .col-md-push-0 { + left: auto; + } + .col-md-offset-12 { + margin-left: 100%; + } + .col-md-offset-11 { + margin-left: 91.66666667%; + } + .col-md-offset-10 { + margin-left: 83.33333333%; + } + .col-md-offset-9 { + margin-left: 75%; + } + .col-md-offset-8 { + margin-left: 66.66666667%; + } + .col-md-offset-7 { + margin-left: 58.33333333%; + } + .col-md-offset-6 { + margin-left: 50%; + } + .col-md-offset-5 { + margin-left: 41.66666667%; + } + .col-md-offset-4 { + margin-left: 33.33333333%; + } + .col-md-offset-3 { + margin-left: 25%; + } + .col-md-offset-2 { + margin-left: 16.66666667%; + } + .col-md-offset-1 { + margin-left: 8.33333333%; + } + .col-md-offset-0 { + margin-left: 0; + } +} +@media (min-width: 1200px) { + .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12 { + float: left; + } + .col-lg-12 { + width: 100%; + } + .col-lg-11 { + width: 91.66666667%; + } + .col-lg-10 { + width: 83.33333333%; + } + .col-lg-9 { + width: 75%; + } + .col-lg-8 { + width: 66.66666667%; + } + .col-lg-7 { + width: 58.33333333%; + } + .col-lg-6 { + width: 50%; + } + .col-lg-5 { + width: 41.66666667%; + } + .col-lg-4 { + width: 33.33333333%; + } + .col-lg-3 { + width: 25%; + } + .col-lg-2 { + width: 16.66666667%; + } + .col-lg-1 { + width: 8.33333333%; + } + .col-lg-pull-12 { + right: 100%; + } + .col-lg-pull-11 { + right: 91.66666667%; + } + .col-lg-pull-10 { + right: 83.33333333%; + } + .col-lg-pull-9 { + right: 75%; + } + .col-lg-pull-8 { + right: 66.66666667%; + } + .col-lg-pull-7 { + right: 58.33333333%; + } + .col-lg-pull-6 { + right: 50%; + } + .col-lg-pull-5 { + right: 41.66666667%; + } + .col-lg-pull-4 { + right: 33.33333333%; + } + .col-lg-pull-3 { + right: 25%; + } + .col-lg-pull-2 { + right: 16.66666667%; + } + .col-lg-pull-1 { + right: 8.33333333%; + } + .col-lg-pull-0 { + right: auto; + } + .col-lg-push-12 { + left: 100%; + } + .col-lg-push-11 { + left: 91.66666667%; + } + .col-lg-push-10 { + left: 83.33333333%; + } + .col-lg-push-9 { + left: 75%; + } + .col-lg-push-8 { + left: 66.66666667%; + } + .col-lg-push-7 { + left: 58.33333333%; + } + .col-lg-push-6 { + left: 50%; + } + .col-lg-push-5 { + left: 41.66666667%; + } + .col-lg-push-4 { + left: 33.33333333%; + } + .col-lg-push-3 { + left: 25%; + } + .col-lg-push-2 { + left: 16.66666667%; + } + .col-lg-push-1 { + left: 8.33333333%; + } + .col-lg-push-0 { + left: auto; + } + .col-lg-offset-12 { + margin-left: 100%; + } + .col-lg-offset-11 { + margin-left: 91.66666667%; + } + .col-lg-offset-10 { + margin-left: 83.33333333%; + } + .col-lg-offset-9 { + margin-left: 75%; + } + .col-lg-offset-8 { + margin-left: 66.66666667%; + } + .col-lg-offset-7 { + margin-left: 58.33333333%; + } + .col-lg-offset-6 { + margin-left: 50%; + } + .col-lg-offset-5 { + margin-left: 41.66666667%; + } + .col-lg-offset-4 { + margin-left: 33.33333333%; + } + .col-lg-offset-3 { + margin-left: 25%; + } + .col-lg-offset-2 { + margin-left: 16.66666667%; + } + .col-lg-offset-1 { + margin-left: 8.33333333%; + } + .col-lg-offset-0 { + margin-left: 0; + } +} +table { + background-color: transparent; +} +caption { + padding-top: 8px; + padding-bottom: 8px; + color: #777; + text-align: left; +} +th { + text-align: left; +} +.table { + width: 100%; + max-width: 100%; + margin-bottom: 20px; +} +.table > thead > tr > th, +.table > tbody > tr > th, +.table > tfoot > tr > th, +.table > thead > tr > td, +.table > tbody > tr > td, +.table > tfoot > tr > td { + padding: 8px; + line-height: 1.42857143; + vertical-align: top; + border-top: 1px solid #ddd; +} +.table > thead > tr > th { + vertical-align: bottom; + border-bottom: 2px solid #ddd; +} +.table > caption + thead > tr:first-child > th, +.table > colgroup + thead > tr:first-child > th, +.table > thead:first-child > tr:first-child > th, +.table > caption + thead > tr:first-child > td, +.table > colgroup + thead > tr:first-child > td, +.table > thead:first-child > tr:first-child > td { + border-top: 0; +} +.table > tbody + tbody { + border-top: 2px solid #ddd; +} +.table .table { + background-color: #fff; +} +.table-condensed > thead > tr > th, +.table-condensed > tbody > tr > th, +.table-condensed > tfoot > tr > th, +.table-condensed > thead > tr > td, +.table-condensed > tbody > tr > td, +.table-condensed > tfoot > tr > td { + padding: 5px; +} +.table-bordered { + border: 1px solid #ddd; +} +.table-bordered > thead > tr > th, +.table-bordered > tbody > tr > th, +.table-bordered > tfoot > tr > th, +.table-bordered > thead > tr > td, +.table-bordered > tbody > tr > td, +.table-bordered > tfoot > tr > td { + border: 1px solid #ddd; +} +.table-bordered > thead > tr > th, +.table-bordered > thead > tr > td { + border-bottom-width: 2px; +} +.table-striped > tbody > tr:nth-of-type(odd) { + background-color: #f9f9f9; +} +.table-hover > tbody > tr:hover { + background-color: #f5f5f5; +} +table col[class*="col-"] { + position: static; + display: table-column; + float: none; +} +table td[class*="col-"], +table th[class*="col-"] { + position: static; + display: table-cell; + float: none; +} +.table > thead > tr > td.active, +.table > tbody > tr > td.active, +.table > tfoot > tr > td.active, +.table > thead > tr > th.active, +.table > tbody > tr > th.active, +.table > tfoot > tr > th.active, +.table > thead > tr.active > td, +.table > tbody > tr.active > td, +.table > tfoot > tr.active > td, +.table > thead > tr.active > th, +.table > tbody > tr.active > th, +.table > tfoot > tr.active > th { + background-color: #f5f5f5; +} +.table-hover > tbody > tr > td.active:hover, +.table-hover > tbody > tr > th.active:hover, +.table-hover > tbody > tr.active:hover > td, +.table-hover > tbody > tr:hover > .active, +.table-hover > tbody > tr.active:hover > th { + background-color: #e8e8e8; +} +.table > thead > tr > td.success, +.table > tbody > tr > td.success, +.table > tfoot > tr > td.success, +.table > thead > tr > th.success, +.table > tbody > tr > th.success, +.table > tfoot > tr > th.success, +.table > thead > tr.success > td, +.table > tbody > tr.success > td, +.table > tfoot > tr.success > td, +.table > thead > tr.success > th, +.table > tbody > tr.success > th, +.table > tfoot > tr.success > th { + background-color: #dff0d8; +} +.table-hover > tbody > tr > td.success:hover, +.table-hover > tbody > tr > th.success:hover, +.table-hover > tbody > tr.success:hover > td, +.table-hover > tbody > tr:hover > .success, +.table-hover > tbody > tr.success:hover > th { + background-color: #d0e9c6; +} +.table > thead > tr > td.info, +.table > tbody > tr > td.info, +.table > tfoot > tr > td.info, +.table > thead > tr > th.info, +.table > tbody > tr > th.info, +.table > tfoot > tr > th.info, +.table > thead > tr.info > td, +.table > tbody > tr.info > td, +.table > tfoot > tr.info > td, +.table > thead > tr.info > th, +.table > tbody > tr.info > th, +.table > tfoot > tr.info > th { + background-color: #d9edf7; +} +.table-hover > tbody > tr > td.info:hover, +.table-hover > tbody > tr > th.info:hover, +.table-hover > tbody > tr.info:hover > td, +.table-hover > tbody > tr:hover > .info, +.table-hover > tbody > tr.info:hover > th { + background-color: #c4e3f3; +} +.table > thead > tr > td.warning, +.table > tbody > tr > td.warning, +.table > tfoot > tr > td.warning, +.table > thead > tr > th.warning, +.table > tbody > tr > th.warning, +.table > tfoot > tr > th.warning, +.table > thead > tr.warning > td, +.table > tbody > tr.warning > td, +.table > tfoot > tr.warning > td, +.table > thead > tr.warning > th, +.table > tbody > tr.warning > th, +.table > tfoot > tr.warning > th { + background-color: #fcf8e3; +} +.table-hover > tbody > tr > td.warning:hover, +.table-hover > tbody > tr > th.warning:hover, +.table-hover > tbody > tr.warning:hover > td, +.table-hover > tbody > tr:hover > .warning, +.table-hover > tbody > tr.warning:hover > th { + background-color: #faf2cc; +} +.table > thead > tr > td.danger, +.table > tbody > tr > td.danger, +.table > tfoot > tr > td.danger, +.table > thead > tr > th.danger, +.table > tbody > tr > th.danger, +.table > tfoot > tr > th.danger, +.table > thead > tr.danger > td, +.table > tbody > tr.danger > td, +.table > tfoot > tr.danger > td, +.table > thead > tr.danger > th, +.table > tbody > tr.danger > th, +.table > tfoot > tr.danger > th { + background-color: #f2dede; +} +.table-hover > tbody > tr > td.danger:hover, +.table-hover > tbody > tr > th.danger:hover, +.table-hover > tbody > tr.danger:hover > td, +.table-hover > tbody > tr:hover > .danger, +.table-hover > tbody > tr.danger:hover > th { + background-color: #ebcccc; +} +.table-responsive { + min-height: .01%; + overflow-x: auto; +} +@media screen and (max-width: 767px) { + .table-responsive { + width: 100%; + margin-bottom: 15px; + overflow-y: hidden; + -ms-overflow-style: -ms-autohiding-scrollbar; + border: 1px solid #ddd; + } + .table-responsive > .table { + margin-bottom: 0; + } + .table-responsive > .table > thead > tr > th, + .table-responsive > .table > tbody > tr > th, + .table-responsive > .table > tfoot > tr > th, + .table-responsive > .table > thead > tr > td, + .table-responsive > .table > tbody > tr > td, + .table-responsive > .table > tfoot > tr > td { + white-space: nowrap; + } + .table-responsive > .table-bordered { + border: 0; + } + .table-responsive > .table-bordered > thead > tr > th:first-child, + .table-responsive > .table-bordered > tbody > tr > th:first-child, + .table-responsive > .table-bordered > tfoot > tr > th:first-child, + .table-responsive > .table-bordered > thead > tr > td:first-child, + .table-responsive > .table-bordered > tbody > tr > td:first-child, + .table-responsive > .table-bordered > tfoot > tr > td:first-child { + border-left: 0; + } + .table-responsive > .table-bordered > thead > tr > th:last-child, + .table-responsive > .table-bordered > tbody > tr > th:last-child, + .table-responsive > .table-bordered > tfoot > tr > th:last-child, + .table-responsive > .table-bordered > thead > tr > td:last-child, + .table-responsive > .table-bordered > tbody > tr > td:last-child, + .table-responsive > .table-bordered > tfoot > tr > td:last-child { + border-right: 0; + } + .table-responsive > .table-bordered > tbody > tr:last-child > th, + .table-responsive > .table-bordered > tfoot > tr:last-child > th, + .table-responsive > .table-bordered > tbody > tr:last-child > td, + .table-responsive > .table-bordered > tfoot > tr:last-child > td { + border-bottom: 0; + } +} +fieldset { + min-width: 0; + padding: 0; + margin: 0; + border: 0; +} +legend { + display: block; + width: 100%; + padding: 0; + margin-bottom: 20px; + font-size: 21px; + line-height: inherit; + color: #333; + border: 0; + border-bottom: 1px solid #e5e5e5; +} +label { + display: inline-block; + max-width: 100%; + margin-bottom: 5px; + font-weight: bold; +} +input[type="search"] { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +input[type="radio"], +input[type="checkbox"] { + margin: 4px 0 0; + margin-top: 1px \9; + line-height: normal; +} +input[type="file"] { + display: block; +} +input[type="range"] { + display: block; + width: 100%; +} +select[multiple], +select[size] { + height: auto; +} +input[type="file"]:focus, +input[type="radio"]:focus, +input[type="checkbox"]:focus { + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +output { + display: block; + padding-top: 7px; + font-size: 14px; + line-height: 1.42857143; + color: #555; +} +.form-control { + display: block; + width: 100%; + height: 34px; + padding: 6px 12px; + font-size: 14px; + line-height: 1.42857143; + color: #555; + background-color: #fff; + background-image: none; + border: 1px solid #ccc; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + -webkit-transition: border-color ease-in-out .15s, -webkit-box-shadow ease-in-out .15s; + -o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; + transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; +} +.form-control:focus { + border-color: #66afe9; + outline: 0; + -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6); + box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6); +} +.form-control::-moz-placeholder { + color: #999; + opacity: 1; +} +.form-control:-ms-input-placeholder { + color: #999; +} +.form-control::-webkit-input-placeholder { + color: #999; +} +.form-control::-ms-expand { + background-color: transparent; + border: 0; +} +.form-control[disabled], +.form-control[readonly], +fieldset[disabled] .form-control { + background-color: #eee; + opacity: 1; +} +.form-control[disabled], +fieldset[disabled] .form-control { + cursor: not-allowed; +} +textarea.form-control { + height: auto; +} +input[type="search"] { + -webkit-appearance: none; +} +@media screen and (-webkit-min-device-pixel-ratio: 0) { + input[type="date"].form-control, + input[type="time"].form-control, + input[type="datetime-local"].form-control, + input[type="month"].form-control { + line-height: 34px; + } + input[type="date"].input-sm, + input[type="time"].input-sm, + input[type="datetime-local"].input-sm, + input[type="month"].input-sm, + .input-group-sm input[type="date"], + .input-group-sm input[type="time"], + .input-group-sm input[type="datetime-local"], + .input-group-sm input[type="month"] { + line-height: 30px; + } + input[type="date"].input-lg, + input[type="time"].input-lg, + input[type="datetime-local"].input-lg, + input[type="month"].input-lg, + .input-group-lg input[type="date"], + .input-group-lg input[type="time"], + .input-group-lg input[type="datetime-local"], + .input-group-lg input[type="month"] { + line-height: 46px; + } +} +.form-group { + margin-bottom: 15px; +} +.radio, +.checkbox { + position: relative; + display: block; + margin-top: 10px; + margin-bottom: 10px; +} +.radio label, +.checkbox label { + min-height: 20px; + padding-left: 20px; + margin-bottom: 0; + font-weight: normal; + cursor: pointer; +} +.radio input[type="radio"], +.radio-inline input[type="radio"], +.checkbox input[type="checkbox"], +.checkbox-inline input[type="checkbox"] { + position: absolute; + margin-top: 4px \9; + margin-left: -20px; +} +.radio + .radio, +.checkbox + .checkbox { + margin-top: -5px; +} +.radio-inline, +.checkbox-inline { + position: relative; + display: inline-block; + padding-left: 20px; + margin-bottom: 0; + font-weight: normal; + vertical-align: middle; + cursor: pointer; +} +.radio-inline + .radio-inline, +.checkbox-inline + .checkbox-inline { + margin-top: 0; + margin-left: 10px; +} +input[type="radio"][disabled], +input[type="checkbox"][disabled], +input[type="radio"].disabled, +input[type="checkbox"].disabled, +fieldset[disabled] input[type="radio"], +fieldset[disabled] input[type="checkbox"] { + cursor: not-allowed; +} +.radio-inline.disabled, +.checkbox-inline.disabled, +fieldset[disabled] .radio-inline, +fieldset[disabled] .checkbox-inline { + cursor: not-allowed; +} +.radio.disabled label, +.checkbox.disabled label, +fieldset[disabled] .radio label, +fieldset[disabled] .checkbox label { + cursor: not-allowed; +} +.form-control-static { + min-height: 34px; + padding-top: 7px; + padding-bottom: 7px; + margin-bottom: 0; +} +.form-control-static.input-lg, +.form-control-static.input-sm { + padding-right: 0; + padding-left: 0; +} +.input-sm { + height: 30px; + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +select.input-sm { + height: 30px; + line-height: 30px; +} +textarea.input-sm, +select[multiple].input-sm { + height: auto; +} +.form-group-sm .form-control { + height: 30px; + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +.form-group-sm select.form-control { + height: 30px; + line-height: 30px; +} +.form-group-sm textarea.form-control, +.form-group-sm select[multiple].form-control { + height: auto; +} +.form-group-sm .form-control-static { + height: 30px; + min-height: 32px; + padding: 6px 10px; + font-size: 12px; + line-height: 1.5; +} +.input-lg { + height: 46px; + padding: 10px 16px; + font-size: 18px; + line-height: 1.3333333; + border-radius: 6px; +} +select.input-lg { + height: 46px; + line-height: 46px; +} +textarea.input-lg, +select[multiple].input-lg { + height: auto; +} +.form-group-lg .form-control { + height: 46px; + padding: 10px 16px; + font-size: 18px; + line-height: 1.3333333; + border-radius: 6px; +} +.form-group-lg select.form-control { + height: 46px; + line-height: 46px; +} +.form-group-lg textarea.form-control, +.form-group-lg select[multiple].form-control { + height: auto; +} +.form-group-lg .form-control-static { + height: 46px; + min-height: 38px; + padding: 11px 16px; + font-size: 18px; + line-height: 1.3333333; +} +.has-feedback { + position: relative; +} +.has-feedback .form-control { + padding-right: 42.5px; +} +.form-control-feedback { + position: absolute; + top: 0; + right: 0; + z-index: 2; + display: block; + width: 34px; + height: 34px; + line-height: 34px; + text-align: center; + pointer-events: none; +} +.input-lg + .form-control-feedback, +.input-group-lg + .form-control-feedback, +.form-group-lg .form-control + .form-control-feedback { + width: 46px; + height: 46px; + line-height: 46px; +} +.input-sm + .form-control-feedback, +.input-group-sm + .form-control-feedback, +.form-group-sm .form-control + .form-control-feedback { + width: 30px; + height: 30px; + line-height: 30px; +} +.has-success .help-block, +.has-success .control-label, +.has-success .radio, +.has-success .checkbox, +.has-success .radio-inline, +.has-success .checkbox-inline, +.has-success.radio label, +.has-success.checkbox label, +.has-success.radio-inline label, +.has-success.checkbox-inline label { + color: #3c763d; +} +.has-success .form-control { + border-color: #3c763d; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); +} +.has-success .form-control:focus { + border-color: #2b542c; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #67b168; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #67b168; +} +.has-success .input-group-addon { + color: #3c763d; + background-color: #dff0d8; + border-color: #3c763d; +} +.has-success .form-control-feedback { + color: #3c763d; +} +.has-warning .help-block, +.has-warning .control-label, +.has-warning .radio, +.has-warning .checkbox, +.has-warning .radio-inline, +.has-warning .checkbox-inline, +.has-warning.radio label, +.has-warning.checkbox label, +.has-warning.radio-inline label, +.has-warning.checkbox-inline label { + color: #8a6d3b; +} +.has-warning .form-control { + border-color: #8a6d3b; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); +} +.has-warning .form-control:focus { + border-color: #66512c; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #c0a16b; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #c0a16b; +} +.has-warning .input-group-addon { + color: #8a6d3b; + background-color: #fcf8e3; + border-color: #8a6d3b; +} +.has-warning .form-control-feedback { + color: #8a6d3b; +} +.has-error .help-block, +.has-error .control-label, +.has-error .radio, +.has-error .checkbox, +.has-error .radio-inline, +.has-error .checkbox-inline, +.has-error.radio label, +.has-error.checkbox label, +.has-error.radio-inline label, +.has-error.checkbox-inline label { + color: #a94442; +} +.has-error .form-control { + border-color: #a94442; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); +} +.has-error .form-control:focus { + border-color: #843534; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #ce8483; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #ce8483; +} +.has-error .input-group-addon { + color: #a94442; + background-color: #f2dede; + border-color: #a94442; +} +.has-error .form-control-feedback { + color: #a94442; +} +.has-feedback label ~ .form-control-feedback { + top: 25px; +} +.has-feedback label.sr-only ~ .form-control-feedback { + top: 0; +} +.help-block { + display: block; + margin-top: 5px; + margin-bottom: 10px; + color: #737373; +} +@media (min-width: 768px) { + .form-inline .form-group { + display: inline-block; + margin-bottom: 0; + vertical-align: middle; + } + .form-inline .form-control { + display: inline-block; + width: auto; + vertical-align: middle; + } + .form-inline .form-control-static { + display: inline-block; + } + .form-inline .input-group { + display: inline-table; + vertical-align: middle; + } + .form-inline .input-group .input-group-addon, + .form-inline .input-group .input-group-btn, + .form-inline .input-group .form-control { + width: auto; + } + .form-inline .input-group > .form-control { + width: 100%; + } + .form-inline .control-label { + margin-bottom: 0; + vertical-align: middle; + } + .form-inline .radio, + .form-inline .checkbox { + display: inline-block; + margin-top: 0; + margin-bottom: 0; + vertical-align: middle; + } + .form-inline .radio label, + .form-inline .checkbox label { + padding-left: 0; + } + .form-inline .radio input[type="radio"], + .form-inline .checkbox input[type="checkbox"] { + position: relative; + margin-left: 0; + } + .form-inline .has-feedback .form-control-feedback { + top: 0; + } +} +.form-horizontal .radio, +.form-horizontal .checkbox, +.form-horizontal .radio-inline, +.form-horizontal .checkbox-inline { + padding-top: 7px; + margin-top: 0; + margin-bottom: 0; +} +.form-horizontal .radio, +.form-horizontal .checkbox { + min-height: 27px; +} +.form-horizontal .form-group { + margin-right: -15px; + margin-left: -15px; +} +@media (min-width: 768px) { + .form-horizontal .control-label { + padding-top: 7px; + margin-bottom: 0; + text-align: right; + } +} +.form-horizontal .has-feedback .form-control-feedback { + right: 15px; +} +@media (min-width: 768px) { + .form-horizontal .form-group-lg .control-label { + padding-top: 11px; + font-size: 18px; + } +} +@media (min-width: 768px) { + .form-horizontal .form-group-sm .control-label { + padding-top: 6px; + font-size: 12px; + } +} +.btn { + display: inline-block; + padding: 6px 12px; + margin-bottom: 0; + font-size: 14px; + font-weight: normal; + line-height: 1.42857143; + text-align: center; + white-space: nowrap; + vertical-align: middle; + -ms-touch-action: manipulation; + touch-action: manipulation; + cursor: pointer; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + background-image: none; + border: 1px solid transparent; + border-radius: 4px; +} +.btn:focus, +.btn:active:focus, +.btn.active:focus, +.btn.focus, +.btn:active.focus, +.btn.active.focus { + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +.btn:hover, +.btn:focus, +.btn.focus { + color: #333; + text-decoration: none; +} +.btn:active, +.btn.active { + background-image: none; + outline: 0; + -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); + box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); +} +.btn.disabled, +.btn[disabled], +fieldset[disabled] .btn { + cursor: not-allowed; + filter: alpha(opacity=65); + -webkit-box-shadow: none; + box-shadow: none; + opacity: .65; +} +a.btn.disabled, +fieldset[disabled] a.btn { + pointer-events: none; +} +.btn-default { + color: #333; + background-color: #fff; + border-color: #ccc; +} +.btn-default:focus, +.btn-default.focus { + color: #333; + background-color: #e6e6e6; + border-color: #8c8c8c; +} +.btn-default:hover { + color: #333; + background-color: #e6e6e6; + border-color: #adadad; +} +.btn-default:active, +.btn-default.active, +.open > .dropdown-toggle.btn-default { + color: #333; + background-color: #e6e6e6; + border-color: #adadad; +} +.btn-default:active:hover, +.btn-default.active:hover, +.open > .dropdown-toggle.btn-default:hover, +.btn-default:active:focus, +.btn-default.active:focus, +.open > .dropdown-toggle.btn-default:focus, +.btn-default:active.focus, +.btn-default.active.focus, +.open > .dropdown-toggle.btn-default.focus { + color: #333; + background-color: #d4d4d4; + border-color: #8c8c8c; +} +.btn-default:active, +.btn-default.active, +.open > .dropdown-toggle.btn-default { + background-image: none; +} +.btn-default.disabled:hover, +.btn-default[disabled]:hover, +fieldset[disabled] .btn-default:hover, +.btn-default.disabled:focus, +.btn-default[disabled]:focus, +fieldset[disabled] .btn-default:focus, +.btn-default.disabled.focus, +.btn-default[disabled].focus, +fieldset[disabled] .btn-default.focus { + background-color: #fff; + border-color: #ccc; +} +.btn-default .badge { + color: #fff; + background-color: #333; +} +.btn-primary { + color: #fff; + background-color: #337ab7; + border-color: #2e6da4; +} +.btn-primary:focus, +.btn-primary.focus { + color: #fff; + background-color: #286090; + border-color: #122b40; +} +.btn-primary:hover { + color: #fff; + background-color: #286090; + border-color: #204d74; +} +.btn-primary:active, +.btn-primary.active, +.open > .dropdown-toggle.btn-primary { + color: #fff; + background-color: #286090; + border-color: #204d74; +} +.btn-primary:active:hover, +.btn-primary.active:hover, +.open > .dropdown-toggle.btn-primary:hover, +.btn-primary:active:focus, +.btn-primary.active:focus, +.open > .dropdown-toggle.btn-primary:focus, +.btn-primary:active.focus, +.btn-primary.active.focus, +.open > .dropdown-toggle.btn-primary.focus { + color: #fff; + background-color: #204d74; + border-color: #122b40; +} +.btn-primary:active, +.btn-primary.active, +.open > .dropdown-toggle.btn-primary { + background-image: none; +} +.btn-primary.disabled:hover, +.btn-primary[disabled]:hover, +fieldset[disabled] .btn-primary:hover, +.btn-primary.disabled:focus, +.btn-primary[disabled]:focus, +fieldset[disabled] .btn-primary:focus, +.btn-primary.disabled.focus, +.btn-primary[disabled].focus, +fieldset[disabled] .btn-primary.focus { + background-color: #337ab7; + border-color: #2e6da4; +} +.btn-primary .badge { + color: #337ab7; + background-color: #fff; +} +.btn-success { + color: #fff; + background-color: #5cb85c; + border-color: #4cae4c; +} +.btn-success:focus, +.btn-success.focus { + color: #fff; + background-color: #449d44; + border-color: #255625; +} +.btn-success:hover { + color: #fff; + background-color: #449d44; + border-color: #398439; +} +.btn-success:active, +.btn-success.active, +.open > .dropdown-toggle.btn-success { + color: #fff; + background-color: #449d44; + border-color: #398439; +} +.btn-success:active:hover, +.btn-success.active:hover, +.open > .dropdown-toggle.btn-success:hover, +.btn-success:active:focus, +.btn-success.active:focus, +.open > .dropdown-toggle.btn-success:focus, +.btn-success:active.focus, +.btn-success.active.focus, +.open > .dropdown-toggle.btn-success.focus { + color: #fff; + background-color: #398439; + border-color: #255625; +} +.btn-success:active, +.btn-success.active, +.open > .dropdown-toggle.btn-success { + background-image: none; +} +.btn-success.disabled:hover, +.btn-success[disabled]:hover, +fieldset[disabled] .btn-success:hover, +.btn-success.disabled:focus, +.btn-success[disabled]:focus, +fieldset[disabled] .btn-success:focus, +.btn-success.disabled.focus, +.btn-success[disabled].focus, +fieldset[disabled] .btn-success.focus { + background-color: #5cb85c; + border-color: #4cae4c; +} +.btn-success .badge { + color: #5cb85c; + background-color: #fff; +} +.btn-info { + color: #fff; + background-color: #5bc0de; + border-color: #46b8da; +} +.btn-info:focus, +.btn-info.focus { + color: #fff; + background-color: #31b0d5; + border-color: #1b6d85; +} +.btn-info:hover { + color: #fff; + background-color: #31b0d5; + border-color: #269abc; +} +.btn-info:active, +.btn-info.active, +.open > .dropdown-toggle.btn-info { + color: #fff; + background-color: #31b0d5; + border-color: #269abc; +} +.btn-info:active:hover, +.btn-info.active:hover, +.open > .dropdown-toggle.btn-info:hover, +.btn-info:active:focus, +.btn-info.active:focus, +.open > .dropdown-toggle.btn-info:focus, +.btn-info:active.focus, +.btn-info.active.focus, +.open > .dropdown-toggle.btn-info.focus { + color: #fff; + background-color: #269abc; + border-color: #1b6d85; +} +.btn-info:active, +.btn-info.active, +.open > .dropdown-toggle.btn-info { + background-image: none; +} +.btn-info.disabled:hover, +.btn-info[disabled]:hover, +fieldset[disabled] .btn-info:hover, +.btn-info.disabled:focus, +.btn-info[disabled]:focus, +fieldset[disabled] .btn-info:focus, +.btn-info.disabled.focus, +.btn-info[disabled].focus, +fieldset[disabled] .btn-info.focus { + background-color: #5bc0de; + border-color: #46b8da; +} +.btn-info .badge { + color: #5bc0de; + background-color: #fff; +} +.btn-warning { + color: #fff; + background-color: #f0ad4e; + border-color: #eea236; +} +.btn-warning:focus, +.btn-warning.focus { + color: #fff; + background-color: #ec971f; + border-color: #985f0d; +} +.btn-warning:hover { + color: #fff; + background-color: #ec971f; + border-color: #d58512; +} +.btn-warning:active, +.btn-warning.active, +.open > .dropdown-toggle.btn-warning { + color: #fff; + background-color: #ec971f; + border-color: #d58512; +} +.btn-warning:active:hover, +.btn-warning.active:hover, +.open > .dropdown-toggle.btn-warning:hover, +.btn-warning:active:focus, +.btn-warning.active:focus, +.open > .dropdown-toggle.btn-warning:focus, +.btn-warning:active.focus, +.btn-warning.active.focus, +.open > .dropdown-toggle.btn-warning.focus { + color: #fff; + background-color: #d58512; + border-color: #985f0d; +} +.btn-warning:active, +.btn-warning.active, +.open > .dropdown-toggle.btn-warning { + background-image: none; +} +.btn-warning.disabled:hover, +.btn-warning[disabled]:hover, +fieldset[disabled] .btn-warning:hover, +.btn-warning.disabled:focus, +.btn-warning[disabled]:focus, +fieldset[disabled] .btn-warning:focus, +.btn-warning.disabled.focus, +.btn-warning[disabled].focus, +fieldset[disabled] .btn-warning.focus { + background-color: #f0ad4e; + border-color: #eea236; +} +.btn-warning .badge { + color: #f0ad4e; + background-color: #fff; +} +.btn-danger { + color: #fff; + background-color: #d9534f; + border-color: #d43f3a; +} +.btn-danger:focus, +.btn-danger.focus { + color: #fff; + background-color: #c9302c; + border-color: #761c19; +} +.btn-danger:hover { + color: #fff; + background-color: #c9302c; + border-color: #ac2925; +} +.btn-danger:active, +.btn-danger.active, +.open > .dropdown-toggle.btn-danger { + color: #fff; + background-color: #c9302c; + border-color: #ac2925; +} +.btn-danger:active:hover, +.btn-danger.active:hover, +.open > .dropdown-toggle.btn-danger:hover, +.btn-danger:active:focus, +.btn-danger.active:focus, +.open > .dropdown-toggle.btn-danger:focus, +.btn-danger:active.focus, +.btn-danger.active.focus, +.open > .dropdown-toggle.btn-danger.focus { + color: #fff; + background-color: #ac2925; + border-color: #761c19; +} +.btn-danger:active, +.btn-danger.active, +.open > .dropdown-toggle.btn-danger { + background-image: none; +} +.btn-danger.disabled:hover, +.btn-danger[disabled]:hover, +fieldset[disabled] .btn-danger:hover, +.btn-danger.disabled:focus, +.btn-danger[disabled]:focus, +fieldset[disabled] .btn-danger:focus, +.btn-danger.disabled.focus, +.btn-danger[disabled].focus, +fieldset[disabled] .btn-danger.focus { + background-color: #d9534f; + border-color: #d43f3a; +} +.btn-danger .badge { + color: #d9534f; + background-color: #fff; +} +.btn-link { + font-weight: normal; + color: #337ab7; + border-radius: 0; +} +.btn-link, +.btn-link:active, +.btn-link.active, +.btn-link[disabled], +fieldset[disabled] .btn-link { + background-color: transparent; + -webkit-box-shadow: none; + box-shadow: none; +} +.btn-link, +.btn-link:hover, +.btn-link:focus, +.btn-link:active { + border-color: transparent; +} +.btn-link:hover, +.btn-link:focus { + color: #23527c; + text-decoration: underline; + background-color: transparent; +} +.btn-link[disabled]:hover, +fieldset[disabled] .btn-link:hover, +.btn-link[disabled]:focus, +fieldset[disabled] .btn-link:focus { + color: #777; + text-decoration: none; +} +.btn-lg, +.btn-group-lg > .btn { + padding: 10px 16px; + font-size: 18px; + line-height: 1.3333333; + border-radius: 6px; +} +.btn-sm, +.btn-group-sm > .btn { + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +.btn-xs, +.btn-group-xs > .btn { + padding: 1px 5px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +.btn-block { + display: block; + width: 100%; +} +.btn-block + .btn-block { + margin-top: 5px; +} +input[type="submit"].btn-block, +input[type="reset"].btn-block, +input[type="button"].btn-block { + width: 100%; +} +.fade { + opacity: 0; + -webkit-transition: opacity .15s linear; + -o-transition: opacity .15s linear; + transition: opacity .15s linear; +} +.fade.in { + opacity: 1; +} +.collapse { + display: none; +} +.collapse.in { + display: block; +} +tr.collapse.in { + display: table-row; +} +tbody.collapse.in { + display: table-row-group; +} +.collapsing { + position: relative; + height: 0; + overflow: hidden; + -webkit-transition-timing-function: ease; + -o-transition-timing-function: ease; + transition-timing-function: ease; + -webkit-transition-duration: .35s; + -o-transition-duration: .35s; + transition-duration: .35s; + -webkit-transition-property: height, visibility; + -o-transition-property: height, visibility; + transition-property: height, visibility; +} +.caret { + display: inline-block; + width: 0; + height: 0; + margin-left: 2px; + vertical-align: middle; + border-top: 4px dashed; + border-top: 4px solid \9; + border-right: 4px solid transparent; + border-left: 4px solid transparent; +} +.dropup, +.dropdown { + position: relative; +} +.dropdown-toggle:focus { + outline: 0; +} +.dropdown-menu { + position: absolute; + top: 100%; + left: 0; + z-index: 1000; + display: none; + float: left; + min-width: 160px; + padding: 5px 0; + margin: 2px 0 0; + font-size: 14px; + text-align: left; + list-style: none; + background-color: #fff; + -webkit-background-clip: padding-box; + background-clip: padding-box; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, .15); + border-radius: 4px; + -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, .175); + box-shadow: 0 6px 12px rgba(0, 0, 0, .175); +} +.dropdown-menu.pull-right { + right: 0; + left: auto; +} +.dropdown-menu .divider { + height: 1px; + margin: 9px 0; + overflow: hidden; + background-color: #e5e5e5; +} +.dropdown-menu > li > a { + display: block; + padding: 3px 20px; + clear: both; + font-weight: normal; + line-height: 1.42857143; + color: #333; + white-space: nowrap; +} +.dropdown-menu > li > a:hover, +.dropdown-menu > li > a:focus { + color: #262626; + text-decoration: none; + background-color: #f5f5f5; +} +.dropdown-menu > .active > a, +.dropdown-menu > .active > a:hover, +.dropdown-menu > .active > a:focus { + color: #fff; + text-decoration: none; + background-color: #337ab7; + outline: 0; +} +.dropdown-menu > .disabled > a, +.dropdown-menu > .disabled > a:hover, +.dropdown-menu > .disabled > a:focus { + color: #777; +} +.dropdown-menu > .disabled > a:hover, +.dropdown-menu > .disabled > a:focus { + text-decoration: none; + cursor: not-allowed; + background-color: transparent; + background-image: none; + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); +} +.open > .dropdown-menu { + display: block; +} +.open > a { + outline: 0; +} +.dropdown-menu-right { + right: 0; + left: auto; +} +.dropdown-menu-left { + right: auto; + left: 0; +} +.dropdown-header { + display: block; + padding: 3px 20px; + font-size: 12px; + line-height: 1.42857143; + color: #777; + white-space: nowrap; +} +.dropdown-backdrop { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 990; +} +.pull-right > .dropdown-menu { + right: 0; + left: auto; +} +.dropup .caret, +.navbar-fixed-bottom .dropdown .caret { + content: ""; + border-top: 0; + border-bottom: 4px dashed; + border-bottom: 4px solid \9; +} +.dropup .dropdown-menu, +.navbar-fixed-bottom .dropdown .dropdown-menu { + top: auto; + bottom: 100%; + margin-bottom: 2px; +} +@media (min-width: 768px) { + .navbar-right .dropdown-menu { + right: 0; + left: auto; + } + .navbar-right .dropdown-menu-left { + right: auto; + left: 0; + } +} +.btn-group, +.btn-group-vertical { + position: relative; + display: inline-block; + vertical-align: middle; +} +.btn-group > .btn, +.btn-group-vertical > .btn { + position: relative; + float: left; +} +.btn-group > .btn:hover, +.btn-group-vertical > .btn:hover, +.btn-group > .btn:focus, +.btn-group-vertical > .btn:focus, +.btn-group > .btn:active, +.btn-group-vertical > .btn:active, +.btn-group > .btn.active, +.btn-group-vertical > .btn.active { + z-index: 2; +} +.btn-group .btn + .btn, +.btn-group .btn + .btn-group, +.btn-group .btn-group + .btn, +.btn-group .btn-group + .btn-group { + margin-left: -1px; +} +.btn-toolbar { + margin-left: -5px; +} +.btn-toolbar .btn, +.btn-toolbar .btn-group, +.btn-toolbar .input-group { + float: left; +} +.btn-toolbar > .btn, +.btn-toolbar > .btn-group, +.btn-toolbar > .input-group { + margin-left: 5px; +} +.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) { + border-radius: 0; +} +.btn-group > .btn:first-child { + margin-left: 0; +} +.btn-group > .btn:first-child:not(:last-child):not(.dropdown-toggle) { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} +.btn-group > .btn:last-child:not(:first-child), +.btn-group > .dropdown-toggle:not(:first-child) { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} +.btn-group > .btn-group { + float: left; +} +.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn { + border-radius: 0; +} +.btn-group > .btn-group:first-child:not(:last-child) > .btn:last-child, +.btn-group > .btn-group:first-child:not(:last-child) > .dropdown-toggle { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} +.btn-group > .btn-group:last-child:not(:first-child) > .btn:first-child { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} +.btn-group .dropdown-toggle:active, +.btn-group.open .dropdown-toggle { + outline: 0; +} +.btn-group > .btn + .dropdown-toggle { + padding-right: 8px; + padding-left: 8px; +} +.btn-group > .btn-lg + .dropdown-toggle { + padding-right: 12px; + padding-left: 12px; +} +.btn-group.open .dropdown-toggle { + -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); + box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); +} +.btn-group.open .dropdown-toggle.btn-link { + -webkit-box-shadow: none; + box-shadow: none; +} +.btn .caret { + margin-left: 0; +} +.btn-lg .caret { + border-width: 5px 5px 0; + border-bottom-width: 0; +} +.dropup .btn-lg .caret { + border-width: 0 5px 5px; +} +.btn-group-vertical > .btn, +.btn-group-vertical > .btn-group, +.btn-group-vertical > .btn-group > .btn { + display: block; + float: none; + width: 100%; + max-width: 100%; +} +.btn-group-vertical > .btn-group > .btn { + float: none; +} +.btn-group-vertical > .btn + .btn, +.btn-group-vertical > .btn + .btn-group, +.btn-group-vertical > .btn-group + .btn, +.btn-group-vertical > .btn-group + .btn-group { + margin-top: -1px; + margin-left: 0; +} +.btn-group-vertical > .btn:not(:first-child):not(:last-child) { + border-radius: 0; +} +.btn-group-vertical > .btn:first-child:not(:last-child) { + border-top-left-radius: 4px; + border-top-right-radius: 4px; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} +.btn-group-vertical > .btn:last-child:not(:first-child) { + border-top-left-radius: 0; + border-top-right-radius: 0; + border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px; +} +.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn { + border-radius: 0; +} +.btn-group-vertical > .btn-group:first-child:not(:last-child) > .btn:last-child, +.btn-group-vertical > .btn-group:first-child:not(:last-child) > .dropdown-toggle { + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} +.btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child { + border-top-left-radius: 0; + border-top-right-radius: 0; +} +.btn-group-justified { + display: table; + width: 100%; + table-layout: fixed; + border-collapse: separate; +} +.btn-group-justified > .btn, +.btn-group-justified > .btn-group { + display: table-cell; + float: none; + width: 1%; +} +.btn-group-justified > .btn-group .btn { + width: 100%; +} +.btn-group-justified > .btn-group .dropdown-menu { + left: auto; +} +[data-toggle="buttons"] > .btn input[type="radio"], +[data-toggle="buttons"] > .btn-group > .btn input[type="radio"], +[data-toggle="buttons"] > .btn input[type="checkbox"], +[data-toggle="buttons"] > .btn-group > .btn input[type="checkbox"] { + position: absolute; + clip: rect(0, 0, 0, 0); + pointer-events: none; +} +.input-group { + position: relative; + display: table; + border-collapse: separate; +} +.input-group[class*="col-"] { + float: none; + padding-right: 0; + padding-left: 0; +} +.input-group .form-control { + position: relative; + z-index: 2; + float: left; + width: 100%; + margin-bottom: 0; +} +.input-group .form-control:focus { + z-index: 3; +} +.input-group-lg > .form-control, +.input-group-lg > .input-group-addon, +.input-group-lg > .input-group-btn > .btn { + height: 46px; + padding: 10px 16px; + font-size: 18px; + line-height: 1.3333333; + border-radius: 6px; +} +select.input-group-lg > .form-control, +select.input-group-lg > .input-group-addon, +select.input-group-lg > .input-group-btn > .btn { + height: 46px; + line-height: 46px; +} +textarea.input-group-lg > .form-control, +textarea.input-group-lg > .input-group-addon, +textarea.input-group-lg > .input-group-btn > .btn, +select[multiple].input-group-lg > .form-control, +select[multiple].input-group-lg > .input-group-addon, +select[multiple].input-group-lg > .input-group-btn > .btn { + height: auto; +} +.input-group-sm > .form-control, +.input-group-sm > .input-group-addon, +.input-group-sm > .input-group-btn > .btn { + height: 30px; + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +select.input-group-sm > .form-control, +select.input-group-sm > .input-group-addon, +select.input-group-sm > .input-group-btn > .btn { + height: 30px; + line-height: 30px; +} +textarea.input-group-sm > .form-control, +textarea.input-group-sm > .input-group-addon, +textarea.input-group-sm > .input-group-btn > .btn, +select[multiple].input-group-sm > .form-control, +select[multiple].input-group-sm > .input-group-addon, +select[multiple].input-group-sm > .input-group-btn > .btn { + height: auto; +} +.input-group-addon, +.input-group-btn, +.input-group .form-control { + display: table-cell; +} +.input-group-addon:not(:first-child):not(:last-child), +.input-group-btn:not(:first-child):not(:last-child), +.input-group .form-control:not(:first-child):not(:last-child) { + border-radius: 0; +} +.input-group-addon, +.input-group-btn { + width: 1%; + white-space: nowrap; + vertical-align: middle; +} +.input-group-addon { + padding: 6px 12px; + font-size: 14px; + font-weight: normal; + line-height: 1; + color: #555; + text-align: center; + background-color: #eee; + border: 1px solid #ccc; + border-radius: 4px; +} +.input-group-addon.input-sm { + padding: 5px 10px; + font-size: 12px; + border-radius: 3px; +} +.input-group-addon.input-lg { + padding: 10px 16px; + font-size: 18px; + border-radius: 6px; +} +.input-group-addon input[type="radio"], +.input-group-addon input[type="checkbox"] { + margin-top: 0; +} +.input-group .form-control:first-child, +.input-group-addon:first-child, +.input-group-btn:first-child > .btn, +.input-group-btn:first-child > .btn-group > .btn, +.input-group-btn:first-child > .dropdown-toggle, +.input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle), +.input-group-btn:last-child > .btn-group:not(:last-child) > .btn { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} +.input-group-addon:first-child { + border-right: 0; +} +.input-group .form-control:last-child, +.input-group-addon:last-child, +.input-group-btn:last-child > .btn, +.input-group-btn:last-child > .btn-group > .btn, +.input-group-btn:last-child > .dropdown-toggle, +.input-group-btn:first-child > .btn:not(:first-child), +.input-group-btn:first-child > .btn-group:not(:first-child) > .btn { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} +.input-group-addon:last-child { + border-left: 0; +} +.input-group-btn { + position: relative; + font-size: 0; + white-space: nowrap; +} +.input-group-btn > .btn { + position: relative; +} +.input-group-btn > .btn + .btn { + margin-left: -1px; +} +.input-group-btn > .btn:hover, +.input-group-btn > .btn:focus, +.input-group-btn > .btn:active { + z-index: 2; +} +.input-group-btn:first-child > .btn, +.input-group-btn:first-child > .btn-group { + margin-right: -1px; +} +.input-group-btn:last-child > .btn, +.input-group-btn:last-child > .btn-group { + z-index: 2; + margin-left: -1px; +} +.nav { + padding-left: 0; + margin-bottom: 0; + list-style: none; +} +.nav > li { + position: relative; + display: block; +} +.nav > li > a { + position: relative; + display: block; + padding: 10px 15px; +} +.nav > li > a:hover, +.nav > li > a:focus { + text-decoration: none; + background-color: #eee; +} +.nav > li.disabled > a { + color: #777; +} +.nav > li.disabled > a:hover, +.nav > li.disabled > a:focus { + color: #777; + text-decoration: none; + cursor: not-allowed; + background-color: transparent; +} +.nav .open > a, +.nav .open > a:hover, +.nav .open > a:focus { + background-color: #eee; + border-color: #337ab7; +} +.nav .nav-divider { + height: 1px; + margin: 9px 0; + overflow: hidden; + background-color: #e5e5e5; +} +.nav > li > a > img { + max-width: none; +} +.nav-tabs { + border-bottom: 1px solid #ddd; +} +.nav-tabs > li { + float: left; + margin-bottom: -1px; +} +.nav-tabs > li > a { + margin-right: 2px; + line-height: 1.42857143; + border: 1px solid transparent; + border-radius: 4px 4px 0 0; +} +.nav-tabs > li > a:hover { + border-color: #eee #eee #ddd; +} +.nav-tabs > li.active > a, +.nav-tabs > li.active > a:hover, +.nav-tabs > li.active > a:focus { + color: #555; + cursor: default; + background-color: #fff; + border: 1px solid #ddd; + border-bottom-color: transparent; +} +.nav-tabs.nav-justified { + width: 100%; + border-bottom: 0; +} +.nav-tabs.nav-justified > li { + float: none; +} +.nav-tabs.nav-justified > li > a { + margin-bottom: 5px; + text-align: center; +} +.nav-tabs.nav-justified > .dropdown .dropdown-menu { + top: auto; + left: auto; +} +@media (min-width: 768px) { + .nav-tabs.nav-justified > li { + display: table-cell; + width: 1%; + } + .nav-tabs.nav-justified > li > a { + margin-bottom: 0; + } +} +.nav-tabs.nav-justified > li > a { + margin-right: 0; + border-radius: 4px; +} +.nav-tabs.nav-justified > .active > a, +.nav-tabs.nav-justified > .active > a:hover, +.nav-tabs.nav-justified > .active > a:focus { + border: 1px solid #ddd; +} +@media (min-width: 768px) { + .nav-tabs.nav-justified > li > a { + border-bottom: 1px solid #ddd; + border-radius: 4px 4px 0 0; + } + .nav-tabs.nav-justified > .active > a, + .nav-tabs.nav-justified > .active > a:hover, + .nav-tabs.nav-justified > .active > a:focus { + border-bottom-color: #fff; + } +} +.nav-pills > li { + float: left; +} +.nav-pills > li > a { + border-radius: 4px; +} +.nav-pills > li + li { + margin-left: 2px; +} +.nav-pills > li.active > a, +.nav-pills > li.active > a:hover, +.nav-pills > li.active > a:focus { + color: #fff; + background-color: #337ab7; +} +.nav-stacked > li { + float: none; +} +.nav-stacked > li + li { + margin-top: 2px; + margin-left: 0; +} +.nav-justified { + width: 100%; +} +.nav-justified > li { + float: none; +} +.nav-justified > li > a { + margin-bottom: 5px; + text-align: center; +} +.nav-justified > .dropdown .dropdown-menu { + top: auto; + left: auto; +} +@media (min-width: 768px) { + .nav-justified > li { + display: table-cell; + width: 1%; + } + .nav-justified > li > a { + margin-bottom: 0; + } +} +.nav-tabs-justified { + border-bottom: 0; +} +.nav-tabs-justified > li > a { + margin-right: 0; + border-radius: 4px; +} +.nav-tabs-justified > .active > a, +.nav-tabs-justified > .active > a:hover, +.nav-tabs-justified > .active > a:focus { + border: 1px solid #ddd; +} +@media (min-width: 768px) { + .nav-tabs-justified > li > a { + border-bottom: 1px solid #ddd; + border-radius: 4px 4px 0 0; + } + .nav-tabs-justified > .active > a, + .nav-tabs-justified > .active > a:hover, + .nav-tabs-justified > .active > a:focus { + border-bottom-color: #fff; + } +} +.tab-content > .tab-pane { + display: none; +} +.tab-content > .active { + display: block; +} +.nav-tabs .dropdown-menu { + margin-top: -1px; + border-top-left-radius: 0; + border-top-right-radius: 0; +} +.navbar { + position: relative; + min-height: 50px; + margin-bottom: 20px; + border: 1px solid transparent; +} +@media (min-width: 768px) { + .navbar { + border-radius: 4px; + } +} +@media (min-width: 768px) { + .navbar-header { + float: left; + } +} +.navbar-collapse { + padding-right: 15px; + padding-left: 15px; + overflow-x: visible; + -webkit-overflow-scrolling: touch; + border-top: 1px solid transparent; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1); +} +.navbar-collapse.in { + overflow-y: auto; +} +@media (min-width: 768px) { + .navbar-collapse { + width: auto; + border-top: 0; + -webkit-box-shadow: none; + box-shadow: none; + } + .navbar-collapse.collapse { + display: block !important; + height: auto !important; + padding-bottom: 0; + overflow: visible !important; + } + .navbar-collapse.in { + overflow-y: visible; + } + .navbar-fixed-top .navbar-collapse, + .navbar-static-top .navbar-collapse, + .navbar-fixed-bottom .navbar-collapse { + padding-right: 0; + padding-left: 0; + } +} +.navbar-fixed-top .navbar-collapse, +.navbar-fixed-bottom .navbar-collapse { + max-height: 340px; +} +@media (max-device-width: 480px) and (orientation: landscape) { + .navbar-fixed-top .navbar-collapse, + .navbar-fixed-bottom .navbar-collapse { + max-height: 200px; + } +} +.container > .navbar-header, +.container-fluid > .navbar-header, +.container > .navbar-collapse, +.container-fluid > .navbar-collapse { + margin-right: -15px; + margin-left: -15px; +} +@media (min-width: 768px) { + .container > .navbar-header, + .container-fluid > .navbar-header, + .container > .navbar-collapse, + .container-fluid > .navbar-collapse { + margin-right: 0; + margin-left: 0; + } +} +.navbar-static-top { + z-index: 1000; + border-width: 0 0 1px; +} +@media (min-width: 768px) { + .navbar-static-top { + border-radius: 0; + } +} +.navbar-fixed-top, +.navbar-fixed-bottom { + position: fixed; + right: 0; + left: 0; + z-index: 1030; +} +@media (min-width: 768px) { + .navbar-fixed-top, + .navbar-fixed-bottom { + border-radius: 0; + } +} +.navbar-fixed-top { + top: 0; + border-width: 0 0 1px; +} +.navbar-fixed-bottom { + bottom: 0; + margin-bottom: 0; + border-width: 1px 0 0; +} +.navbar-brand { + float: left; + height: 50px; + padding: 15px 15px; + font-size: 18px; + line-height: 20px; +} +.navbar-brand:hover, +.navbar-brand:focus { + text-decoration: none; +} +.navbar-brand > img { + display: block; +} +@media (min-width: 768px) { + .navbar > .container .navbar-brand, + .navbar > .container-fluid .navbar-brand { + margin-left: -15px; + } +} +.navbar-toggle { + position: relative; + float: right; + padding: 9px 10px; + margin-top: 8px; + margin-right: 15px; + margin-bottom: 8px; + background-color: transparent; + background-image: none; + border: 1px solid transparent; + border-radius: 4px; +} +.navbar-toggle:focus { + outline: 0; +} +.navbar-toggle .icon-bar { + display: block; + width: 22px; + height: 2px; + border-radius: 1px; +} +.navbar-toggle .icon-bar + .icon-bar { + margin-top: 4px; +} +@media (min-width: 768px) { + .navbar-toggle { + display: none; + } +} +.navbar-nav { + margin: 7.5px -15px; +} +.navbar-nav > li > a { + padding-top: 10px; + padding-bottom: 10px; + line-height: 20px; +} +@media (max-width: 767px) { + .navbar-nav .open .dropdown-menu { + position: static; + float: none; + width: auto; + margin-top: 0; + background-color: transparent; + border: 0; + -webkit-box-shadow: none; + box-shadow: none; + } + .navbar-nav .open .dropdown-menu > li > a, + .navbar-nav .open .dropdown-menu .dropdown-header { + padding: 5px 15px 5px 25px; + } + .navbar-nav .open .dropdown-menu > li > a { + line-height: 20px; + } + .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-nav .open .dropdown-menu > li > a:focus { + background-image: none; + } +} +@media (min-width: 768px) { + .navbar-nav { + float: left; + margin: 0; + } + .navbar-nav > li { + float: left; + } + .navbar-nav > li > a { + padding-top: 15px; + padding-bottom: 15px; + } +} +.navbar-form { + padding: 10px 15px; + margin-top: 8px; + margin-right: -15px; + margin-bottom: 8px; + margin-left: -15px; + border-top: 1px solid transparent; + border-bottom: 1px solid transparent; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 0 rgba(255, 255, 255, .1); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 0 rgba(255, 255, 255, .1); +} +@media (min-width: 768px) { + .navbar-form .form-group { + display: inline-block; + margin-bottom: 0; + vertical-align: middle; + } + .navbar-form .form-control { + display: inline-block; + width: auto; + vertical-align: middle; + } + .navbar-form .form-control-static { + display: inline-block; + } + .navbar-form .input-group { + display: inline-table; + vertical-align: middle; + } + .navbar-form .input-group .input-group-addon, + .navbar-form .input-group .input-group-btn, + .navbar-form .input-group .form-control { + width: auto; + } + .navbar-form .input-group > .form-control { + width: 100%; + } + .navbar-form .control-label { + margin-bottom: 0; + vertical-align: middle; + } + .navbar-form .radio, + .navbar-form .checkbox { + display: inline-block; + margin-top: 0; + margin-bottom: 0; + vertical-align: middle; + } + .navbar-form .radio label, + .navbar-form .checkbox label { + padding-left: 0; + } + .navbar-form .radio input[type="radio"], + .navbar-form .checkbox input[type="checkbox"] { + position: relative; + margin-left: 0; + } + .navbar-form .has-feedback .form-control-feedback { + top: 0; + } +} +@media (max-width: 767px) { + .navbar-form .form-group { + margin-bottom: 5px; + } + .navbar-form .form-group:last-child { + margin-bottom: 0; + } +} +@media (min-width: 768px) { + .navbar-form { + width: auto; + padding-top: 0; + padding-bottom: 0; + margin-right: 0; + margin-left: 0; + border: 0; + -webkit-box-shadow: none; + box-shadow: none; + } +} +.navbar-nav > li > .dropdown-menu { + margin-top: 0; + border-top-left-radius: 0; + border-top-right-radius: 0; +} +.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu { + margin-bottom: 0; + border-top-left-radius: 4px; + border-top-right-radius: 4px; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} +.navbar-btn { + margin-top: 8px; + margin-bottom: 8px; +} +.navbar-btn.btn-sm { + margin-top: 10px; + margin-bottom: 10px; +} +.navbar-btn.btn-xs { + margin-top: 14px; + margin-bottom: 14px; +} +.navbar-text { + margin-top: 15px; + margin-bottom: 15px; +} +@media (min-width: 768px) { + .navbar-text { + float: left; + margin-right: 15px; + margin-left: 15px; + } +} +@media (min-width: 768px) { + .navbar-left { + float: left !important; + } + .navbar-right { + float: right !important; + margin-right: -15px; + } + .navbar-right ~ .navbar-right { + margin-right: 0; + } +} +.navbar-default { + background-color: #f8f8f8; + border-color: #e7e7e7; +} +.navbar-default .navbar-brand { + color: #777; +} +.navbar-default .navbar-brand:hover, +.navbar-default .navbar-brand:focus { + color: #5e5e5e; + background-color: transparent; +} +.navbar-default .navbar-text { + color: #777; +} +.navbar-default .navbar-nav > li > a { + color: #777; +} +.navbar-default .navbar-nav > li > a:hover, +.navbar-default .navbar-nav > li > a:focus { + color: #333; + background-color: transparent; +} +.navbar-default .navbar-nav > .active > a, +.navbar-default .navbar-nav > .active > a:hover, +.navbar-default .navbar-nav > .active > a:focus { + color: #555; + background-color: #e7e7e7; +} +.navbar-default .navbar-nav > .disabled > a, +.navbar-default .navbar-nav > .disabled > a:hover, +.navbar-default .navbar-nav > .disabled > a:focus { + color: #ccc; + background-color: transparent; +} +.navbar-default .navbar-toggle { + border-color: #ddd; +} +.navbar-default .navbar-toggle:hover, +.navbar-default .navbar-toggle:focus { + background-color: #ddd; +} +.navbar-default .navbar-toggle .icon-bar { + background-color: #888; +} +.navbar-default .navbar-collapse, +.navbar-default .navbar-form { + border-color: #e7e7e7; +} +.navbar-default .navbar-nav > .open > a, +.navbar-default .navbar-nav > .open > a:hover, +.navbar-default .navbar-nav > .open > a:focus { + color: #555; + background-color: #e7e7e7; +} +@media (max-width: 767px) { + .navbar-default .navbar-nav .open .dropdown-menu > li > a { + color: #777; + } + .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus { + color: #333; + background-color: transparent; + } + .navbar-default .navbar-nav .open .dropdown-menu > .active > a, + .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus { + color: #555; + background-color: #e7e7e7; + } + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a, + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:focus { + color: #ccc; + background-color: transparent; + } +} +.navbar-default .navbar-link { + color: #777; +} +.navbar-default .navbar-link:hover { + color: #333; +} +.navbar-default .btn-link { + color: #777; +} +.navbar-default .btn-link:hover, +.navbar-default .btn-link:focus { + color: #333; +} +.navbar-default .btn-link[disabled]:hover, +fieldset[disabled] .navbar-default .btn-link:hover, +.navbar-default .btn-link[disabled]:focus, +fieldset[disabled] .navbar-default .btn-link:focus { + color: #ccc; +} +.navbar-inverse { + background-color: #222; + border-color: #080808; +} +.navbar-inverse .navbar-brand { + color: #9d9d9d; +} +.navbar-inverse .navbar-brand:hover, +.navbar-inverse .navbar-brand:focus { + color: #fff; + background-color: transparent; +} +.navbar-inverse .navbar-text { + color: #9d9d9d; +} +.navbar-inverse .navbar-nav > li > a { + color: #9d9d9d; +} +.navbar-inverse .navbar-nav > li > a:hover, +.navbar-inverse .navbar-nav > li > a:focus { + color: #fff; + background-color: transparent; +} +.navbar-inverse .navbar-nav > .active > a, +.navbar-inverse .navbar-nav > .active > a:hover, +.navbar-inverse .navbar-nav > .active > a:focus { + color: #fff; + background-color: #080808; +} +.navbar-inverse .navbar-nav > .disabled > a, +.navbar-inverse .navbar-nav > .disabled > a:hover, +.navbar-inverse .navbar-nav > .disabled > a:focus { + color: #444; + background-color: transparent; +} +.navbar-inverse .navbar-toggle { + border-color: #333; +} +.navbar-inverse .navbar-toggle:hover, +.navbar-inverse .navbar-toggle:focus { + background-color: #333; +} +.navbar-inverse .navbar-toggle .icon-bar { + background-color: #fff; +} +.navbar-inverse .navbar-collapse, +.navbar-inverse .navbar-form { + border-color: #101010; +} +.navbar-inverse .navbar-nav > .open > a, +.navbar-inverse .navbar-nav > .open > a:hover, +.navbar-inverse .navbar-nav > .open > a:focus { + color: #fff; + background-color: #080808; +} +@media (max-width: 767px) { + .navbar-inverse .navbar-nav .open .dropdown-menu > .dropdown-header { + border-color: #080808; + } + .navbar-inverse .navbar-nav .open .dropdown-menu .divider { + background-color: #080808; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > li > a { + color: #9d9d9d; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:focus { + color: #fff; + background-color: transparent; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a, + .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:hover, + .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:focus { + color: #fff; + background-color: #080808; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a, + .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:hover, + .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:focus { + color: #444; + background-color: transparent; + } +} +.navbar-inverse .navbar-link { + color: #9d9d9d; +} +.navbar-inverse .navbar-link:hover { + color: #fff; +} +.navbar-inverse .btn-link { + color: #9d9d9d; +} +.navbar-inverse .btn-link:hover, +.navbar-inverse .btn-link:focus { + color: #fff; +} +.navbar-inverse .btn-link[disabled]:hover, +fieldset[disabled] .navbar-inverse .btn-link:hover, +.navbar-inverse .btn-link[disabled]:focus, +fieldset[disabled] .navbar-inverse .btn-link:focus { + color: #444; +} +.breadcrumb { + padding: 8px 15px; + margin-bottom: 20px; + list-style: none; + background-color: #f5f5f5; + border-radius: 4px; +} +.breadcrumb > li { + display: inline-block; +} +.breadcrumb > li + li:before { + padding: 0 5px; + color: #ccc; + content: "/\00a0"; +} +.breadcrumb > .active { + color: #777; +} +.pagination { + display: inline-block; + padding-left: 0; + margin: 20px 0; + border-radius: 4px; +} +.pagination > li { + display: inline; +} +.pagination > li > a, +.pagination > li > span { + position: relative; + float: left; + padding: 6px 12px; + margin-left: -1px; + line-height: 1.42857143; + color: #337ab7; + text-decoration: none; + background-color: #fff; + border: 1px solid #ddd; +} +.pagination > li:first-child > a, +.pagination > li:first-child > span { + margin-left: 0; + border-top-left-radius: 4px; + border-bottom-left-radius: 4px; +} +.pagination > li:last-child > a, +.pagination > li:last-child > span { + border-top-right-radius: 4px; + border-bottom-right-radius: 4px; +} +.pagination > li > a:hover, +.pagination > li > span:hover, +.pagination > li > a:focus, +.pagination > li > span:focus { + z-index: 2; + color: #23527c; + background-color: #eee; + border-color: #ddd; +} +.pagination > .active > a, +.pagination > .active > span, +.pagination > .active > a:hover, +.pagination > .active > span:hover, +.pagination > .active > a:focus, +.pagination > .active > span:focus { + z-index: 3; + color: #fff; + cursor: default; + background-color: #337ab7; + border-color: #337ab7; +} +.pagination > .disabled > span, +.pagination > .disabled > span:hover, +.pagination > .disabled > span:focus, +.pagination > .disabled > a, +.pagination > .disabled > a:hover, +.pagination > .disabled > a:focus { + color: #777; + cursor: not-allowed; + background-color: #fff; + border-color: #ddd; +} +.pagination-lg > li > a, +.pagination-lg > li > span { + padding: 10px 16px; + font-size: 18px; + line-height: 1.3333333; +} +.pagination-lg > li:first-child > a, +.pagination-lg > li:first-child > span { + border-top-left-radius: 6px; + border-bottom-left-radius: 6px; +} +.pagination-lg > li:last-child > a, +.pagination-lg > li:last-child > span { + border-top-right-radius: 6px; + border-bottom-right-radius: 6px; +} +.pagination-sm > li > a, +.pagination-sm > li > span { + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; +} +.pagination-sm > li:first-child > a, +.pagination-sm > li:first-child > span { + border-top-left-radius: 3px; + border-bottom-left-radius: 3px; +} +.pagination-sm > li:last-child > a, +.pagination-sm > li:last-child > span { + border-top-right-radius: 3px; + border-bottom-right-radius: 3px; +} +.pager { + padding-left: 0; + margin: 20px 0; + text-align: center; + list-style: none; +} +.pager li { + display: inline; +} +.pager li > a, +.pager li > span { + display: inline-block; + padding: 5px 14px; + background-color: #fff; + border: 1px solid #ddd; + border-radius: 15px; +} +.pager li > a:hover, +.pager li > a:focus { + text-decoration: none; + background-color: #eee; +} +.pager .next > a, +.pager .next > span { + float: right; +} +.pager .previous > a, +.pager .previous > span { + float: left; +} +.pager .disabled > a, +.pager .disabled > a:hover, +.pager .disabled > a:focus, +.pager .disabled > span { + color: #777; + cursor: not-allowed; + background-color: #fff; +} +.label { + display: inline; + padding: .2em .6em .3em; + font-size: 75%; + font-weight: bold; + line-height: 1; + color: #fff; + text-align: center; + white-space: nowrap; + vertical-align: baseline; + border-radius: .25em; +} +a.label:hover, +a.label:focus { + color: #fff; + text-decoration: none; + cursor: pointer; +} +.label:empty { + display: none; +} +.btn .label { + position: relative; + top: -1px; +} +.label-default { + background-color: #777; +} +.label-default[href]:hover, +.label-default[href]:focus { + background-color: #5e5e5e; +} +.label-primary { + background-color: #337ab7; +} +.label-primary[href]:hover, +.label-primary[href]:focus { + background-color: #286090; +} +.label-success { + background-color: #5cb85c; +} +.label-success[href]:hover, +.label-success[href]:focus { + background-color: #449d44; +} +.label-info { + background-color: #5bc0de; +} +.label-info[href]:hover, +.label-info[href]:focus { + background-color: #31b0d5; +} +.label-warning { + background-color: #f0ad4e; +} +.label-warning[href]:hover, +.label-warning[href]:focus { + background-color: #ec971f; +} +.label-danger { + background-color: #d9534f; +} +.label-danger[href]:hover, +.label-danger[href]:focus { + background-color: #c9302c; +} +.badge { + display: inline-block; + min-width: 10px; + padding: 3px 7px; + font-size: 12px; + font-weight: bold; + line-height: 1; + color: #fff; + text-align: center; + white-space: nowrap; + vertical-align: middle; + background-color: #777; + border-radius: 10px; +} +.badge:empty { + display: none; +} +.btn .badge { + position: relative; + top: -1px; +} +.btn-xs .badge, +.btn-group-xs > .btn .badge { + top: 0; + padding: 1px 5px; +} +a.badge:hover, +a.badge:focus { + color: #fff; + text-decoration: none; + cursor: pointer; +} +.list-group-item.active > .badge, +.nav-pills > .active > a > .badge { + color: #337ab7; + background-color: #fff; +} +.list-group-item > .badge { + float: right; +} +.list-group-item > .badge + .badge { + margin-right: 5px; +} +.nav-pills > li > a > .badge { + margin-left: 3px; +} +.jumbotron { + padding-top: 30px; + padding-bottom: 30px; + margin-bottom: 30px; + color: inherit; + background-color: #eee; +} +.jumbotron h1, +.jumbotron .h1 { + color: inherit; +} +.jumbotron p { + margin-bottom: 15px; + font-size: 21px; + font-weight: 200; +} +.jumbotron > hr { + border-top-color: #d5d5d5; +} +.container .jumbotron, +.container-fluid .jumbotron { + padding-right: 15px; + padding-left: 15px; + border-radius: 6px; +} +.jumbotron .container { + max-width: 100%; +} +@media screen and (min-width: 768px) { + .jumbotron { + padding-top: 48px; + padding-bottom: 48px; + } + .container .jumbotron, + .container-fluid .jumbotron { + padding-right: 60px; + padding-left: 60px; + } + .jumbotron h1, + .jumbotron .h1 { + font-size: 63px; + } +} +.thumbnail { + display: block; + padding: 4px; + margin-bottom: 20px; + line-height: 1.42857143; + background-color: #fff; + border: 1px solid #ddd; + border-radius: 4px; + -webkit-transition: border .2s ease-in-out; + -o-transition: border .2s ease-in-out; + transition: border .2s ease-in-out; +} +.thumbnail > img, +.thumbnail a > img { + margin-right: auto; + margin-left: auto; +} +a.thumbnail:hover, +a.thumbnail:focus, +a.thumbnail.active { + border-color: #337ab7; +} +.thumbnail .caption { + padding: 9px; + color: #333; +} +.alert { + padding: 15px; + margin-bottom: 20px; + border: 1px solid transparent; + border-radius: 4px; +} +.alert h4 { + margin-top: 0; + color: inherit; +} +.alert .alert-link { + font-weight: bold; +} +.alert > p, +.alert > ul { + margin-bottom: 0; +} +.alert > p + p { + margin-top: 5px; +} +.alert-dismissable, +.alert-dismissible { + padding-right: 35px; +} +.alert-dismissable .close, +.alert-dismissible .close { + position: relative; + top: -2px; + right: -21px; + color: inherit; +} +.alert-success { + color: #3c763d; + background-color: #dff0d8; + border-color: #d6e9c6; +} +.alert-success hr { + border-top-color: #c9e2b3; +} +.alert-success .alert-link { + color: #2b542c; +} +.alert-info { + color: #31708f; + background-color: #d9edf7; + border-color: #bce8f1; +} +.alert-info hr { + border-top-color: #a6e1ec; +} +.alert-info .alert-link { + color: #245269; +} +.alert-warning { + color: #8a6d3b; + background-color: #fcf8e3; + border-color: #faebcc; +} +.alert-warning hr { + border-top-color: #f7e1b5; +} +.alert-warning .alert-link { + color: #66512c; +} +.alert-danger { + color: #a94442; + background-color: #f2dede; + border-color: #ebccd1; +} +.alert-danger hr { + border-top-color: #e4b9c0; +} +.alert-danger .alert-link { + color: #843534; +} +@-webkit-keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} +@-o-keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} +@keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} +.progress { + height: 20px; + margin-bottom: 20px; + overflow: hidden; + background-color: #f5f5f5; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1); + box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1); +} +.progress-bar { + float: left; + width: 0; + height: 100%; + font-size: 12px; + line-height: 20px; + color: #fff; + text-align: center; + background-color: #337ab7; + -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15); + box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15); + -webkit-transition: width .6s ease; + -o-transition: width .6s ease; + transition: width .6s ease; +} +.progress-striped .progress-bar, +.progress-bar-striped { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + -webkit-background-size: 40px 40px; + background-size: 40px 40px; +} +.progress.active .progress-bar, +.progress-bar.active { + -webkit-animation: progress-bar-stripes 2s linear infinite; + -o-animation: progress-bar-stripes 2s linear infinite; + animation: progress-bar-stripes 2s linear infinite; +} +.progress-bar-success { + background-color: #5cb85c; +} +.progress-striped .progress-bar-success { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); +} +.progress-bar-info { + background-color: #5bc0de; +} +.progress-striped .progress-bar-info { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); +} +.progress-bar-warning { + background-color: #f0ad4e; +} +.progress-striped .progress-bar-warning { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); +} +.progress-bar-danger { + background-color: #d9534f; +} +.progress-striped .progress-bar-danger { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); +} +.media { + margin-top: 15px; +} +.media:first-child { + margin-top: 0; +} +.media, +.media-body { + overflow: hidden; + zoom: 1; +} +.media-body { + width: 10000px; +} +.media-object { + display: block; +} +.media-object.img-thumbnail { + max-width: none; +} +.media-right, +.media > .pull-right { + padding-left: 10px; +} +.media-left, +.media > .pull-left { + padding-right: 10px; +} +.media-left, +.media-right, +.media-body { + display: table-cell; + vertical-align: top; +} +.media-middle { + vertical-align: middle; +} +.media-bottom { + vertical-align: bottom; +} +.media-heading { + margin-top: 0; + margin-bottom: 5px; +} +.media-list { + padding-left: 0; + list-style: none; +} +.list-group { + padding-left: 0; + margin-bottom: 20px; +} +.list-group-item { + position: relative; + display: block; + padding: 10px 15px; + margin-bottom: -1px; + background-color: #fff; + border: 1px solid #ddd; +} +.list-group-item:first-child { + border-top-left-radius: 4px; + border-top-right-radius: 4px; +} +.list-group-item:last-child { + margin-bottom: 0; + border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px; +} +a.list-group-item, +button.list-group-item { + color: #555; +} +a.list-group-item .list-group-item-heading, +button.list-group-item .list-group-item-heading { + color: #333; +} +a.list-group-item:hover, +button.list-group-item:hover, +a.list-group-item:focus, +button.list-group-item:focus { + color: #555; + text-decoration: none; + background-color: #f5f5f5; +} +button.list-group-item { + width: 100%; + text-align: left; +} +.list-group-item.disabled, +.list-group-item.disabled:hover, +.list-group-item.disabled:focus { + color: #777; + cursor: not-allowed; + background-color: #eee; +} +.list-group-item.disabled .list-group-item-heading, +.list-group-item.disabled:hover .list-group-item-heading, +.list-group-item.disabled:focus .list-group-item-heading { + color: inherit; +} +.list-group-item.disabled .list-group-item-text, +.list-group-item.disabled:hover .list-group-item-text, +.list-group-item.disabled:focus .list-group-item-text { + color: #777; +} +.list-group-item.active, +.list-group-item.active:hover, +.list-group-item.active:focus { + z-index: 2; + color: #fff; + background-color: #337ab7; + border-color: #337ab7; +} +.list-group-item.active .list-group-item-heading, +.list-group-item.active:hover .list-group-item-heading, +.list-group-item.active:focus .list-group-item-heading, +.list-group-item.active .list-group-item-heading > small, +.list-group-item.active:hover .list-group-item-heading > small, +.list-group-item.active:focus .list-group-item-heading > small, +.list-group-item.active .list-group-item-heading > .small, +.list-group-item.active:hover .list-group-item-heading > .small, +.list-group-item.active:focus .list-group-item-heading > .small { + color: inherit; +} +.list-group-item.active .list-group-item-text, +.list-group-item.active:hover .list-group-item-text, +.list-group-item.active:focus .list-group-item-text { + color: #c7ddef; +} +.list-group-item-success { + color: #3c763d; + background-color: #dff0d8; +} +a.list-group-item-success, +button.list-group-item-success { + color: #3c763d; +} +a.list-group-item-success .list-group-item-heading, +button.list-group-item-success .list-group-item-heading { + color: inherit; +} +a.list-group-item-success:hover, +button.list-group-item-success:hover, +a.list-group-item-success:focus, +button.list-group-item-success:focus { + color: #3c763d; + background-color: #d0e9c6; +} +a.list-group-item-success.active, +button.list-group-item-success.active, +a.list-group-item-success.active:hover, +button.list-group-item-success.active:hover, +a.list-group-item-success.active:focus, +button.list-group-item-success.active:focus { + color: #fff; + background-color: #3c763d; + border-color: #3c763d; +} +.list-group-item-info { + color: #31708f; + background-color: #d9edf7; +} +a.list-group-item-info, +button.list-group-item-info { + color: #31708f; +} +a.list-group-item-info .list-group-item-heading, +button.list-group-item-info .list-group-item-heading { + color: inherit; +} +a.list-group-item-info:hover, +button.list-group-item-info:hover, +a.list-group-item-info:focus, +button.list-group-item-info:focus { + color: #31708f; + background-color: #c4e3f3; +} +a.list-group-item-info.active, +button.list-group-item-info.active, +a.list-group-item-info.active:hover, +button.list-group-item-info.active:hover, +a.list-group-item-info.active:focus, +button.list-group-item-info.active:focus { + color: #fff; + background-color: #31708f; + border-color: #31708f; +} +.list-group-item-warning { + color: #8a6d3b; + background-color: #fcf8e3; +} +a.list-group-item-warning, +button.list-group-item-warning { + color: #8a6d3b; +} +a.list-group-item-warning .list-group-item-heading, +button.list-group-item-warning .list-group-item-heading { + color: inherit; +} +a.list-group-item-warning:hover, +button.list-group-item-warning:hover, +a.list-group-item-warning:focus, +button.list-group-item-warning:focus { + color: #8a6d3b; + background-color: #faf2cc; +} +a.list-group-item-warning.active, +button.list-group-item-warning.active, +a.list-group-item-warning.active:hover, +button.list-group-item-warning.active:hover, +a.list-group-item-warning.active:focus, +button.list-group-item-warning.active:focus { + color: #fff; + background-color: #8a6d3b; + border-color: #8a6d3b; +} +.list-group-item-danger { + color: #a94442; + background-color: #f2dede; +} +a.list-group-item-danger, +button.list-group-item-danger { + color: #a94442; +} +a.list-group-item-danger .list-group-item-heading, +button.list-group-item-danger .list-group-item-heading { + color: inherit; +} +a.list-group-item-danger:hover, +button.list-group-item-danger:hover, +a.list-group-item-danger:focus, +button.list-group-item-danger:focus { + color: #a94442; + background-color: #ebcccc; +} +a.list-group-item-danger.active, +button.list-group-item-danger.active, +a.list-group-item-danger.active:hover, +button.list-group-item-danger.active:hover, +a.list-group-item-danger.active:focus, +button.list-group-item-danger.active:focus { + color: #fff; + background-color: #a94442; + border-color: #a94442; +} +.list-group-item-heading { + margin-top: 0; + margin-bottom: 5px; +} +.list-group-item-text { + margin-bottom: 0; + line-height: 1.3; +} +.panel { + margin-bottom: 20px; + background-color: #fff; + border: 1px solid transparent; + border-radius: 4px; + -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, .05); + box-shadow: 0 1px 1px rgba(0, 0, 0, .05); +} +.panel-body { + padding: 15px; +} +.panel-heading { + padding: 10px 15px; + border-bottom: 1px solid transparent; + border-top-left-radius: 3px; + border-top-right-radius: 3px; +} +.panel-heading > .dropdown .dropdown-toggle { + color: inherit; +} +.panel-title { + margin-top: 0; + margin-bottom: 0; + font-size: 16px; + color: inherit; +} +.panel-title > a, +.panel-title > small, +.panel-title > .small, +.panel-title > small > a, +.panel-title > .small > a { + color: inherit; +} +.panel-footer { + padding: 10px 15px; + background-color: #f5f5f5; + border-top: 1px solid #ddd; + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} +.panel > .list-group, +.panel > .panel-collapse > .list-group { + margin-bottom: 0; +} +.panel > .list-group .list-group-item, +.panel > .panel-collapse > .list-group .list-group-item { + border-width: 1px 0; + border-radius: 0; +} +.panel > .list-group:first-child .list-group-item:first-child, +.panel > .panel-collapse > .list-group:first-child .list-group-item:first-child { + border-top: 0; + border-top-left-radius: 3px; + border-top-right-radius: 3px; +} +.panel > .list-group:last-child .list-group-item:last-child, +.panel > .panel-collapse > .list-group:last-child .list-group-item:last-child { + border-bottom: 0; + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} +.panel > .panel-heading + .panel-collapse > .list-group .list-group-item:first-child { + border-top-left-radius: 0; + border-top-right-radius: 0; +} +.panel-heading + .list-group .list-group-item:first-child { + border-top-width: 0; +} +.list-group + .panel-footer { + border-top-width: 0; +} +.panel > .table, +.panel > .table-responsive > .table, +.panel > .panel-collapse > .table { + margin-bottom: 0; +} +.panel > .table caption, +.panel > .table-responsive > .table caption, +.panel > .panel-collapse > .table caption { + padding-right: 15px; + padding-left: 15px; +} +.panel > .table:first-child, +.panel > .table-responsive:first-child > .table:first-child { + border-top-left-radius: 3px; + border-top-right-radius: 3px; +} +.panel > .table:first-child > thead:first-child > tr:first-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child, +.panel > .table:first-child > tbody:first-child > tr:first-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child { + border-top-left-radius: 3px; + border-top-right-radius: 3px; +} +.panel > .table:first-child > thead:first-child > tr:first-child td:first-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:first-child, +.panel > .table:first-child > tbody:first-child > tr:first-child td:first-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:first-child, +.panel > .table:first-child > thead:first-child > tr:first-child th:first-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:first-child, +.panel > .table:first-child > tbody:first-child > tr:first-child th:first-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:first-child { + border-top-left-radius: 3px; +} +.panel > .table:first-child > thead:first-child > tr:first-child td:last-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:last-child, +.panel > .table:first-child > tbody:first-child > tr:first-child td:last-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:last-child, +.panel > .table:first-child > thead:first-child > tr:first-child th:last-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:last-child, +.panel > .table:first-child > tbody:first-child > tr:first-child th:last-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:last-child { + border-top-right-radius: 3px; +} +.panel > .table:last-child, +.panel > .table-responsive:last-child > .table:last-child { + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} +.panel > .table:last-child > tbody:last-child > tr:last-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child { + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} +.panel > .table:last-child > tbody:last-child > tr:last-child td:first-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:first-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child td:first-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:first-child, +.panel > .table:last-child > tbody:last-child > tr:last-child th:first-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:first-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child th:first-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:first-child { + border-bottom-left-radius: 3px; +} +.panel > .table:last-child > tbody:last-child > tr:last-child td:last-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:last-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child td:last-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:last-child, +.panel > .table:last-child > tbody:last-child > tr:last-child th:last-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:last-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child th:last-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:last-child { + border-bottom-right-radius: 3px; +} +.panel > .panel-body + .table, +.panel > .panel-body + .table-responsive, +.panel > .table + .panel-body, +.panel > .table-responsive + .panel-body { + border-top: 1px solid #ddd; +} +.panel > .table > tbody:first-child > tr:first-child th, +.panel > .table > tbody:first-child > tr:first-child td { + border-top: 0; +} +.panel > .table-bordered, +.panel > .table-responsive > .table-bordered { + border: 0; +} +.panel > .table-bordered > thead > tr > th:first-child, +.panel > .table-responsive > .table-bordered > thead > tr > th:first-child, +.panel > .table-bordered > tbody > tr > th:first-child, +.panel > .table-responsive > .table-bordered > tbody > tr > th:first-child, +.panel > .table-bordered > tfoot > tr > th:first-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > th:first-child, +.panel > .table-bordered > thead > tr > td:first-child, +.panel > .table-responsive > .table-bordered > thead > tr > td:first-child, +.panel > .table-bordered > tbody > tr > td:first-child, +.panel > .table-responsive > .table-bordered > tbody > tr > td:first-child, +.panel > .table-bordered > tfoot > tr > td:first-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > td:first-child { + border-left: 0; +} +.panel > .table-bordered > thead > tr > th:last-child, +.panel > .table-responsive > .table-bordered > thead > tr > th:last-child, +.panel > .table-bordered > tbody > tr > th:last-child, +.panel > .table-responsive > .table-bordered > tbody > tr > th:last-child, +.panel > .table-bordered > tfoot > tr > th:last-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > th:last-child, +.panel > .table-bordered > thead > tr > td:last-child, +.panel > .table-responsive > .table-bordered > thead > tr > td:last-child, +.panel > .table-bordered > tbody > tr > td:last-child, +.panel > .table-responsive > .table-bordered > tbody > tr > td:last-child, +.panel > .table-bordered > tfoot > tr > td:last-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > td:last-child { + border-right: 0; +} +.panel > .table-bordered > thead > tr:first-child > td, +.panel > .table-responsive > .table-bordered > thead > tr:first-child > td, +.panel > .table-bordered > tbody > tr:first-child > td, +.panel > .table-responsive > .table-bordered > tbody > tr:first-child > td, +.panel > .table-bordered > thead > tr:first-child > th, +.panel > .table-responsive > .table-bordered > thead > tr:first-child > th, +.panel > .table-bordered > tbody > tr:first-child > th, +.panel > .table-responsive > .table-bordered > tbody > tr:first-child > th { + border-bottom: 0; +} +.panel > .table-bordered > tbody > tr:last-child > td, +.panel > .table-responsive > .table-bordered > tbody > tr:last-child > td, +.panel > .table-bordered > tfoot > tr:last-child > td, +.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > td, +.panel > .table-bordered > tbody > tr:last-child > th, +.panel > .table-responsive > .table-bordered > tbody > tr:last-child > th, +.panel > .table-bordered > tfoot > tr:last-child > th, +.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > th { + border-bottom: 0; +} +.panel > .table-responsive { + margin-bottom: 0; + border: 0; +} +.panel-group { + margin-bottom: 20px; +} +.panel-group .panel { + margin-bottom: 0; + border-radius: 4px; +} +.panel-group .panel + .panel { + margin-top: 5px; +} +.panel-group .panel-heading { + border-bottom: 0; +} +.panel-group .panel-heading + .panel-collapse > .panel-body, +.panel-group .panel-heading + .panel-collapse > .list-group { + border-top: 1px solid #ddd; +} +.panel-group .panel-footer { + border-top: 0; +} +.panel-group .panel-footer + .panel-collapse .panel-body { + border-bottom: 1px solid #ddd; +} +.panel-default { + border-color: #ddd; +} +.panel-default > .panel-heading { + color: #333; + background-color: #f5f5f5; + border-color: #ddd; +} +.panel-default > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #ddd; +} +.panel-default > .panel-heading .badge { + color: #f5f5f5; + background-color: #333; +} +.panel-default > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #ddd; +} +.panel-primary { + border-color: #337ab7; +} +.panel-primary > .panel-heading { + color: #fff; + background-color: #337ab7; + border-color: #337ab7; +} +.panel-primary > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #337ab7; +} +.panel-primary > .panel-heading .badge { + color: #337ab7; + background-color: #fff; +} +.panel-primary > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #337ab7; +} +.panel-success { + border-color: #d6e9c6; +} +.panel-success > .panel-heading { + color: #3c763d; + background-color: #dff0d8; + border-color: #d6e9c6; +} +.panel-success > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #d6e9c6; +} +.panel-success > .panel-heading .badge { + color: #dff0d8; + background-color: #3c763d; +} +.panel-success > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #d6e9c6; +} +.panel-info { + border-color: #bce8f1; +} +.panel-info > .panel-heading { + color: #31708f; + background-color: #d9edf7; + border-color: #bce8f1; +} +.panel-info > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #bce8f1; +} +.panel-info > .panel-heading .badge { + color: #d9edf7; + background-color: #31708f; +} +.panel-info > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #bce8f1; +} +.panel-warning { + border-color: #faebcc; +} +.panel-warning > .panel-heading { + color: #8a6d3b; + background-color: #fcf8e3; + border-color: #faebcc; +} +.panel-warning > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #faebcc; +} +.panel-warning > .panel-heading .badge { + color: #fcf8e3; + background-color: #8a6d3b; +} +.panel-warning > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #faebcc; +} +.panel-danger { + border-color: #ebccd1; +} +.panel-danger > .panel-heading { + color: #a94442; + background-color: #f2dede; + border-color: #ebccd1; +} +.panel-danger > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #ebccd1; +} +.panel-danger > .panel-heading .badge { + color: #f2dede; + background-color: #a94442; +} +.panel-danger > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #ebccd1; +} +.embed-responsive { + position: relative; + display: block; + height: 0; + padding: 0; + overflow: hidden; +} +.embed-responsive .embed-responsive-item, +.embed-responsive iframe, +.embed-responsive embed, +.embed-responsive object, +.embed-responsive video { + position: absolute; + top: 0; + bottom: 0; + left: 0; + width: 100%; + height: 100%; + border: 0; +} +.embed-responsive-16by9 { + padding-bottom: 56.25%; +} +.embed-responsive-4by3 { + padding-bottom: 75%; +} +.well { + min-height: 20px; + padding: 19px; + margin-bottom: 20px; + background-color: #f5f5f5; + border: 1px solid #e3e3e3; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05); +} +.well blockquote { + border-color: #ddd; + border-color: rgba(0, 0, 0, .15); +} +.well-lg { + padding: 24px; + border-radius: 6px; +} +.well-sm { + padding: 9px; + border-radius: 3px; +} +.close { + float: right; + font-size: 21px; + font-weight: bold; + line-height: 1; + color: #000; + text-shadow: 0 1px 0 #fff; + filter: alpha(opacity=20); + opacity: .2; +} +.close:hover, +.close:focus { + color: #000; + text-decoration: none; + cursor: pointer; + filter: alpha(opacity=50); + opacity: .5; +} +button.close { + -webkit-appearance: none; + padding: 0; + cursor: pointer; + background: transparent; + border: 0; +} +.modal-open { + overflow: hidden; +} +.modal { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1050; + display: none; + overflow: hidden; + -webkit-overflow-scrolling: touch; + outline: 0; +} +.modal.fade .modal-dialog { + -webkit-transition: -webkit-transform .3s ease-out; + -o-transition: -o-transform .3s ease-out; + transition: transform .3s ease-out; + -webkit-transform: translate(0, -25%); + -ms-transform: translate(0, -25%); + -o-transform: translate(0, -25%); + transform: translate(0, -25%); +} +.modal.in .modal-dialog { + -webkit-transform: translate(0, 0); + -ms-transform: translate(0, 0); + -o-transform: translate(0, 0); + transform: translate(0, 0); +} +.modal-open .modal { + overflow-x: hidden; + overflow-y: auto; +} +.modal-dialog { + position: relative; + width: auto; + margin: 10px; +} +.modal-content { + position: relative; + background-color: #fff; + -webkit-background-clip: padding-box; + background-clip: padding-box; + border: 1px solid #999; + border: 1px solid rgba(0, 0, 0, .2); + border-radius: 6px; + outline: 0; + -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, .5); + box-shadow: 0 3px 9px rgba(0, 0, 0, .5); +} +.modal-backdrop { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1040; + background-color: #000; +} +.modal-backdrop.fade { + filter: alpha(opacity=0); + opacity: 0; +} +.modal-backdrop.in { + filter: alpha(opacity=50); + opacity: .5; +} +.modal-header { + padding: 15px; + border-bottom: 1px solid #e5e5e5; +} +.modal-header .close { + margin-top: -2px; +} +.modal-title { + margin: 0; + line-height: 1.42857143; +} +.modal-body { + position: relative; + padding: 15px; +} +.modal-footer { + padding: 15px; + text-align: right; + border-top: 1px solid #e5e5e5; +} +.modal-footer .btn + .btn { + margin-bottom: 0; + margin-left: 5px; +} +.modal-footer .btn-group .btn + .btn { + margin-left: -1px; +} +.modal-footer .btn-block + .btn-block { + margin-left: 0; +} +.modal-scrollbar-measure { + position: absolute; + top: -9999px; + width: 50px; + height: 50px; + overflow: scroll; +} +@media (min-width: 768px) { + .modal-dialog { + width: 600px; + margin: 30px auto; + } + .modal-content { + -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, .5); + box-shadow: 0 5px 15px rgba(0, 0, 0, .5); + } + .modal-sm { + width: 300px; + } +} +@media (min-width: 992px) { + .modal-lg { + width: 900px; + } +} +.tooltip { + position: absolute; + z-index: 1070; + display: block; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 12px; + font-style: normal; + font-weight: normal; + line-height: 1.42857143; + text-align: left; + text-align: start; + text-decoration: none; + text-shadow: none; + text-transform: none; + letter-spacing: normal; + word-break: normal; + word-spacing: normal; + word-wrap: normal; + white-space: normal; + filter: alpha(opacity=0); + opacity: 0; + + line-break: auto; +} +.tooltip.in { + filter: alpha(opacity=90); + opacity: .9; +} +.tooltip.top { + padding: 5px 0; + margin-top: -3px; +} +.tooltip.right { + padding: 0 5px; + margin-left: 3px; +} +.tooltip.bottom { + padding: 5px 0; + margin-top: 3px; +} +.tooltip.left { + padding: 0 5px; + margin-left: -3px; +} +.tooltip-inner { + max-width: 200px; + padding: 3px 8px; + color: #fff; + text-align: center; + background-color: #000; + border-radius: 4px; +} +.tooltip-arrow { + position: absolute; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; +} +.tooltip.top .tooltip-arrow { + bottom: 0; + left: 50%; + margin-left: -5px; + border-width: 5px 5px 0; + border-top-color: #000; +} +.tooltip.top-left .tooltip-arrow { + right: 5px; + bottom: 0; + margin-bottom: -5px; + border-width: 5px 5px 0; + border-top-color: #000; +} +.tooltip.top-right .tooltip-arrow { + bottom: 0; + left: 5px; + margin-bottom: -5px; + border-width: 5px 5px 0; + border-top-color: #000; +} +.tooltip.right .tooltip-arrow { + top: 50%; + left: 0; + margin-top: -5px; + border-width: 5px 5px 5px 0; + border-right-color: #000; +} +.tooltip.left .tooltip-arrow { + top: 50%; + right: 0; + margin-top: -5px; + border-width: 5px 0 5px 5px; + border-left-color: #000; +} +.tooltip.bottom .tooltip-arrow { + top: 0; + left: 50%; + margin-left: -5px; + border-width: 0 5px 5px; + border-bottom-color: #000; +} +.tooltip.bottom-left .tooltip-arrow { + top: 0; + right: 5px; + margin-top: -5px; + border-width: 0 5px 5px; + border-bottom-color: #000; +} +.tooltip.bottom-right .tooltip-arrow { + top: 0; + left: 5px; + margin-top: -5px; + border-width: 0 5px 5px; + border-bottom-color: #000; +} +.popover { + position: absolute; + top: 0; + left: 0; + z-index: 1060; + display: none; + max-width: 276px; + padding: 1px; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 14px; + font-style: normal; + font-weight: normal; + line-height: 1.42857143; + text-align: left; + text-align: start; + text-decoration: none; + text-shadow: none; + text-transform: none; + letter-spacing: normal; + word-break: normal; + word-spacing: normal; + word-wrap: normal; + white-space: normal; + background-color: #fff; + -webkit-background-clip: padding-box; + background-clip: padding-box; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, .2); + border-radius: 6px; + -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, .2); + box-shadow: 0 5px 10px rgba(0, 0, 0, .2); + + line-break: auto; +} +.popover.top { + margin-top: -10px; +} +.popover.right { + margin-left: 10px; +} +.popover.bottom { + margin-top: 10px; +} +.popover.left { + margin-left: -10px; +} +.popover-title { + padding: 8px 14px; + margin: 0; + font-size: 14px; + background-color: #f7f7f7; + border-bottom: 1px solid #ebebeb; + border-radius: 5px 5px 0 0; +} +.popover-content { + padding: 9px 14px; +} +.popover > .arrow, +.popover > .arrow:after { + position: absolute; + display: block; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; +} +.popover > .arrow { + border-width: 11px; +} +.popover > .arrow:after { + content: ""; + border-width: 10px; +} +.popover.top > .arrow { + bottom: -11px; + left: 50%; + margin-left: -11px; + border-top-color: #999; + border-top-color: rgba(0, 0, 0, .25); + border-bottom-width: 0; +} +.popover.top > .arrow:after { + bottom: 1px; + margin-left: -10px; + content: " "; + border-top-color: #fff; + border-bottom-width: 0; +} +.popover.right > .arrow { + top: 50%; + left: -11px; + margin-top: -11px; + border-right-color: #999; + border-right-color: rgba(0, 0, 0, .25); + border-left-width: 0; +} +.popover.right > .arrow:after { + bottom: -10px; + left: 1px; + content: " "; + border-right-color: #fff; + border-left-width: 0; +} +.popover.bottom > .arrow { + top: -11px; + left: 50%; + margin-left: -11px; + border-top-width: 0; + border-bottom-color: #999; + border-bottom-color: rgba(0, 0, 0, .25); +} +.popover.bottom > .arrow:after { + top: 1px; + margin-left: -10px; + content: " "; + border-top-width: 0; + border-bottom-color: #fff; +} +.popover.left > .arrow { + top: 50%; + right: -11px; + margin-top: -11px; + border-right-width: 0; + border-left-color: #999; + border-left-color: rgba(0, 0, 0, .25); +} +.popover.left > .arrow:after { + right: 1px; + bottom: -10px; + content: " "; + border-right-width: 0; + border-left-color: #fff; +} +.carousel { + position: relative; +} +.carousel-inner { + position: relative; + width: 100%; + overflow: hidden; +} +.carousel-inner > .item { + position: relative; + display: none; + -webkit-transition: .6s ease-in-out left; + -o-transition: .6s ease-in-out left; + transition: .6s ease-in-out left; +} +.carousel-inner > .item > img, +.carousel-inner > .item > a > img { + line-height: 1; +} +@media all and (transform-3d), (-webkit-transform-3d) { + .carousel-inner > .item { + -webkit-transition: -webkit-transform .6s ease-in-out; + -o-transition: -o-transform .6s ease-in-out; + transition: transform .6s ease-in-out; + + -webkit-backface-visibility: hidden; + backface-visibility: hidden; + -webkit-perspective: 1000px; + perspective: 1000px; + } + .carousel-inner > .item.next, + .carousel-inner > .item.active.right { + left: 0; + -webkit-transform: translate3d(100%, 0, 0); + transform: translate3d(100%, 0, 0); + } + .carousel-inner > .item.prev, + .carousel-inner > .item.active.left { + left: 0; + -webkit-transform: translate3d(-100%, 0, 0); + transform: translate3d(-100%, 0, 0); + } + .carousel-inner > .item.next.left, + .carousel-inner > .item.prev.right, + .carousel-inner > .item.active { + left: 0; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } +} +.carousel-inner > .active, +.carousel-inner > .next, +.carousel-inner > .prev { + display: block; +} +.carousel-inner > .active { + left: 0; +} +.carousel-inner > .next, +.carousel-inner > .prev { + position: absolute; + top: 0; + width: 100%; +} +.carousel-inner > .next { + left: 100%; +} +.carousel-inner > .prev { + left: -100%; +} +.carousel-inner > .next.left, +.carousel-inner > .prev.right { + left: 0; +} +.carousel-inner > .active.left { + left: -100%; +} +.carousel-inner > .active.right { + left: 100%; +} +.carousel-control { + position: absolute; + top: 0; + bottom: 0; + left: 0; + width: 15%; + font-size: 20px; + color: #fff; + text-align: center; + text-shadow: 0 1px 2px rgba(0, 0, 0, .6); + background-color: rgba(0, 0, 0, 0); + filter: alpha(opacity=50); + opacity: .5; +} +.carousel-control.left { + background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%); + background-image: -o-linear-gradient(left, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%); + background-image: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, .5)), to(rgba(0, 0, 0, .0001))); + background-image: linear-gradient(to right, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1); + background-repeat: repeat-x; +} +.carousel-control.right { + right: 0; + left: auto; + background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%); + background-image: -o-linear-gradient(left, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%); + background-image: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, .0001)), to(rgba(0, 0, 0, .5))); + background-image: linear-gradient(to right, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1); + background-repeat: repeat-x; +} +.carousel-control:hover, +.carousel-control:focus { + color: #fff; + text-decoration: none; + filter: alpha(opacity=90); + outline: 0; + opacity: .9; +} +.carousel-control .icon-prev, +.carousel-control .icon-next, +.carousel-control .glyphicon-chevron-left, +.carousel-control .glyphicon-chevron-right { + position: absolute; + top: 50%; + z-index: 5; + display: inline-block; + margin-top: -10px; +} +.carousel-control .icon-prev, +.carousel-control .glyphicon-chevron-left { + left: 50%; + margin-left: -10px; +} +.carousel-control .icon-next, +.carousel-control .glyphicon-chevron-right { + right: 50%; + margin-right: -10px; +} +.carousel-control .icon-prev, +.carousel-control .icon-next { + width: 20px; + height: 20px; + font-family: serif; + line-height: 1; +} +.carousel-control .icon-prev:before { + content: '\2039'; +} +.carousel-control .icon-next:before { + content: '\203a'; +} +.carousel-indicators { + position: absolute; + bottom: 10px; + left: 50%; + z-index: 15; + width: 60%; + padding-left: 0; + margin-left: -30%; + text-align: center; + list-style: none; +} +.carousel-indicators li { + display: inline-block; + width: 10px; + height: 10px; + margin: 1px; + text-indent: -999px; + cursor: pointer; + background-color: #000 \9; + background-color: rgba(0, 0, 0, 0); + border: 1px solid #fff; + border-radius: 10px; +} +.carousel-indicators .active { + width: 12px; + height: 12px; + margin: 0; + background-color: #fff; +} +.carousel-caption { + position: absolute; + right: 15%; + bottom: 20px; + left: 15%; + z-index: 10; + padding-top: 20px; + padding-bottom: 20px; + color: #fff; + text-align: center; + text-shadow: 0 1px 2px rgba(0, 0, 0, .6); +} +.carousel-caption .btn { + text-shadow: none; +} +@media screen and (min-width: 768px) { + .carousel-control .glyphicon-chevron-left, + .carousel-control .glyphicon-chevron-right, + .carousel-control .icon-prev, + .carousel-control .icon-next { + width: 30px; + height: 30px; + margin-top: -10px; + font-size: 30px; + } + .carousel-control .glyphicon-chevron-left, + .carousel-control .icon-prev { + margin-left: -10px; + } + .carousel-control .glyphicon-chevron-right, + .carousel-control .icon-next { + margin-right: -10px; + } + .carousel-caption { + right: 20%; + left: 20%; + padding-bottom: 30px; + } + .carousel-indicators { + bottom: 20px; + } +} +.clearfix:before, +.clearfix:after, +.dl-horizontal dd:before, +.dl-horizontal dd:after, +.container:before, +.container:after, +.container-fluid:before, +.container-fluid:after, +.row:before, +.row:after, +.form-horizontal .form-group:before, +.form-horizontal .form-group:after, +.btn-toolbar:before, +.btn-toolbar:after, +.btn-group-vertical > .btn-group:before, +.btn-group-vertical > .btn-group:after, +.nav:before, +.nav:after, +.navbar:before, +.navbar:after, +.navbar-header:before, +.navbar-header:after, +.navbar-collapse:before, +.navbar-collapse:after, +.pager:before, +.pager:after, +.panel-body:before, +.panel-body:after, +.modal-header:before, +.modal-header:after, +.modal-footer:before, +.modal-footer:after { + display: table; + content: " "; +} +.clearfix:after, +.dl-horizontal dd:after, +.container:after, +.container-fluid:after, +.row:after, +.form-horizontal .form-group:after, +.btn-toolbar:after, +.btn-group-vertical > .btn-group:after, +.nav:after, +.navbar:after, +.navbar-header:after, +.navbar-collapse:after, +.pager:after, +.panel-body:after, +.modal-header:after, +.modal-footer:after { + clear: both; +} +.center-block { + display: block; + margin-right: auto; + margin-left: auto; +} +.pull-right { + float: right !important; +} +.pull-left { + float: left !important; +} +.hide { + display: none !important; +} +.show { + display: block !important; +} +.invisible { + visibility: hidden; +} +.text-hide { + font: 0/0 a; + color: transparent; + text-shadow: none; + background-color: transparent; + border: 0; +} +.hidden { + display: none !important; +} +.affix { + position: fixed; +} +@-ms-viewport { + width: device-width; +} +.visible-xs, +.visible-sm, +.visible-md, +.visible-lg { + display: none !important; +} +.visible-xs-block, +.visible-xs-inline, +.visible-xs-inline-block, +.visible-sm-block, +.visible-sm-inline, +.visible-sm-inline-block, +.visible-md-block, +.visible-md-inline, +.visible-md-inline-block, +.visible-lg-block, +.visible-lg-inline, +.visible-lg-inline-block { + display: none !important; +} +@media (max-width: 767px) { + .visible-xs { + display: block !important; + } + table.visible-xs { + display: table !important; + } + tr.visible-xs { + display: table-row !important; + } + th.visible-xs, + td.visible-xs { + display: table-cell !important; + } +} +@media (max-width: 767px) { + .visible-xs-block { + display: block !important; + } +} +@media (max-width: 767px) { + .visible-xs-inline { + display: inline !important; + } +} +@media (max-width: 767px) { + .visible-xs-inline-block { + display: inline-block !important; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .visible-sm { + display: block !important; + } + table.visible-sm { + display: table !important; + } + tr.visible-sm { + display: table-row !important; + } + th.visible-sm, + td.visible-sm { + display: table-cell !important; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .visible-sm-block { + display: block !important; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .visible-sm-inline { + display: inline !important; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .visible-sm-inline-block { + display: inline-block !important; + } +} +@media (min-width: 992px) and (max-width: 1199px) { + .visible-md { + display: block !important; + } + table.visible-md { + display: table !important; + } + tr.visible-md { + display: table-row !important; + } + th.visible-md, + td.visible-md { + display: table-cell !important; + } +} +@media (min-width: 992px) and (max-width: 1199px) { + .visible-md-block { + display: block !important; + } +} +@media (min-width: 992px) and (max-width: 1199px) { + .visible-md-inline { + display: inline !important; + } +} +@media (min-width: 992px) and (max-width: 1199px) { + .visible-md-inline-block { + display: inline-block !important; + } +} +@media (min-width: 1200px) { + .visible-lg { + display: block !important; + } + table.visible-lg { + display: table !important; + } + tr.visible-lg { + display: table-row !important; + } + th.visible-lg, + td.visible-lg { + display: table-cell !important; + } +} +@media (min-width: 1200px) { + .visible-lg-block { + display: block !important; + } +} +@media (min-width: 1200px) { + .visible-lg-inline { + display: inline !important; + } +} +@media (min-width: 1200px) { + .visible-lg-inline-block { + display: inline-block !important; + } +} +@media (max-width: 767px) { + .hidden-xs { + display: none !important; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .hidden-sm { + display: none !important; + } +} +@media (min-width: 992px) and (max-width: 1199px) { + .hidden-md { + display: none !important; + } +} +@media (min-width: 1200px) { + .hidden-lg { + display: none !important; + } +} +.visible-print { + display: none !important; +} +@media print { + .visible-print { + display: block !important; + } + table.visible-print { + display: table !important; + } + tr.visible-print { + display: table-row !important; + } + th.visible-print, + td.visible-print { + display: table-cell !important; + } +} +.visible-print-block { + display: none !important; +} +@media print { + .visible-print-block { + display: block !important; + } +} +.visible-print-inline { + display: none !important; +} +@media print { + .visible-print-inline { + display: inline !important; + } +} +.visible-print-inline-block { + display: none !important; +} +@media print { + .visible-print-inline-block { + display: inline-block !important; + } +} +@media print { + .hidden-print { + display: none !important; + } +} +/*# sourceMappingURL=bootstrap.css.map */ diff --git a/main/webapp/static/css/bootstrap.min.css b/main/webapp/static/css/bootstrap.min.css new file mode 100644 index 0000000..3b2a01b --- /dev/null +++ b/main/webapp/static/css/bootstrap.min.css @@ -0,0 +1,9 @@ +/*! + * Bootstrap v2.2.1 + * + * Copyright 2012 Twitter, Inc + * Licensed under the Apache License v2.0 + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Designed and built with all the love in the world @twitter by @mdo and @fat. + */article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block}audio,canvas,video{display:inline-block;*display:inline;*zoom:1}audio:not([controls]){display:none}html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}a:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}a:hover,a:active{outline:0}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{width:auto\9;height:auto;max-width:100%;vertical-align:middle;border:0;-ms-interpolation-mode:bicubic}#map_canvas img,.google-maps img{max-width:none}button,input,select,textarea{margin:0;font-size:100%;vertical-align:middle}button,input{*overflow:visible;line-height:normal}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}button,html input[type="button"],input[type="reset"],input[type="submit"]{cursor:pointer;-webkit-appearance:button}input[type="search"]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type="search"]::-webkit-search-decoration,input[type="search"]::-webkit-search-cancel-button{-webkit-appearance:none}textarea{overflow:auto;vertical-align:top}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;line-height:0;content:""}.clearfix:after{clear:both}.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.input-block-level{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}body{margin:0;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:20px;color:#333;background-color:#fff}a{color:#08c;text-decoration:none}a:hover{color:#005580;text-decoration:underline}.img-rounded{-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.img-polaroid{padding:4px;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);-webkit-box-shadow:0 1px 3px rgba(0,0,0,0.1);-moz-box-shadow:0 1px 3px rgba(0,0,0,0.1);box-shadow:0 1px 3px rgba(0,0,0,0.1)}.img-circle{-webkit-border-radius:500px;-moz-border-radius:500px;border-radius:500px}.row{margin-left:-20px;*zoom:1}.row:before,.row:after{display:table;line-height:0;content:""}.row:after{clear:both}[class*="span"]{float:left;min-height:1px;margin-left:20px}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:940px}.span12{width:940px}.span11{width:860px}.span10{width:780px}.span9{width:700px}.span8{width:620px}.span7{width:540px}.span6{width:460px}.span5{width:380px}.span4{width:300px}.span3{width:220px}.span2{width:140px}.span1{width:60px}.offset12{margin-left:980px}.offset11{margin-left:900px}.offset10{margin-left:820px}.offset9{margin-left:740px}.offset8{margin-left:660px}.offset7{margin-left:580px}.offset6{margin-left:500px}.offset5{margin-left:420px}.offset4{margin-left:340px}.offset3{margin-left:260px}.offset2{margin-left:180px}.offset1{margin-left:100px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;line-height:0;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:2.127659574468085%;*margin-left:2.074468085106383%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .controls-row [class*="span"]+[class*="span"]{margin-left:2.127659574468085%}.row-fluid .span12{width:100%;*width:99.94680851063829%}.row-fluid .span11{width:91.48936170212765%;*width:91.43617021276594%}.row-fluid .span10{width:82.97872340425532%;*width:82.92553191489361%}.row-fluid .span9{width:74.46808510638297%;*width:74.41489361702126%}.row-fluid .span8{width:65.95744680851064%;*width:65.90425531914893%}.row-fluid .span7{width:57.44680851063829%;*width:57.39361702127659%}.row-fluid .span6{width:48.93617021276595%;*width:48.88297872340425%}.row-fluid .span5{width:40.42553191489362%;*width:40.37234042553192%}.row-fluid .span4{width:31.914893617021278%;*width:31.861702127659576%}.row-fluid .span3{width:23.404255319148934%;*width:23.351063829787233%}.row-fluid .span2{width:14.893617021276595%;*width:14.840425531914894%}.row-fluid .span1{width:6.382978723404255%;*width:6.329787234042553%}.row-fluid .offset12{margin-left:104.25531914893617%;*margin-left:104.14893617021275%}.row-fluid .offset12:first-child{margin-left:102.12765957446808%;*margin-left:102.02127659574467%}.row-fluid .offset11{margin-left:95.74468085106382%;*margin-left:95.6382978723404%}.row-fluid .offset11:first-child{margin-left:93.61702127659574%;*margin-left:93.51063829787232%}.row-fluid .offset10{margin-left:87.23404255319149%;*margin-left:87.12765957446807%}.row-fluid .offset10:first-child{margin-left:85.1063829787234%;*margin-left:84.99999999999999%}.row-fluid .offset9{margin-left:78.72340425531914%;*margin-left:78.61702127659572%}.row-fluid .offset9:first-child{margin-left:76.59574468085106%;*margin-left:76.48936170212764%}.row-fluid .offset8{margin-left:70.2127659574468%;*margin-left:70.10638297872339%}.row-fluid .offset8:first-child{margin-left:68.08510638297872%;*margin-left:67.9787234042553%}.row-fluid .offset7{margin-left:61.70212765957446%;*margin-left:61.59574468085106%}.row-fluid .offset7:first-child{margin-left:59.574468085106375%;*margin-left:59.46808510638297%}.row-fluid .offset6{margin-left:53.191489361702125%;*margin-left:53.085106382978715%}.row-fluid .offset6:first-child{margin-left:51.063829787234035%;*margin-left:50.95744680851063%}.row-fluid .offset5{margin-left:44.68085106382979%;*margin-left:44.57446808510638%}.row-fluid .offset5:first-child{margin-left:42.5531914893617%;*margin-left:42.4468085106383%}.row-fluid .offset4{margin-left:36.170212765957444%;*margin-left:36.06382978723405%}.row-fluid .offset4:first-child{margin-left:34.04255319148936%;*margin-left:33.93617021276596%}.row-fluid .offset3{margin-left:27.659574468085104%;*margin-left:27.5531914893617%}.row-fluid .offset3:first-child{margin-left:25.53191489361702%;*margin-left:25.425531914893618%}.row-fluid .offset2{margin-left:19.148936170212764%;*margin-left:19.04255319148936%}.row-fluid .offset2:first-child{margin-left:17.02127659574468%;*margin-left:16.914893617021278%}.row-fluid .offset1{margin-left:10.638297872340425%;*margin-left:10.53191489361702%}.row-fluid .offset1:first-child{margin-left:8.51063829787234%;*margin-left:8.404255319148938%}[class*="span"].hide,.row-fluid [class*="span"].hide{display:none}[class*="span"].pull-right,.row-fluid [class*="span"].pull-right{float:right}.container{margin-right:auto;margin-left:auto;*zoom:1}.container:before,.container:after{display:table;line-height:0;content:""}.container:after{clear:both}.container-fluid{padding-right:20px;padding-left:20px;*zoom:1}.container-fluid:before,.container-fluid:after{display:table;line-height:0;content:""}.container-fluid:after{clear:both}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:21px;font-weight:200;line-height:30px}small{font-size:85%}strong{font-weight:bold}em{font-style:italic}cite{font-style:normal}.muted{color:#999}.text-warning{color:#c09853}a.text-warning:hover{color:#a47e3c}.text-error{color:#b94a48}a.text-error:hover{color:#953b39}.text-info{color:#3a87ad}a.text-info:hover{color:#2d6987}.text-success{color:#468847}a.text-success:hover{color:#356635}h1,h2,h3,h4,h5,h6{margin:10px 0;font-family:inherit;font-weight:bold;line-height:20px;color:inherit;text-rendering:optimizelegibility}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small{font-weight:normal;line-height:1;color:#999}h1,h2,h3{line-height:40px}h1{font-size:38.5px}h2{font-size:31.5px}h3{font-size:24.5px}h4{font-size:17.5px}h5{font-size:14px}h6{font-size:11.9px}h1 small{font-size:24.5px}h2 small{font-size:17.5px}h3 small{font-size:14px}h4 small{font-size:14px}.page-header{padding-bottom:9px;margin:20px 0 30px;border-bottom:1px solid #eee}ul,ol{padding:0;margin:0 0 10px 25px}ul ul,ul ol,ol ol,ol ul{margin-bottom:0}li{line-height:20px}ul.unstyled,ol.unstyled{margin-left:0;list-style:none}dl{margin-bottom:20px}dt,dd{line-height:20px}dt{font-weight:bold}dd{margin-left:10px}.dl-horizontal{*zoom:1}.dl-horizontal:before,.dl-horizontal:after{display:table;line-height:0;content:""}.dl-horizontal:after{clear:both}.dl-horizontal dt{float:left;width:160px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}hr{margin:20px 0;border:0;border-top:1px solid #eee;border-bottom:1px solid #fff}abbr[title],abbr[data-original-title]{cursor:help;border-bottom:1px dotted #999}abbr.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:0 0 0 15px;margin:0 0 20px;border-left:5px solid #eee}blockquote p{margin-bottom:0;font-size:16px;font-weight:300;line-height:25px}blockquote small{display:block;line-height:20px;color:#999}blockquote small:before{content:'\2014 \00A0'}blockquote.pull-right{float:right;padding-right:15px;padding-left:0;border-right:5px solid #eee;border-left:0}blockquote.pull-right p,blockquote.pull-right small{text-align:right}blockquote.pull-right small:before{content:''}blockquote.pull-right small:after{content:'\00A0 \2014'}q:before,q:after,blockquote:before,blockquote:after{content:""}address{display:block;margin-bottom:20px;font-style:normal;line-height:20px}code,pre{padding:0 3px 2px;font-family:Monaco,Menlo,Consolas,"Courier New",monospace;font-size:12px;color:#333;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}code{padding:2px 4px;color:#d14;background-color:#f7f7f9;border:1px solid #e1e1e8}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:20px;word-break:break-all;word-wrap:break-word;white-space:pre;white-space:pre-wrap;background-color:#f5f5f5;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.15);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}pre.prettyprint{margin-bottom:20px}pre code{padding:0;color:inherit;background-color:transparent;border:0}.pre-scrollable{max-height:340px;overflow-y:scroll}form{margin:0 0 20px}fieldset{padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:40px;color:#333;border:0;border-bottom:1px solid #e5e5e5}legend small{font-size:15px;color:#999}label,input,button,select,textarea{font-size:14px;font-weight:normal;line-height:20px}input,button,select,textarea{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif}label{display:block;margin-bottom:5px}select,textarea,input[type="text"],input[type="password"],input[type="datetime"],input[type="datetime-local"],input[type="date"],input[type="month"],input[type="time"],input[type="week"],input[type="number"],input[type="email"],input[type="url"],input[type="search"],input[type="tel"],input[type="color"],.uneditable-input{display:inline-block;height:20px;padding:4px 6px;margin-bottom:10px;font-size:14px;line-height:20px;color:#555;vertical-align:middle;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}input,textarea,.uneditable-input{width:206px}textarea{height:auto}textarea,input[type="text"],input[type="password"],input[type="datetime"],input[type="datetime-local"],input[type="date"],input[type="month"],input[type="time"],input[type="week"],input[type="number"],input[type="email"],input[type="url"],input[type="search"],input[type="tel"],input[type="color"],.uneditable-input{background-color:#fff;border:1px solid #ccc;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-webkit-transition:border linear .2s,box-shadow linear .2s;-moz-transition:border linear .2s,box-shadow linear .2s;-o-transition:border linear .2s,box-shadow linear .2s;transition:border linear .2s,box-shadow linear .2s}textarea:focus,input[type="text"]:focus,input[type="password"]:focus,input[type="datetime"]:focus,input[type="datetime-local"]:focus,input[type="date"]:focus,input[type="month"]:focus,input[type="time"]:focus,input[type="week"]:focus,input[type="number"]:focus,input[type="email"]:focus,input[type="url"]:focus,input[type="search"]:focus,input[type="tel"]:focus,input[type="color"]:focus,.uneditable-input:focus{border-color:rgba(82,168,236,0.8);outline:0;outline:thin dotted \9;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6)}input[type="radio"],input[type="checkbox"]{margin:4px 0 0;margin-top:1px \9;*margin-top:0;line-height:normal;cursor:pointer}input[type="file"],input[type="image"],input[type="submit"],input[type="reset"],input[type="button"],input[type="radio"],input[type="checkbox"]{width:auto}select,input[type="file"]{height:30px;*margin-top:4px;line-height:30px}select{width:220px;background-color:#fff;border:1px solid #ccc}select[multiple],select[size]{height:auto}select:focus,input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.uneditable-input,.uneditable-textarea{color:#999;cursor:not-allowed;background-color:#fcfcfc;border-color:#ccc;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.025);-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,0.025);box-shadow:inset 0 1px 2px rgba(0,0,0,0.025)}.uneditable-input{overflow:hidden;white-space:nowrap}.uneditable-textarea{width:auto;height:auto}input:-moz-placeholder,textarea:-moz-placeholder{color:#999}input:-ms-input-placeholder,textarea:-ms-input-placeholder{color:#999}input::-webkit-input-placeholder,textarea::-webkit-input-placeholder{color:#999}.radio,.checkbox{min-height:20px;padding-left:20px}.radio input[type="radio"],.checkbox input[type="checkbox"]{float:left;margin-left:-20px}.controls>.radio:first-child,.controls>.checkbox:first-child{padding-top:5px}.radio.inline,.checkbox.inline{display:inline-block;padding-top:5px;margin-bottom:0;vertical-align:middle}.radio.inline+.radio.inline,.checkbox.inline+.checkbox.inline{margin-left:10px}.input-mini{width:60px}.input-small{width:90px}.input-medium{width:150px}.input-large{width:210px}.input-xlarge{width:270px}.input-xxlarge{width:530px}input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input[class*="span"],.row-fluid input[class*="span"],.row-fluid select[class*="span"],.row-fluid textarea[class*="span"],.row-fluid .uneditable-input[class*="span"]{float:none;margin-left:0}.input-append input[class*="span"],.input-append .uneditable-input[class*="span"],.input-prepend input[class*="span"],.input-prepend .uneditable-input[class*="span"],.row-fluid input[class*="span"],.row-fluid select[class*="span"],.row-fluid textarea[class*="span"],.row-fluid .uneditable-input[class*="span"],.row-fluid .input-prepend [class*="span"],.row-fluid .input-append [class*="span"]{display:inline-block}input,textarea,.uneditable-input{margin-left:0}.controls-row [class*="span"]+[class*="span"]{margin-left:20px}input.span12,textarea.span12,.uneditable-input.span12{width:926px}input.span11,textarea.span11,.uneditable-input.span11{width:846px}input.span10,textarea.span10,.uneditable-input.span10{width:766px}input.span9,textarea.span9,.uneditable-input.span9{width:686px}input.span8,textarea.span8,.uneditable-input.span8{width:606px}input.span7,textarea.span7,.uneditable-input.span7{width:526px}input.span6,textarea.span6,.uneditable-input.span6{width:446px}input.span5,textarea.span5,.uneditable-input.span5{width:366px}input.span4,textarea.span4,.uneditable-input.span4{width:286px}input.span3,textarea.span3,.uneditable-input.span3{width:206px}input.span2,textarea.span2,.uneditable-input.span2{width:126px}input.span1,textarea.span1,.uneditable-input.span1{width:46px}.controls-row{*zoom:1}.controls-row:before,.controls-row:after{display:table;line-height:0;content:""}.controls-row:after{clear:both}.controls-row [class*="span"],.row-fluid .controls-row [class*="span"]{float:left}.controls-row .checkbox[class*="span"],.controls-row .radio[class*="span"]{padding-top:5px}input[disabled],select[disabled],textarea[disabled],input[readonly],select[readonly],textarea[readonly]{cursor:not-allowed;background-color:#eee}input[type="radio"][disabled],input[type="checkbox"][disabled],input[type="radio"][readonly],input[type="checkbox"][readonly]{background-color:transparent}.control-group.warning>label,.control-group.warning .help-block,.control-group.warning .help-inline{color:#c09853}.control-group.warning .checkbox,.control-group.warning .radio,.control-group.warning input,.control-group.warning select,.control-group.warning textarea{color:#c09853}.control-group.warning input,.control-group.warning select,.control-group.warning textarea{border-color:#c09853;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.control-group.warning input:focus,.control-group.warning select:focus,.control-group.warning textarea:focus{border-color:#a47e3c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #dbc59e;-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #dbc59e;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #dbc59e}.control-group.warning .input-prepend .add-on,.control-group.warning .input-append .add-on{color:#c09853;background-color:#fcf8e3;border-color:#c09853}.control-group.error>label,.control-group.error .help-block,.control-group.error .help-inline{color:#b94a48}.control-group.error .checkbox,.control-group.error .radio,.control-group.error input,.control-group.error select,.control-group.error textarea{color:#b94a48}.control-group.error input,.control-group.error select,.control-group.error textarea{border-color:#b94a48;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.control-group.error input:focus,.control-group.error select:focus,.control-group.error textarea:focus{border-color:#953b39;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #d59392;-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #d59392;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #d59392}.control-group.error .input-prepend .add-on,.control-group.error .input-append .add-on{color:#b94a48;background-color:#f2dede;border-color:#b94a48}.control-group.success>label,.control-group.success .help-block,.control-group.success .help-inline{color:#468847}.control-group.success .checkbox,.control-group.success .radio,.control-group.success input,.control-group.success select,.control-group.success textarea{color:#468847}.control-group.success input,.control-group.success select,.control-group.success textarea{border-color:#468847;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.control-group.success input:focus,.control-group.success select:focus,.control-group.success textarea:focus{border-color:#356635;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7aba7b;-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7aba7b;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7aba7b}.control-group.success .input-prepend .add-on,.control-group.success .input-append .add-on{color:#468847;background-color:#dff0d8;border-color:#468847}.control-group.info>label,.control-group.info .help-block,.control-group.info .help-inline{color:#3a87ad}.control-group.info .checkbox,.control-group.info .radio,.control-group.info input,.control-group.info select,.control-group.info textarea{color:#3a87ad}.control-group.info input,.control-group.info select,.control-group.info textarea{border-color:#3a87ad;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.control-group.info input:focus,.control-group.info select:focus,.control-group.info textarea:focus{border-color:#2d6987;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7ab5d3;-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7ab5d3;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7ab5d3}.control-group.info .input-prepend .add-on,.control-group.info .input-append .add-on{color:#3a87ad;background-color:#d9edf7;border-color:#3a87ad}input:focus:required:invalid,textarea:focus:required:invalid,select:focus:required:invalid{color:#b94a48;border-color:#ee5f5b}input:focus:required:invalid:focus,textarea:focus:required:invalid:focus,select:focus:required:invalid:focus{border-color:#e9322d;-webkit-box-shadow:0 0 6px #f8b9b7;-moz-box-shadow:0 0 6px #f8b9b7;box-shadow:0 0 6px #f8b9b7}.form-actions{padding:19px 20px 20px;margin-top:20px;margin-bottom:20px;background-color:#f5f5f5;border-top:1px solid #e5e5e5;*zoom:1}.form-actions:before,.form-actions:after{display:table;line-height:0;content:""}.form-actions:after{clear:both}.help-block,.help-inline{color:#595959}.help-block{display:block;margin-bottom:10px}.help-inline{display:inline-block;*display:inline;padding-left:5px;vertical-align:middle;*zoom:1}.input-append,.input-prepend{margin-bottom:5px;font-size:0;white-space:nowrap}.input-append input,.input-prepend input,.input-append select,.input-prepend select,.input-append .uneditable-input,.input-prepend .uneditable-input,.input-append .dropdown-menu,.input-prepend .dropdown-menu{font-size:14px}.input-append input,.input-prepend input,.input-append select,.input-prepend select,.input-append .uneditable-input,.input-prepend .uneditable-input{position:relative;margin-bottom:0;*margin-left:0;vertical-align:top;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.input-append input:focus,.input-prepend input:focus,.input-append select:focus,.input-prepend select:focus,.input-append .uneditable-input:focus,.input-prepend .uneditable-input:focus{z-index:2}.input-append .add-on,.input-prepend .add-on{display:inline-block;width:auto;height:20px;min-width:16px;padding:4px 5px;font-size:14px;font-weight:normal;line-height:20px;text-align:center;text-shadow:0 1px 0 #fff;background-color:#eee;border:1px solid #ccc}.input-append .add-on,.input-prepend .add-on,.input-append .btn,.input-prepend .btn{vertical-align:top;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.input-append .active,.input-prepend .active{background-color:#a9dba9;border-color:#46a546}.input-prepend .add-on,.input-prepend .btn{margin-right:-1px}.input-prepend .add-on:first-child,.input-prepend .btn:first-child{-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.input-append input,.input-append select,.input-append .uneditable-input{-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.input-append input+.btn-group .btn,.input-append select+.btn-group .btn,.input-append .uneditable-input+.btn-group .btn{-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.input-append .add-on,.input-append .btn,.input-append .btn-group{margin-left:-1px}.input-append .add-on:last-child,.input-append .btn:last-child{-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.input-prepend.input-append input,.input-prepend.input-append select,.input-prepend.input-append .uneditable-input{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.input-prepend.input-append input+.btn-group .btn,.input-prepend.input-append select+.btn-group .btn,.input-prepend.input-append .uneditable-input+.btn-group .btn{-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.input-prepend.input-append .add-on:first-child,.input-prepend.input-append .btn:first-child{margin-right:-1px;-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.input-prepend.input-append .add-on:last-child,.input-prepend.input-append .btn:last-child{margin-left:-1px;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.input-prepend.input-append .btn-group:first-child{margin-left:0}input.search-query{padding-right:14px;padding-right:4px \9;padding-left:14px;padding-left:4px \9;margin-bottom:0;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px}.form-search .input-append .search-query,.form-search .input-prepend .search-query{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.form-search .input-append .search-query{-webkit-border-radius:14px 0 0 14px;-moz-border-radius:14px 0 0 14px;border-radius:14px 0 0 14px}.form-search .input-append .btn{-webkit-border-radius:0 14px 14px 0;-moz-border-radius:0 14px 14px 0;border-radius:0 14px 14px 0}.form-search .input-prepend .search-query{-webkit-border-radius:0 14px 14px 0;-moz-border-radius:0 14px 14px 0;border-radius:0 14px 14px 0}.form-search .input-prepend .btn{-webkit-border-radius:14px 0 0 14px;-moz-border-radius:14px 0 0 14px;border-radius:14px 0 0 14px}.form-search input,.form-inline input,.form-horizontal input,.form-search textarea,.form-inline textarea,.form-horizontal textarea,.form-search select,.form-inline select,.form-horizontal select,.form-search .help-inline,.form-inline .help-inline,.form-horizontal .help-inline,.form-search .uneditable-input,.form-inline .uneditable-input,.form-horizontal .uneditable-input,.form-search .input-prepend,.form-inline .input-prepend,.form-horizontal .input-prepend,.form-search .input-append,.form-inline .input-append,.form-horizontal .input-append{display:inline-block;*display:inline;margin-bottom:0;vertical-align:middle;*zoom:1}.form-search .hide,.form-inline .hide,.form-horizontal .hide{display:none}.form-search label,.form-inline label,.form-search .btn-group,.form-inline .btn-group{display:inline-block}.form-search .input-append,.form-inline .input-append,.form-search .input-prepend,.form-inline .input-prepend{margin-bottom:0}.form-search .radio,.form-search .checkbox,.form-inline .radio,.form-inline .checkbox{padding-left:0;margin-bottom:0;vertical-align:middle}.form-search .radio input[type="radio"],.form-search .checkbox input[type="checkbox"],.form-inline .radio input[type="radio"],.form-inline .checkbox input[type="checkbox"]{float:left;margin-right:3px;margin-left:0}.control-group{margin-bottom:10px}legend+.control-group{margin-top:20px;-webkit-margin-top-collapse:separate}.form-horizontal .control-group{margin-bottom:20px;*zoom:1}.form-horizontal .control-group:before,.form-horizontal .control-group:after{display:table;line-height:0;content:""}.form-horizontal .control-group:after{clear:both}.form-horizontal .control-label{float:left;width:160px;padding-top:5px;text-align:right}.form-horizontal .controls{*display:inline-block;*padding-left:20px;margin-left:180px;*margin-left:0}.form-horizontal .controls:first-child{*padding-left:180px}.form-horizontal .help-block{margin-bottom:0}.form-horizontal input+.help-block,.form-horizontal select+.help-block,.form-horizontal textarea+.help-block{margin-top:10px}.form-horizontal .form-actions{padding-left:180px}table{max-width:100%;background-color:transparent;border-collapse:collapse;border-spacing:0}.table{width:100%;margin-bottom:20px}.table th,.table td{padding:8px;line-height:20px;text-align:left;vertical-align:top;border-top:1px solid #ddd}.table th{font-weight:bold}.table thead th{vertical-align:bottom}.table caption+thead tr:first-child th,.table caption+thead tr:first-child td,.table colgroup+thead tr:first-child th,.table colgroup+thead tr:first-child td,.table thead:first-child tr:first-child th,.table thead:first-child tr:first-child td{border-top:0}.table tbody+tbody{border-top:2px solid #ddd}.table-condensed th,.table-condensed td{padding:4px 5px}.table-bordered{border:1px solid #ddd;border-collapse:separate;*border-collapse:collapse;border-left:0;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.table-bordered th,.table-bordered td{border-left:1px solid #ddd}.table-bordered caption+thead tr:first-child th,.table-bordered caption+tbody tr:first-child th,.table-bordered caption+tbody tr:first-child td,.table-bordered colgroup+thead tr:first-child th,.table-bordered colgroup+tbody tr:first-child th,.table-bordered colgroup+tbody tr:first-child td,.table-bordered thead:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child td{border-top:0}.table-bordered thead:first-child tr:first-child th:first-child,.table-bordered tbody:first-child tr:first-child td:first-child{-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topleft:4px}.table-bordered thead:first-child tr:first-child th:last-child,.table-bordered tbody:first-child tr:first-child td:last-child{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-topright:4px}.table-bordered thead:last-child tr:last-child th:first-child,.table-bordered tbody:last-child tr:last-child td:first-child,.table-bordered tfoot:last-child tr:last-child td:first-child{-webkit-border-radius:0 0 0 4px;-moz-border-radius:0 0 0 4px;border-radius:0 0 0 4px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-moz-border-radius-bottomleft:4px}.table-bordered thead:last-child tr:last-child th:last-child,.table-bordered tbody:last-child tr:last-child td:last-child,.table-bordered tfoot:last-child tr:last-child td:last-child{-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-bottomright:4px}.table-bordered caption+thead tr:first-child th:first-child,.table-bordered caption+tbody tr:first-child td:first-child,.table-bordered colgroup+thead tr:first-child th:first-child,.table-bordered colgroup+tbody tr:first-child td:first-child{-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topleft:4px}.table-bordered caption+thead tr:first-child th:last-child,.table-bordered caption+tbody tr:first-child td:last-child,.table-bordered colgroup+thead tr:first-child th:last-child,.table-bordered colgroup+tbody tr:first-child td:last-child{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-topright:4px}.table-striped tbody tr:nth-child(odd) td,.table-striped tbody tr:nth-child(odd) th{background-color:#f9f9f9}.table-hover tbody tr:hover td,.table-hover tbody tr:hover th{background-color:#f5f5f5}table td[class*="span"],table th[class*="span"],.row-fluid table td[class*="span"],.row-fluid table th[class*="span"]{display:table-cell;float:none;margin-left:0}.table td.span1,.table th.span1{float:none;width:44px;margin-left:0}.table td.span2,.table th.span2{float:none;width:124px;margin-left:0}.table td.span3,.table th.span3{float:none;width:204px;margin-left:0}.table td.span4,.table th.span4{float:none;width:284px;margin-left:0}.table td.span5,.table th.span5{float:none;width:364px;margin-left:0}.table td.span6,.table th.span6{float:none;width:444px;margin-left:0}.table td.span7,.table th.span7{float:none;width:524px;margin-left:0}.table td.span8,.table th.span8{float:none;width:604px;margin-left:0}.table td.span9,.table th.span9{float:none;width:684px;margin-left:0}.table td.span10,.table th.span10{float:none;width:764px;margin-left:0}.table td.span11,.table th.span11{float:none;width:844px;margin-left:0}.table td.span12,.table th.span12{float:none;width:924px;margin-left:0}.table tbody tr.success td{background-color:#dff0d8}.table tbody tr.error td{background-color:#f2dede}.table tbody tr.warning td{background-color:#fcf8e3}.table tbody tr.info td{background-color:#d9edf7}.table-hover tbody tr.success:hover td{background-color:#d0e9c6}.table-hover tbody tr.error:hover td{background-color:#ebcccc}.table-hover tbody tr.warning:hover td{background-color:#faf2cc}.table-hover tbody tr.info:hover td{background-color:#c4e3f3}[class^="icon-"],[class*=" icon-"]{display:inline-block;width:14px;height:14px;margin-top:1px;*margin-right:.3em;line-height:14px;vertical-align:text-top;background-image:url("../img/glyphicons-halflings.png");background-position:14px 14px;background-repeat:no-repeat}.icon-white,.nav-pills>.active>a>[class^="icon-"],.nav-pills>.active>a>[class*=" icon-"],.nav-list>.active>a>[class^="icon-"],.nav-list>.active>a>[class*=" icon-"],.navbar-inverse .nav>.active>a>[class^="icon-"],.navbar-inverse .nav>.active>a>[class*=" icon-"],.dropdown-menu>li>a:hover>[class^="icon-"],.dropdown-menu>li>a:hover>[class*=" icon-"],.dropdown-menu>.active>a>[class^="icon-"],.dropdown-menu>.active>a>[class*=" icon-"],.dropdown-submenu:hover>a>[class^="icon-"],.dropdown-submenu:hover>a>[class*=" icon-"]{background-image:url("../img/glyphicons-halflings-white.png")}.icon-glass{background-position:0 0}.icon-music{background-position:-24px 0}.icon-search{background-position:-48px 0}.icon-envelope{background-position:-72px 0}.icon-heart{background-position:-96px 0}.icon-star{background-position:-120px 0}.icon-star-empty{background-position:-144px 0}.icon-user{background-position:-168px 0}.icon-film{background-position:-192px 0}.icon-th-large{background-position:-216px 0}.icon-th{background-position:-240px 0}.icon-th-list{background-position:-264px 0}.icon-ok{background-position:-288px 0}.icon-remove{background-position:-312px 0}.icon-zoom-in{background-position:-336px 0}.icon-zoom-out{background-position:-360px 0}.icon-off{background-position:-384px 0}.icon-signal{background-position:-408px 0}.icon-cog{background-position:-432px 0}.icon-trash{background-position:-456px 0}.icon-home{background-position:0 -24px}.icon-file{background-position:-24px -24px}.icon-time{background-position:-48px -24px}.icon-road{background-position:-72px -24px}.icon-download-alt{background-position:-96px -24px}.icon-download{background-position:-120px -24px}.icon-upload{background-position:-144px -24px}.icon-inbox{background-position:-168px -24px}.icon-play-circle{background-position:-192px -24px}.icon-repeat{background-position:-216px -24px}.icon-refresh{background-position:-240px -24px}.icon-list-alt{background-position:-264px -24px}.icon-lock{background-position:-287px -24px}.icon-flag{background-position:-312px -24px}.icon-headphones{background-position:-336px -24px}.icon-volume-off{background-position:-360px -24px}.icon-volume-down{background-position:-384px -24px}.icon-volume-up{background-position:-408px -24px}.icon-qrcode{background-position:-432px -24px}.icon-barcode{background-position:-456px -24px}.icon-tag{background-position:0 -48px}.icon-tags{background-position:-25px -48px}.icon-book{background-position:-48px -48px}.icon-bookmark{background-position:-72px -48px}.icon-print{background-position:-96px -48px}.icon-camera{background-position:-120px -48px}.icon-font{background-position:-144px -48px}.icon-bold{background-position:-167px -48px}.icon-italic{background-position:-192px -48px}.icon-text-height{background-position:-216px -48px}.icon-text-width{background-position:-240px -48px}.icon-align-left{background-position:-264px -48px}.icon-align-center{background-position:-288px -48px}.icon-align-right{background-position:-312px -48px}.icon-align-justify{background-position:-336px -48px}.icon-list{background-position:-360px -48px}.icon-indent-left{background-position:-384px -48px}.icon-indent-right{background-position:-408px -48px}.icon-facetime-video{background-position:-432px -48px}.icon-picture{background-position:-456px -48px}.icon-pencil{background-position:0 -72px}.icon-map-marker{background-position:-24px -72px}.icon-adjust{background-position:-48px -72px}.icon-tint{background-position:-72px -72px}.icon-edit{background-position:-96px -72px}.icon-share{background-position:-120px -72px}.icon-check{background-position:-144px -72px}.icon-move{background-position:-168px -72px}.icon-step-backward{background-position:-192px -72px}.icon-fast-backward{background-position:-216px -72px}.icon-backward{background-position:-240px -72px}.icon-play{background-position:-264px -72px}.icon-pause{background-position:-288px -72px}.icon-stop{background-position:-312px -72px}.icon-forward{background-position:-336px -72px}.icon-fast-forward{background-position:-360px -72px}.icon-step-forward{background-position:-384px -72px}.icon-eject{background-position:-408px -72px}.icon-chevron-left{background-position:-432px -72px}.icon-chevron-right{background-position:-456px -72px}.icon-plus-sign{background-position:0 -96px}.icon-minus-sign{background-position:-24px -96px}.icon-remove-sign{background-position:-48px -96px}.icon-ok-sign{background-position:-72px -96px}.icon-question-sign{background-position:-96px -96px}.icon-info-sign{background-position:-120px -96px}.icon-screenshot{background-position:-144px -96px}.icon-remove-circle{background-position:-168px -96px}.icon-ok-circle{background-position:-192px -96px}.icon-ban-circle{background-position:-216px -96px}.icon-arrow-left{background-position:-240px -96px}.icon-arrow-right{background-position:-264px -96px}.icon-arrow-up{background-position:-289px -96px}.icon-arrow-down{background-position:-312px -96px}.icon-share-alt{background-position:-336px -96px}.icon-resize-full{background-position:-360px -96px}.icon-resize-small{background-position:-384px -96px}.icon-plus{background-position:-408px -96px}.icon-minus{background-position:-433px -96px}.icon-asterisk{background-position:-456px -96px}.icon-exclamation-sign{background-position:0 -120px}.icon-gift{background-position:-24px -120px}.icon-leaf{background-position:-48px -120px}.icon-fire{background-position:-72px -120px}.icon-eye-open{background-position:-96px -120px}.icon-eye-close{background-position:-120px -120px}.icon-warning-sign{background-position:-144px -120px}.icon-plane{background-position:-168px -120px}.icon-calendar{background-position:-192px -120px}.icon-random{width:16px;background-position:-216px -120px}.icon-comment{background-position:-240px -120px}.icon-magnet{background-position:-264px -120px}.icon-chevron-up{background-position:-288px -120px}.icon-chevron-down{background-position:-313px -119px}.icon-retweet{background-position:-336px -120px}.icon-shopping-cart{background-position:-360px -120px}.icon-folder-close{background-position:-384px -120px}.icon-folder-open{width:16px;background-position:-408px -120px}.icon-resize-vertical{background-position:-432px -119px}.icon-resize-horizontal{background-position:-456px -118px}.icon-hdd{background-position:0 -144px}.icon-bullhorn{background-position:-24px -144px}.icon-bell{background-position:-48px -144px}.icon-certificate{background-position:-72px -144px}.icon-thumbs-up{background-position:-96px -144px}.icon-thumbs-down{background-position:-120px -144px}.icon-hand-right{background-position:-144px -144px}.icon-hand-left{background-position:-168px -144px}.icon-hand-up{background-position:-192px -144px}.icon-hand-down{background-position:-216px -144px}.icon-circle-arrow-right{background-position:-240px -144px}.icon-circle-arrow-left{background-position:-264px -144px}.icon-circle-arrow-up{background-position:-288px -144px}.icon-circle-arrow-down{background-position:-312px -144px}.icon-globe{background-position:-336px -144px}.icon-wrench{background-position:-360px -144px}.icon-tasks{background-position:-384px -144px}.icon-filter{background-position:-408px -144px}.icon-briefcase{background-position:-432px -144px}.icon-fullscreen{background-position:-456px -144px}.dropup,.dropdown{position:relative}.dropdown-toggle{*margin-bottom:-3px}.dropdown-toggle:active,.open .dropdown-toggle{outline:0}.caret{display:inline-block;width:0;height:0;vertical-align:top;border-top:4px solid #000;border-right:4px solid transparent;border-left:4px solid transparent;content:""}.dropdown .caret{margin-top:8px;margin-left:2px}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;list-style:none;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);*border-right-width:2px;*border-bottom-width:2px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);-moz-box-shadow:0 5px 10px rgba(0,0,0,0.2);box-shadow:0 5px 10px rgba(0,0,0,0.2);-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{*width:100%;height:1px;margin:9px 1px;*margin:-5px 0 5px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #fff}.dropdown-menu li>a{display:block;padding:3px 20px;clear:both;font-weight:normal;line-height:20px;color:#333;white-space:nowrap}.dropdown-menu li>a:hover,.dropdown-menu li>a:focus,.dropdown-submenu:hover>a{color:#fff;text-decoration:none;background-color:#0081c2;background-image:-moz-linear-gradient(top,#08c,#0077b3);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#0077b3));background-image:-webkit-linear-gradient(top,#08c,#0077b3);background-image:-o-linear-gradient(top,#08c,#0077b3);background-image:linear-gradient(to bottom,#08c,#0077b3);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc',endColorstr='#ff0077b3',GradientType=0)}.dropdown-menu .active>a,.dropdown-menu .active>a:hover{color:#333;text-decoration:none;background-color:#0081c2;background-image:-moz-linear-gradient(top,#08c,#0077b3);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#0077b3));background-image:-webkit-linear-gradient(top,#08c,#0077b3);background-image:-o-linear-gradient(top,#08c,#0077b3);background-image:linear-gradient(to bottom,#08c,#0077b3);background-repeat:repeat-x;outline:0;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc',endColorstr='#ff0077b3',GradientType=0)}.dropdown-menu .disabled>a,.dropdown-menu .disabled>a:hover{color:#999}.dropdown-menu .disabled>a:hover{text-decoration:none;cursor:default;background-color:transparent;background-image:none}.open{*z-index:1000}.open>.dropdown-menu{display:block}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{border-top:0;border-bottom:4px solid #000;content:""}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:1px}.dropdown-submenu{position:relative}.dropdown-submenu>.dropdown-menu{top:0;left:100%;margin-top:-6px;margin-left:-1px;-webkit-border-radius:0 6px 6px 6px;-moz-border-radius:0 6px 6px 6px;border-radius:0 6px 6px 6px}.dropdown-submenu:hover>.dropdown-menu{display:block}.dropup .dropdown-submenu>.dropdown-menu{top:auto;bottom:0;margin-top:0;margin-bottom:-2px;-webkit-border-radius:5px 5px 5px 0;-moz-border-radius:5px 5px 5px 0;border-radius:5px 5px 5px 0}.dropdown-submenu>a:after{display:block;float:right;width:0;height:0;margin-top:5px;margin-right:-10px;border-color:transparent;border-left-color:#ccc;border-style:solid;border-width:5px 0 5px 5px;content:" "}.dropdown-submenu:hover>a:after{border-left-color:#fff}.dropdown-submenu.pull-left{float:none}.dropdown-submenu.pull-left>.dropdown-menu{left:-100%;margin-left:10px;-webkit-border-radius:6px 0 6px 6px;-moz-border-radius:6px 0 6px 6px;border-radius:6px 0 6px 6px}.dropdown .dropdown-menu .nav-header{padding-right:20px;padding-left:20px}.typeahead{margin-top:2px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.05);box-shadow:inset 0 1px 1px rgba(0,0,0,0.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,0.15)}.well-large{padding:24px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.well-small{padding:9px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.fade{opacity:0;-webkit-transition:opacity .15s linear;-moz-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{position:relative;height:0;overflow:hidden;-webkit-transition:height .35s ease;-moz-transition:height .35s ease;-o-transition:height .35s ease;transition:height .35s ease}.collapse.in{height:auto}.close{float:right;font-size:20px;font-weight:bold;line-height:20px;color:#000;text-shadow:0 1px 0 #fff;opacity:.2;filter:alpha(opacity=20)}.close:hover{color:#000;text-decoration:none;cursor:pointer;opacity:.4;filter:alpha(opacity=40)}button.close{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none}.btn{display:inline-block;*display:inline;padding:4px 12px;margin-bottom:0;*margin-left:.3em;font-size:14px;line-height:20px;*line-height:20px;color:#333;text-align:center;text-shadow:0 1px 1px rgba(255,255,255,0.75);vertical-align:middle;cursor:pointer;background-color:#f5f5f5;*background-color:#e6e6e6;background-image:-moz-linear-gradient(top,#fff,#e6e6e6);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#e6e6e6));background-image:-webkit-linear-gradient(top,#fff,#e6e6e6);background-image:-o-linear-gradient(top,#fff,#e6e6e6);background-image:linear-gradient(to bottom,#fff,#e6e6e6);background-repeat:repeat-x;border:1px solid #bbb;*border:0;border-color:#e6e6e6 #e6e6e6 #bfbfbf;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);border-bottom-color:#a2a2a2;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff',endColorstr='#ffe6e6e6',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);*zoom:1;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05)}.btn:hover,.btn:active,.btn.active,.btn.disabled,.btn[disabled]{color:#333;background-color:#e6e6e6;*background-color:#d9d9d9}.btn:active,.btn.active{background-color:#ccc \9}.btn:first-child{*margin-left:0}.btn:hover{color:#333;text-decoration:none;background-color:#e6e6e6;*background-color:#d9d9d9;background-position:0 -15px;-webkit-transition:background-position .1s linear;-moz-transition:background-position .1s linear;-o-transition:background-position .1s linear;transition:background-position .1s linear}.btn:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn.active,.btn:active{background-color:#e6e6e6;background-color:#d9d9d9 \9;background-image:none;outline:0;-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05)}.btn.disabled,.btn[disabled]{cursor:default;background-color:#e6e6e6;background-image:none;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.btn-large{padding:11px 19px;font-size:17.5px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.btn-large [class^="icon-"],.btn-large [class*=" icon-"]{margin-top:2px}.btn-small{padding:2px 10px;font-size:11.9px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.btn-small [class^="icon-"],.btn-small [class*=" icon-"]{margin-top:0}.btn-mini{padding:1px 6px;font-size:10.5px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.btn-block{display:block;width:100%;padding-right:0;padding-left:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.btn-block+.btn-block{margin-top:5px}input[type="submit"].btn-block,input[type="reset"].btn-block,input[type="button"].btn-block{width:100%}.btn-primary.active,.btn-warning.active,.btn-danger.active,.btn-success.active,.btn-info.active,.btn-inverse.active{color:rgba(255,255,255,0.75)}.btn{border-color:#c5c5c5;border-color:rgba(0,0,0,0.15) rgba(0,0,0,0.15) rgba(0,0,0,0.25)}.btn-primary{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#006dcc;*background-color:#04c;background-image:-moz-linear-gradient(top,#08c,#04c);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#04c));background-image:-webkit-linear-gradient(top,#08c,#04c);background-image:-o-linear-gradient(top,#08c,#04c);background-image:linear-gradient(to bottom,#08c,#04c);background-repeat:repeat-x;border-color:#04c #04c #002a80;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc',endColorstr='#ff0044cc',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-primary:hover,.btn-primary:active,.btn-primary.active,.btn-primary.disabled,.btn-primary[disabled]{color:#fff;background-color:#04c;*background-color:#003bb3}.btn-primary:active,.btn-primary.active{background-color:#039 \9}.btn-warning{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#faa732;*background-color:#f89406;background-image:-moz-linear-gradient(top,#fbb450,#f89406);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fbb450),to(#f89406));background-image:-webkit-linear-gradient(top,#fbb450,#f89406);background-image:-o-linear-gradient(top,#fbb450,#f89406);background-image:linear-gradient(to bottom,#fbb450,#f89406);background-repeat:repeat-x;border-color:#f89406 #f89406 #ad6704;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450',endColorstr='#fff89406',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-warning:hover,.btn-warning:active,.btn-warning.active,.btn-warning.disabled,.btn-warning[disabled]{color:#fff;background-color:#f89406;*background-color:#df8505}.btn-warning:active,.btn-warning.active{background-color:#c67605 \9}.btn-danger{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#da4f49;*background-color:#bd362f;background-image:-moz-linear-gradient(top,#ee5f5b,#bd362f);background-image:-webkit-gradient(linear,0 0,0 100%,from(#ee5f5b),to(#bd362f));background-image:-webkit-linear-gradient(top,#ee5f5b,#bd362f);background-image:-o-linear-gradient(top,#ee5f5b,#bd362f);background-image:linear-gradient(to bottom,#ee5f5b,#bd362f);background-repeat:repeat-x;border-color:#bd362f #bd362f #802420;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b',endColorstr='#ffbd362f',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-danger:hover,.btn-danger:active,.btn-danger.active,.btn-danger.disabled,.btn-danger[disabled]{color:#fff;background-color:#bd362f;*background-color:#a9302a}.btn-danger:active,.btn-danger.active{background-color:#942a25 \9}.btn-success{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#5bb75b;*background-color:#51a351;background-image:-moz-linear-gradient(top,#62c462,#51a351);background-image:-webkit-gradient(linear,0 0,0 100%,from(#62c462),to(#51a351));background-image:-webkit-linear-gradient(top,#62c462,#51a351);background-image:-o-linear-gradient(top,#62c462,#51a351);background-image:linear-gradient(to bottom,#62c462,#51a351);background-repeat:repeat-x;border-color:#51a351 #51a351 #387038;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462',endColorstr='#ff51a351',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-success:hover,.btn-success:active,.btn-success.active,.btn-success.disabled,.btn-success[disabled]{color:#fff;background-color:#51a351;*background-color:#499249}.btn-success:active,.btn-success.active{background-color:#408140 \9}.btn-info{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#49afcd;*background-color:#2f96b4;background-image:-moz-linear-gradient(top,#5bc0de,#2f96b4);background-image:-webkit-gradient(linear,0 0,0 100%,from(#5bc0de),to(#2f96b4));background-image:-webkit-linear-gradient(top,#5bc0de,#2f96b4);background-image:-o-linear-gradient(top,#5bc0de,#2f96b4);background-image:linear-gradient(to bottom,#5bc0de,#2f96b4);background-repeat:repeat-x;border-color:#2f96b4 #2f96b4 #1f6377;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de',endColorstr='#ff2f96b4',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-info:hover,.btn-info:active,.btn-info.active,.btn-info.disabled,.btn-info[disabled]{color:#fff;background-color:#2f96b4;*background-color:#2a85a0}.btn-info:active,.btn-info.active{background-color:#24748c \9}.btn-inverse{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#363636;*background-color:#222;background-image:-moz-linear-gradient(top,#444,#222);background-image:-webkit-gradient(linear,0 0,0 100%,from(#444),to(#222));background-image:-webkit-linear-gradient(top,#444,#222);background-image:-o-linear-gradient(top,#444,#222);background-image:linear-gradient(to bottom,#444,#222);background-repeat:repeat-x;border-color:#222 #222 #000;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff444444',endColorstr='#ff222222',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-inverse:hover,.btn-inverse:active,.btn-inverse.active,.btn-inverse.disabled,.btn-inverse[disabled]{color:#fff;background-color:#222;*background-color:#151515}.btn-inverse:active,.btn-inverse.active{background-color:#080808 \9}button.btn,input[type="submit"].btn{*padding-top:3px;*padding-bottom:3px}button.btn::-moz-focus-inner,input[type="submit"].btn::-moz-focus-inner{padding:0;border:0}button.btn.btn-large,input[type="submit"].btn.btn-large{*padding-top:7px;*padding-bottom:7px}button.btn.btn-small,input[type="submit"].btn.btn-small{*padding-top:3px;*padding-bottom:3px}button.btn.btn-mini,input[type="submit"].btn.btn-mini{*padding-top:1px;*padding-bottom:1px}.btn-link,.btn-link:active,.btn-link[disabled]{background-color:transparent;background-image:none;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.btn-link{color:#08c;cursor:pointer;border-color:transparent;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.btn-link:hover{color:#005580;text-decoration:underline;background-color:transparent}.btn-link[disabled]:hover{color:#333;text-decoration:none}.btn-group{position:relative;display:inline-block;*display:inline;*margin-left:.3em;font-size:0;white-space:nowrap;vertical-align:middle;*zoom:1}.btn-group:first-child{*margin-left:0}.btn-group+.btn-group{margin-left:5px}.btn-toolbar{margin-top:10px;margin-bottom:10px;font-size:0}.btn-toolbar .btn+.btn,.btn-toolbar .btn-group+.btn,.btn-toolbar .btn+.btn-group{margin-left:5px}.btn-group>.btn{position:relative;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.btn-group>.btn+.btn{margin-left:-1px}.btn-group>.btn,.btn-group>.dropdown-menu{font-size:14px}.btn-group>.btn-mini{font-size:11px}.btn-group>.btn-small{font-size:12px}.btn-group>.btn-large{font-size:16px}.btn-group>.btn:first-child{margin-left:0;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-bottomleft:4px;-moz-border-radius-topleft:4px}.btn-group>.btn:last-child,.btn-group>.dropdown-toggle{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-topright:4px;-moz-border-radius-bottomright:4px}.btn-group>.btn.large:first-child{margin-left:0;-webkit-border-bottom-left-radius:6px;border-bottom-left-radius:6px;-webkit-border-top-left-radius:6px;border-top-left-radius:6px;-moz-border-radius-bottomleft:6px;-moz-border-radius-topleft:6px}.btn-group>.btn.large:last-child,.btn-group>.large.dropdown-toggle{-webkit-border-top-right-radius:6px;border-top-right-radius:6px;-webkit-border-bottom-right-radius:6px;border-bottom-right-radius:6px;-moz-border-radius-topright:6px;-moz-border-radius-bottomright:6px}.btn-group>.btn:hover,.btn-group>.btn:focus,.btn-group>.btn:active,.btn-group>.btn.active{z-index:2}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{*padding-top:5px;padding-right:8px;*padding-bottom:5px;padding-left:8px;-webkit-box-shadow:inset 1px 0 0 rgba(255,255,255,0.125),inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 1px 0 0 rgba(255,255,255,0.125),inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 1px 0 0 rgba(255,255,255,0.125),inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05)}.btn-group>.btn-mini+.dropdown-toggle{*padding-top:2px;padding-right:5px;*padding-bottom:2px;padding-left:5px}.btn-group>.btn-small+.dropdown-toggle{*padding-top:5px;*padding-bottom:4px}.btn-group>.btn-large+.dropdown-toggle{*padding-top:7px;padding-right:12px;*padding-bottom:7px;padding-left:12px}.btn-group.open .dropdown-toggle{background-image:none;-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05)}.btn-group.open .btn.dropdown-toggle{background-color:#e6e6e6}.btn-group.open .btn-primary.dropdown-toggle{background-color:#04c}.btn-group.open .btn-warning.dropdown-toggle{background-color:#f89406}.btn-group.open .btn-danger.dropdown-toggle{background-color:#bd362f}.btn-group.open .btn-success.dropdown-toggle{background-color:#51a351}.btn-group.open .btn-info.dropdown-toggle{background-color:#2f96b4}.btn-group.open .btn-inverse.dropdown-toggle{background-color:#222}.btn .caret{margin-top:8px;margin-left:0}.btn-mini .caret,.btn-small .caret,.btn-large .caret{margin-top:6px}.btn-large .caret{border-top-width:5px;border-right-width:5px;border-left-width:5px}.dropup .btn-large .caret{border-bottom-width:5px}.btn-primary .caret,.btn-warning .caret,.btn-danger .caret,.btn-info .caret,.btn-success .caret,.btn-inverse .caret{border-top-color:#fff;border-bottom-color:#fff}.btn-group-vertical{display:inline-block;*display:inline;*zoom:1}.btn-group-vertical .btn{display:block;float:none;width:100%;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.btn-group-vertical .btn+.btn{margin-top:-1px;margin-left:0}.btn-group-vertical .btn:first-child{-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0}.btn-group-vertical .btn:last-child{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px}.btn-group-vertical .btn-large:first-child{-webkit-border-radius:6px 6px 0 0;-moz-border-radius:6px 6px 0 0;border-radius:6px 6px 0 0}.btn-group-vertical .btn-large:last-child{-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px}.alert{padding:8px 35px 8px 14px;margin-bottom:20px;color:#c09853;text-shadow:0 1px 0 rgba(255,255,255,0.5);background-color:#fcf8e3;border:1px solid #fbeed5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.alert h4{margin:0}.alert .close{position:relative;top:-2px;right:-21px;line-height:20px}.alert-success{color:#468847;background-color:#dff0d8;border-color:#d6e9c6}.alert-danger,.alert-error{color:#b94a48;background-color:#f2dede;border-color:#eed3d7}.alert-info{color:#3a87ad;background-color:#d9edf7;border-color:#bce8f1}.alert-block{padding-top:14px;padding-bottom:14px}.alert-block>p,.alert-block>ul{margin-bottom:0}.alert-block p+p{margin-top:5px}.nav{margin-bottom:20px;margin-left:0;list-style:none}.nav>li>a{display:block}.nav>li>a:hover{text-decoration:none;background-color:#eee}.nav>.pull-right{float:right}.nav-header{display:block;padding:3px 15px;font-size:11px;font-weight:bold;line-height:20px;color:#999;text-shadow:0 1px 0 rgba(255,255,255,0.5);text-transform:uppercase}.nav li+.nav-header{margin-top:9px}.nav-list{padding-right:15px;padding-left:15px;margin-bottom:0}.nav-list>li>a,.nav-list .nav-header{margin-right:-15px;margin-left:-15px;text-shadow:0 1px 0 rgba(255,255,255,0.5)}.nav-list>li>a{padding:3px 15px}.nav-list>.active>a,.nav-list>.active>a:hover{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.2);background-color:#08c}.nav-list [class^="icon-"],.nav-list [class*=" icon-"]{margin-right:2px}.nav-list .divider{*width:100%;height:1px;margin:9px 1px;*margin:-5px 0 5px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #fff}.nav-tabs,.nav-pills{*zoom:1}.nav-tabs:before,.nav-pills:before,.nav-tabs:after,.nav-pills:after{display:table;line-height:0;content:""}.nav-tabs:after,.nav-pills:after{clear:both}.nav-tabs>li,.nav-pills>li{float:left}.nav-tabs>li>a,.nav-pills>li>a{padding-right:12px;padding-left:12px;margin-right:2px;line-height:14px}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{margin-bottom:-1px}.nav-tabs>li>a{padding-top:8px;padding-bottom:8px;line-height:20px;border:1px solid transparent;-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>.active>a,.nav-tabs>.active>a:hover{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-pills>li>a{padding-top:8px;padding-bottom:8px;margin-top:2px;margin-bottom:2px;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.nav-pills>.active>a,.nav-pills>.active>a:hover{color:#fff;background-color:#08c}.nav-stacked>li{float:none}.nav-stacked>li>a{margin-right:0}.nav-tabs.nav-stacked{border-bottom:0}.nav-tabs.nav-stacked>li>a{border:1px solid #ddd;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.nav-tabs.nav-stacked>li:first-child>a{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topright:4px;-moz-border-radius-topleft:4px}.nav-tabs.nav-stacked>li:last-child>a{-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-moz-border-radius-bottomright:4px;-moz-border-radius-bottomleft:4px}.nav-tabs.nav-stacked>li>a:hover{z-index:2;border-color:#ddd}.nav-pills.nav-stacked>li>a{margin-bottom:3px}.nav-pills.nav-stacked>li:last-child>a{margin-bottom:1px}.nav-tabs .dropdown-menu{-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px}.nav-pills .dropdown-menu{-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.nav .dropdown-toggle .caret{margin-top:6px;border-top-color:#08c;border-bottom-color:#08c}.nav .dropdown-toggle:hover .caret{border-top-color:#005580;border-bottom-color:#005580}.nav-tabs .dropdown-toggle .caret{margin-top:8px}.nav .active .dropdown-toggle .caret{border-top-color:#fff;border-bottom-color:#fff}.nav-tabs .active .dropdown-toggle .caret{border-top-color:#555;border-bottom-color:#555}.nav>.dropdown.active>a:hover{cursor:pointer}.nav-tabs .open .dropdown-toggle,.nav-pills .open .dropdown-toggle,.nav>li.dropdown.open.active>a:hover{color:#fff;background-color:#999;border-color:#999}.nav li.dropdown.open .caret,.nav li.dropdown.open.active .caret,.nav li.dropdown.open a:hover .caret{border-top-color:#fff;border-bottom-color:#fff;opacity:1;filter:alpha(opacity=100)}.tabs-stacked .open>a:hover{border-color:#999}.tabbable{*zoom:1}.tabbable:before,.tabbable:after{display:table;line-height:0;content:""}.tabbable:after{clear:both}.tab-content{overflow:auto}.tabs-below>.nav-tabs,.tabs-right>.nav-tabs,.tabs-left>.nav-tabs{border-bottom:0}.tab-content>.tab-pane,.pill-content>.pill-pane{display:none}.tab-content>.active,.pill-content>.active{display:block}.tabs-below>.nav-tabs{border-top:1px solid #ddd}.tabs-below>.nav-tabs>li{margin-top:-1px;margin-bottom:0}.tabs-below>.nav-tabs>li>a{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px}.tabs-below>.nav-tabs>li>a:hover{border-top-color:#ddd;border-bottom-color:transparent}.tabs-below>.nav-tabs>.active>a,.tabs-below>.nav-tabs>.active>a:hover{border-color:transparent #ddd #ddd #ddd}.tabs-left>.nav-tabs>li,.tabs-right>.nav-tabs>li{float:none}.tabs-left>.nav-tabs>li>a,.tabs-right>.nav-tabs>li>a{min-width:74px;margin-right:0;margin-bottom:3px}.tabs-left>.nav-tabs{float:left;margin-right:19px;border-right:1px solid #ddd}.tabs-left>.nav-tabs>li>a{margin-right:-1px;-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.tabs-left>.nav-tabs>li>a:hover{border-color:#eee #ddd #eee #eee}.tabs-left>.nav-tabs .active>a,.tabs-left>.nav-tabs .active>a:hover{border-color:#ddd transparent #ddd #ddd;*border-right-color:#fff}.tabs-right>.nav-tabs{float:right;margin-left:19px;border-left:1px solid #ddd}.tabs-right>.nav-tabs>li>a{margin-left:-1px;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.tabs-right>.nav-tabs>li>a:hover{border-color:#eee #eee #eee #ddd}.tabs-right>.nav-tabs .active>a,.tabs-right>.nav-tabs .active>a:hover{border-color:#ddd #ddd #ddd transparent;*border-left-color:#fff}.nav>.disabled>a{color:#999}.nav>.disabled>a:hover{text-decoration:none;cursor:default;background-color:transparent}.navbar{*position:relative;*z-index:2;margin-bottom:20px;overflow:visible;color:#777}.navbar-inner{min-height:40px;padding-right:20px;padding-left:20px;background-color:#fafafa;background-image:-moz-linear-gradient(top,#fff,#f2f2f2);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#f2f2f2));background-image:-webkit-linear-gradient(top,#fff,#f2f2f2);background-image:-o-linear-gradient(top,#fff,#f2f2f2);background-image:linear-gradient(to bottom,#fff,#f2f2f2);background-repeat:repeat-x;border:1px solid #d4d4d4;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff',endColorstr='#fff2f2f2',GradientType=0);*zoom:1;-webkit-box-shadow:0 1px 4px rgba(0,0,0,0.065);-moz-box-shadow:0 1px 4px rgba(0,0,0,0.065);box-shadow:0 1px 4px rgba(0,0,0,0.065)}.navbar-inner:before,.navbar-inner:after{display:table;line-height:0;content:""}.navbar-inner:after{clear:both}.navbar .container{width:auto}.nav-collapse.collapse{height:auto;overflow:visible}.navbar .brand{display:block;float:left;padding:10px 20px 10px;margin-left:-20px;font-size:20px;font-weight:200;color:#777;text-shadow:0 1px 0 #fff}.navbar .brand:hover{text-decoration:none}.navbar-text{margin-bottom:0;line-height:40px}.navbar-link{color:#777}.navbar-link:hover{color:#333}.navbar .divider-vertical{height:40px;margin:0 9px;border-right:1px solid #fff;border-left:1px solid #f2f2f2}.navbar .btn,.navbar .btn-group{margin-top:5px}.navbar .btn-group .btn,.navbar .input-prepend .btn,.navbar .input-append .btn{margin-top:0}.navbar-form{margin-bottom:0;*zoom:1}.navbar-form:before,.navbar-form:after{display:table;line-height:0;content:""}.navbar-form:after{clear:both}.navbar-form input,.navbar-form select,.navbar-form .radio,.navbar-form .checkbox{margin-top:5px}.navbar-form input,.navbar-form select,.navbar-form .btn{display:inline-block;margin-bottom:0}.navbar-form input[type="image"],.navbar-form input[type="checkbox"],.navbar-form input[type="radio"]{margin-top:3px}.navbar-form .input-append,.navbar-form .input-prepend{margin-top:6px;white-space:nowrap}.navbar-form .input-append input,.navbar-form .input-prepend input{margin-top:0}.navbar-search{position:relative;float:left;margin-top:5px;margin-bottom:0}.navbar-search .search-query{padding:4px 14px;margin-bottom:0;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;font-weight:normal;line-height:1;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px}.navbar-static-top{position:static;margin-bottom:0}.navbar-static-top .navbar-inner{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030;margin-bottom:0}.navbar-fixed-top .navbar-inner,.navbar-static-top .navbar-inner{border-width:0 0 1px}.navbar-fixed-bottom .navbar-inner{border-width:1px 0 0}.navbar-fixed-top .navbar-inner,.navbar-fixed-bottom .navbar-inner{padding-right:0;padding-left:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:940px}.navbar-fixed-top{top:0}.navbar-fixed-top .navbar-inner,.navbar-static-top .navbar-inner{-webkit-box-shadow:0 1px 10px rgba(0,0,0,0.1);-moz-box-shadow:0 1px 10px rgba(0,0,0,0.1);box-shadow:0 1px 10px rgba(0,0,0,0.1)}.navbar-fixed-bottom{bottom:0}.navbar-fixed-bottom .navbar-inner{-webkit-box-shadow:0 -1px 10px rgba(0,0,0,0.1);-moz-box-shadow:0 -1px 10px rgba(0,0,0,0.1);box-shadow:0 -1px 10px rgba(0,0,0,0.1)}.navbar .nav{position:relative;left:0;display:block;float:left;margin:0 10px 0 0}.navbar .nav.pull-right{float:right;margin-right:0}.navbar .nav>li{float:left}.navbar .nav>li>a{float:none;padding:10px 15px 10px;color:#777;text-decoration:none;text-shadow:0 1px 0 #fff}.navbar .nav .dropdown-toggle .caret{margin-top:8px}.navbar .nav>li>a:focus,.navbar .nav>li>a:hover{color:#333;text-decoration:none;background-color:transparent}.navbar .nav>.active>a,.navbar .nav>.active>a:hover,.navbar .nav>.active>a:focus{color:#555;text-decoration:none;background-color:#e5e5e5;-webkit-box-shadow:inset 0 3px 8px rgba(0,0,0,0.125);-moz-box-shadow:inset 0 3px 8px rgba(0,0,0,0.125);box-shadow:inset 0 3px 8px rgba(0,0,0,0.125)}.navbar .btn-navbar{display:none;float:right;padding:7px 10px;margin-right:5px;margin-left:5px;color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#ededed;*background-color:#e5e5e5;background-image:-moz-linear-gradient(top,#f2f2f2,#e5e5e5);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f2f2f2),to(#e5e5e5));background-image:-webkit-linear-gradient(top,#f2f2f2,#e5e5e5);background-image:-o-linear-gradient(top,#f2f2f2,#e5e5e5);background-image:linear-gradient(to bottom,#f2f2f2,#e5e5e5);background-repeat:repeat-x;border-color:#e5e5e5 #e5e5e5 #bfbfbf;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2f2f2',endColorstr='#ffe5e5e5',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.075);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.075);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.075)}.navbar .btn-navbar:hover,.navbar .btn-navbar:active,.navbar .btn-navbar.active,.navbar .btn-navbar.disabled,.navbar .btn-navbar[disabled]{color:#fff;background-color:#e5e5e5;*background-color:#d9d9d9}.navbar .btn-navbar:active,.navbar .btn-navbar.active{background-color:#ccc \9}.navbar .btn-navbar .icon-bar{display:block;width:18px;height:2px;background-color:#f5f5f5;-webkit-border-radius:1px;-moz-border-radius:1px;border-radius:1px;-webkit-box-shadow:0 1px 0 rgba(0,0,0,0.25);-moz-box-shadow:0 1px 0 rgba(0,0,0,0.25);box-shadow:0 1px 0 rgba(0,0,0,0.25)}.btn-navbar .icon-bar+.icon-bar{margin-top:3px}.navbar .nav>li>.dropdown-menu:before{position:absolute;top:-7px;left:9px;display:inline-block;border-right:7px solid transparent;border-bottom:7px solid #ccc;border-left:7px solid transparent;border-bottom-color:rgba(0,0,0,0.2);content:''}.navbar .nav>li>.dropdown-menu:after{position:absolute;top:-6px;left:10px;display:inline-block;border-right:6px solid transparent;border-bottom:6px solid #fff;border-left:6px solid transparent;content:''}.navbar-fixed-bottom .nav>li>.dropdown-menu:before{top:auto;bottom:-7px;border-top:7px solid #ccc;border-bottom:0;border-top-color:rgba(0,0,0,0.2)}.navbar-fixed-bottom .nav>li>.dropdown-menu:after{top:auto;bottom:-6px;border-top:6px solid #fff;border-bottom:0}.navbar .nav li.dropdown.open>.dropdown-toggle,.navbar .nav li.dropdown.active>.dropdown-toggle,.navbar .nav li.dropdown.open.active>.dropdown-toggle{color:#555;background-color:#e5e5e5}.navbar .nav li.dropdown>.dropdown-toggle .caret{border-top-color:#777;border-bottom-color:#777}.navbar .nav li.dropdown.open>.dropdown-toggle .caret,.navbar .nav li.dropdown.active>.dropdown-toggle .caret,.navbar .nav li.dropdown.open.active>.dropdown-toggle .caret{border-top-color:#555;border-bottom-color:#555}.navbar .pull-right>li>.dropdown-menu,.navbar .nav>li>.dropdown-menu.pull-right{right:0;left:auto}.navbar .pull-right>li>.dropdown-menu:before,.navbar .nav>li>.dropdown-menu.pull-right:before{right:12px;left:auto}.navbar .pull-right>li>.dropdown-menu:after,.navbar .nav>li>.dropdown-menu.pull-right:after{right:13px;left:auto}.navbar .pull-right>li>.dropdown-menu .dropdown-menu,.navbar .nav>li>.dropdown-menu.pull-right .dropdown-menu{right:100%;left:auto;margin-right:-1px;margin-left:0;-webkit-border-radius:6px 0 6px 6px;-moz-border-radius:6px 0 6px 6px;border-radius:6px 0 6px 6px}.navbar-inverse{color:#999}.navbar-inverse .navbar-inner{background-color:#1b1b1b;background-image:-moz-linear-gradient(top,#222,#111);background-image:-webkit-gradient(linear,0 0,0 100%,from(#222),to(#111));background-image:-webkit-linear-gradient(top,#222,#111);background-image:-o-linear-gradient(top,#222,#111);background-image:linear-gradient(to bottom,#222,#111);background-repeat:repeat-x;border-color:#252525;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222',endColorstr='#ff111111',GradientType=0)}.navbar-inverse .brand,.navbar-inverse .nav>li>a{color:#999;text-shadow:0 -1px 0 rgba(0,0,0,0.25)}.navbar-inverse .brand:hover,.navbar-inverse .nav>li>a:hover{color:#fff}.navbar-inverse .nav>li>a:focus,.navbar-inverse .nav>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .nav .active>a,.navbar-inverse .nav .active>a:hover,.navbar-inverse .nav .active>a:focus{color:#fff;background-color:#111}.navbar-inverse .navbar-link{color:#999}.navbar-inverse .navbar-link:hover{color:#fff}.navbar-inverse .divider-vertical{border-right-color:#222;border-left-color:#111}.navbar-inverse .nav li.dropdown.open>.dropdown-toggle,.navbar-inverse .nav li.dropdown.active>.dropdown-toggle,.navbar-inverse .nav li.dropdown.open.active>.dropdown-toggle{color:#fff;background-color:#111}.navbar-inverse .nav li.dropdown>.dropdown-toggle .caret{border-top-color:#999;border-bottom-color:#999}.navbar-inverse .nav li.dropdown.open>.dropdown-toggle .caret,.navbar-inverse .nav li.dropdown.active>.dropdown-toggle .caret,.navbar-inverse .nav li.dropdown.open.active>.dropdown-toggle .caret{border-top-color:#fff;border-bottom-color:#fff}.navbar-inverse .navbar-search .search-query{color:#fff;background-color:#515151;border-color:#111;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1),0 1px 0 rgba(255,255,255,0.15);-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1),0 1px 0 rgba(255,255,255,0.15);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1),0 1px 0 rgba(255,255,255,0.15);-webkit-transition:none;-moz-transition:none;-o-transition:none;transition:none}.navbar-inverse .navbar-search .search-query:-moz-placeholder{color:#ccc}.navbar-inverse .navbar-search .search-query:-ms-input-placeholder{color:#ccc}.navbar-inverse .navbar-search .search-query::-webkit-input-placeholder{color:#ccc}.navbar-inverse .navbar-search .search-query:focus,.navbar-inverse .navbar-search .search-query.focused{padding:5px 15px;color:#333;text-shadow:0 1px 0 #fff;background-color:#fff;border:0;outline:0;-webkit-box-shadow:0 0 3px rgba(0,0,0,0.15);-moz-box-shadow:0 0 3px rgba(0,0,0,0.15);box-shadow:0 0 3px rgba(0,0,0,0.15)}.navbar-inverse .btn-navbar{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#0e0e0e;*background-color:#040404;background-image:-moz-linear-gradient(top,#151515,#040404);background-image:-webkit-gradient(linear,0 0,0 100%,from(#151515),to(#040404));background-image:-webkit-linear-gradient(top,#151515,#040404);background-image:-o-linear-gradient(top,#151515,#040404);background-image:linear-gradient(to bottom,#151515,#040404);background-repeat:repeat-x;border-color:#040404 #040404 #000;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff151515',endColorstr='#ff040404',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.navbar-inverse .btn-navbar:hover,.navbar-inverse .btn-navbar:active,.navbar-inverse .btn-navbar.active,.navbar-inverse .btn-navbar.disabled,.navbar-inverse .btn-navbar[disabled]{color:#fff;background-color:#040404;*background-color:#000}.navbar-inverse .btn-navbar:active,.navbar-inverse .btn-navbar.active{background-color:#000 \9}.breadcrumb{padding:8px 15px;margin:0 0 20px;list-style:none;background-color:#f5f5f5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.breadcrumb li{display:inline-block;*display:inline;text-shadow:0 1px 0 #fff;*zoom:1}.breadcrumb .divider{padding:0 5px;color:#ccc}.breadcrumb .active{color:#999}.pagination{margin:20px 0}.pagination ul{display:inline-block;*display:inline;margin-bottom:0;margin-left:0;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;*zoom:1;-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:0 1px 2px rgba(0,0,0,0.05);box-shadow:0 1px 2px rgba(0,0,0,0.05)}.pagination ul>li{display:inline}.pagination ul>li>a,.pagination ul>li>span{float:left;padding:4px 12px;line-height:20px;text-decoration:none;background-color:#fff;border:1px solid #ddd;border-left-width:0}.pagination ul>li>a:hover,.pagination ul>.active>a,.pagination ul>.active>span{background-color:#f5f5f5}.pagination ul>.active>a,.pagination ul>.active>span{color:#999;cursor:default}.pagination ul>.disabled>span,.pagination ul>.disabled>a,.pagination ul>.disabled>a:hover{color:#999;cursor:default;background-color:transparent}.pagination ul>li:first-child>a,.pagination ul>li:first-child>span{border-left-width:1px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-bottomleft:4px;-moz-border-radius-topleft:4px}.pagination ul>li:last-child>a,.pagination ul>li:last-child>span{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-topright:4px;-moz-border-radius-bottomright:4px}.pagination-centered{text-align:center}.pagination-right{text-align:right}.pagination-large ul>li>a,.pagination-large ul>li>span{padding:11px 19px;font-size:17.5px}.pagination-large ul>li:first-child>a,.pagination-large ul>li:first-child>span{-webkit-border-bottom-left-radius:6px;border-bottom-left-radius:6px;-webkit-border-top-left-radius:6px;border-top-left-radius:6px;-moz-border-radius-bottomleft:6px;-moz-border-radius-topleft:6px}.pagination-large ul>li:last-child>a,.pagination-large ul>li:last-child>span{-webkit-border-top-right-radius:6px;border-top-right-radius:6px;-webkit-border-bottom-right-radius:6px;border-bottom-right-radius:6px;-moz-border-radius-topright:6px;-moz-border-radius-bottomright:6px}.pagination-mini ul>li:first-child>a,.pagination-small ul>li:first-child>a,.pagination-mini ul>li:first-child>span,.pagination-small ul>li:first-child>span{-webkit-border-bottom-left-radius:3px;border-bottom-left-radius:3px;-webkit-border-top-left-radius:3px;border-top-left-radius:3px;-moz-border-radius-bottomleft:3px;-moz-border-radius-topleft:3px}.pagination-mini ul>li:last-child>a,.pagination-small ul>li:last-child>a,.pagination-mini ul>li:last-child>span,.pagination-small ul>li:last-child>span{-webkit-border-top-right-radius:3px;border-top-right-radius:3px;-webkit-border-bottom-right-radius:3px;border-bottom-right-radius:3px;-moz-border-radius-topright:3px;-moz-border-radius-bottomright:3px}.pagination-small ul>li>a,.pagination-small ul>li>span{padding:2px 10px;font-size:11.9px}.pagination-mini ul>li>a,.pagination-mini ul>li>span{padding:1px 6px;font-size:10.5px}.pager{margin:20px 0;text-align:center;list-style:none;*zoom:1}.pager:before,.pager:after{display:table;line-height:0;content:""}.pager:after{clear:both}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px}.pager li>a:hover{text-decoration:none;background-color:#f5f5f5}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:hover,.pager .disabled>span{color:#999;cursor:default;background-color:#fff}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop,.modal-backdrop.fade.in{opacity:.8;filter:alpha(opacity=80)}.modal{position:fixed;top:50%;left:50%;z-index:1050;width:560px;margin:-250px 0 0 -280px;background-color:#fff;border:1px solid #999;border:1px solid rgba(0,0,0,0.3);*border:1px solid #999;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;outline:0;-webkit-box-shadow:0 3px 7px rgba(0,0,0,0.3);-moz-box-shadow:0 3px 7px rgba(0,0,0,0.3);box-shadow:0 3px 7px rgba(0,0,0,0.3);-webkit-background-clip:padding-box;-moz-background-clip:padding-box;background-clip:padding-box}.modal.fade{top:-25%;-webkit-transition:opacity .3s linear,top .3s ease-out;-moz-transition:opacity .3s linear,top .3s ease-out;-o-transition:opacity .3s linear,top .3s ease-out;transition:opacity .3s linear,top .3s ease-out}.modal.fade.in{top:50%}.modal-header{padding:9px 15px;border-bottom:1px solid #eee}.modal-header .close{margin-top:2px}.modal-header h3{margin:0;line-height:30px}.modal-body{max-height:400px;padding:15px;overflow-y:auto}.modal-form{margin-bottom:0}.modal-footer{padding:14px 15px 15px;margin-bottom:0;text-align:right;background-color:#f5f5f5;border-top:1px solid #ddd;-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px;*zoom:1;-webkit-box-shadow:inset 0 1px 0 #fff;-moz-box-shadow:inset 0 1px 0 #fff;box-shadow:inset 0 1px 0 #fff}.modal-footer:before,.modal-footer:after{display:table;line-height:0;content:""}.modal-footer:after{clear:both}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.tooltip{position:absolute;z-index:1030;display:block;padding:5px;font-size:11px;opacity:0;filter:alpha(opacity=0);visibility:visible}.tooltip.in{opacity:.8;filter:alpha(opacity=80)}.tooltip.top{margin-top:-3px}.tooltip.right{margin-left:3px}.tooltip.bottom{margin-top:3px}.tooltip.left{margin-left:-3px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;text-decoration:none;background-color:#000;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-top-color:#000;border-width:5px 5px 0}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-right-color:#000;border-width:5px 5px 5px 0}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-left-color:#000;border-width:5px 0 5px 5px}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-bottom-color:#000;border-width:0 5px 5px}.popover{position:absolute;top:0;left:0;z-index:1010;display:none;width:236px;padding:1px;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);-moz-box-shadow:0 5px 10px rgba(0,0,0,0.2);box-shadow:0 5px 10px rgba(0,0,0,0.2);-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{padding:8px 14px;margin:0;font-size:14px;font-weight:normal;line-height:18px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;-webkit-border-radius:5px 5px 0 0;-moz-border-radius:5px 5px 0 0;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover-content p,.popover-content ul,.popover-content ol{margin-bottom:0}.popover .arrow,.popover .arrow:after{position:absolute;display:inline-block;width:0;height:0;border-color:transparent;border-style:solid}.popover .arrow:after{z-index:-1;content:""}.popover.top .arrow{bottom:-10px;left:50%;margin-left:-10px;border-top-color:#fff;border-width:10px 10px 0}.popover.top .arrow:after{bottom:-1px;left:-11px;border-top-color:rgba(0,0,0,0.25);border-width:11px 11px 0}.popover.right .arrow{top:50%;left:-10px;margin-top:-10px;border-right-color:#fff;border-width:10px 10px 10px 0}.popover.right .arrow:after{bottom:-11px;left:-1px;border-right-color:rgba(0,0,0,0.25);border-width:11px 11px 11px 0}.popover.bottom .arrow{top:-10px;left:50%;margin-left:-10px;border-bottom-color:#fff;border-width:0 10px 10px}.popover.bottom .arrow:after{top:-1px;left:-11px;border-bottom-color:rgba(0,0,0,0.25);border-width:0 11px 11px}.popover.left .arrow{top:50%;right:-10px;margin-top:-10px;border-left-color:#fff;border-width:10px 0 10px 10px}.popover.left .arrow:after{right:-1px;bottom:-11px;border-left-color:rgba(0,0,0,0.25);border-width:11px 0 11px 11px}.thumbnails{margin-left:-20px;list-style:none;*zoom:1}.thumbnails:before,.thumbnails:after{display:table;line-height:0;content:""}.thumbnails:after{clear:both}.row-fluid .thumbnails{margin-left:0}.thumbnails>li{float:left;margin-bottom:20px;margin-left:20px}.thumbnail{display:block;padding:4px;line-height:20px;border:1px solid #ddd;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:0 1px 3px rgba(0,0,0,0.055);-moz-box-shadow:0 1px 3px rgba(0,0,0,0.055);box-shadow:0 1px 3px rgba(0,0,0,0.055);-webkit-transition:all .2s ease-in-out;-moz-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out}a.thumbnail:hover{border-color:#08c;-webkit-box-shadow:0 1px 4px rgba(0,105,214,0.25);-moz-box-shadow:0 1px 4px rgba(0,105,214,0.25);box-shadow:0 1px 4px rgba(0,105,214,0.25)}.thumbnail>img{display:block;max-width:100%;margin-right:auto;margin-left:auto}.thumbnail .caption{padding:9px;color:#555}.media,.media-body{overflow:hidden;*overflow:visible;zoom:1}.media,.media .media{margin-top:15px}.media:first-child{margin-top:0}.media-object{display:block}.media-heading{margin:0 0 5px}.media .pull-left{margin-right:10px}.media .pull-right{margin-left:10px}.media-list{margin-left:0;list-style:none}.label,.badge{display:inline-block;padding:2px 4px;font-size:11.844px;font-weight:bold;line-height:14px;color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);white-space:nowrap;vertical-align:baseline;background-color:#999}.label{-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.badge{padding-right:9px;padding-left:9px;-webkit-border-radius:9px;-moz-border-radius:9px;border-radius:9px}a.label:hover,a.badge:hover{color:#fff;text-decoration:none;cursor:pointer}.label-important,.badge-important{background-color:#b94a48}.label-important[href],.badge-important[href]{background-color:#953b39}.label-warning,.badge-warning{background-color:#f89406}.label-warning[href],.badge-warning[href]{background-color:#c67605}.label-success,.badge-success{background-color:#468847}.label-success[href],.badge-success[href]{background-color:#356635}.label-info,.badge-info{background-color:#3a87ad}.label-info[href],.badge-info[href]{background-color:#2d6987}.label-inverse,.badge-inverse{background-color:#333}.label-inverse[href],.badge-inverse[href]{background-color:#1a1a1a}.btn .label,.btn .badge{position:relative;top:-1px}.btn-mini .label,.btn-mini .badge{top:0}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-moz-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-ms-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:0 0}to{background-position:40px 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f7f7f7;background-image:-moz-linear-gradient(top,#f5f5f5,#f9f9f9);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f5f5f5),to(#f9f9f9));background-image:-webkit-linear-gradient(top,#f5f5f5,#f9f9f9);background-image:-o-linear-gradient(top,#f5f5f5,#f9f9f9);background-image:linear-gradient(to bottom,#f5f5f5,#f9f9f9);background-repeat:repeat-x;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5',endColorstr='#fff9f9f9',GradientType=0);-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1)}.progress .bar{float:left;width:0;height:100%;font-size:12px;color:#fff;text-align:center;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#0e90d2;background-image:-moz-linear-gradient(top,#149bdf,#0480be);background-image:-webkit-gradient(linear,0 0,0 100%,from(#149bdf),to(#0480be));background-image:-webkit-linear-gradient(top,#149bdf,#0480be);background-image:-o-linear-gradient(top,#149bdf,#0480be);background-image:linear-gradient(to bottom,#149bdf,#0480be);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff149bdf',endColorstr='#ff0480be',GradientType=0);-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-moz-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;-webkit-transition:width .6s ease;-moz-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress .bar+.bar{-webkit-box-shadow:inset 1px 0 0 rgba(0,0,0,0.15),inset 0 -1px 0 rgba(0,0,0,0.15);-moz-box-shadow:inset 1px 0 0 rgba(0,0,0,0.15),inset 0 -1px 0 rgba(0,0,0,0.15);box-shadow:inset 1px 0 0 rgba(0,0,0,0.15),inset 0 -1px 0 rgba(0,0,0,0.15)}.progress-striped .bar{background-color:#149bdf;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);-webkit-background-size:40px 40px;-moz-background-size:40px 40px;-o-background-size:40px 40px;background-size:40px 40px}.progress.active .bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-moz-animation:progress-bar-stripes 2s linear infinite;-ms-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-danger .bar,.progress .bar-danger{background-color:#dd514c;background-image:-moz-linear-gradient(top,#ee5f5b,#c43c35);background-image:-webkit-gradient(linear,0 0,0 100%,from(#ee5f5b),to(#c43c35));background-image:-webkit-linear-gradient(top,#ee5f5b,#c43c35);background-image:-o-linear-gradient(top,#ee5f5b,#c43c35);background-image:linear-gradient(to bottom,#ee5f5b,#c43c35);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b',endColorstr='#ffc43c35',GradientType=0)}.progress-danger.progress-striped .bar,.progress-striped .bar-danger{background-color:#ee5f5b;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-success .bar,.progress .bar-success{background-color:#5eb95e;background-image:-moz-linear-gradient(top,#62c462,#57a957);background-image:-webkit-gradient(linear,0 0,0 100%,from(#62c462),to(#57a957));background-image:-webkit-linear-gradient(top,#62c462,#57a957);background-image:-o-linear-gradient(top,#62c462,#57a957);background-image:linear-gradient(to bottom,#62c462,#57a957);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462',endColorstr='#ff57a957',GradientType=0)}.progress-success.progress-striped .bar,.progress-striped .bar-success{background-color:#62c462;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-info .bar,.progress .bar-info{background-color:#4bb1cf;background-image:-moz-linear-gradient(top,#5bc0de,#339bb9);background-image:-webkit-gradient(linear,0 0,0 100%,from(#5bc0de),to(#339bb9));background-image:-webkit-linear-gradient(top,#5bc0de,#339bb9);background-image:-o-linear-gradient(top,#5bc0de,#339bb9);background-image:linear-gradient(to bottom,#5bc0de,#339bb9);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de',endColorstr='#ff339bb9',GradientType=0)}.progress-info.progress-striped .bar,.progress-striped .bar-info{background-color:#5bc0de;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-warning .bar,.progress .bar-warning{background-color:#faa732;background-image:-moz-linear-gradient(top,#fbb450,#f89406);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fbb450),to(#f89406));background-image:-webkit-linear-gradient(top,#fbb450,#f89406);background-image:-o-linear-gradient(top,#fbb450,#f89406);background-image:linear-gradient(to bottom,#fbb450,#f89406);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450',endColorstr='#fff89406',GradientType=0)}.progress-warning.progress-striped .bar,.progress-striped .bar-warning{background-color:#fbb450;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.accordion{margin-bottom:20px}.accordion-group{margin-bottom:2px;border:1px solid #e5e5e5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.accordion-heading{border-bottom:0}.accordion-heading .accordion-toggle{display:block;padding:8px 15px}.accordion-toggle{cursor:pointer}.accordion-inner{padding:9px 15px;border-top:1px solid #e5e5e5}.carousel{position:relative;margin-bottom:20px;line-height:1}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel .item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;-moz-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel .item>img{display:block;line-height:1}.carousel .active,.carousel .next,.carousel .prev{display:block}.carousel .active{left:0}.carousel .next,.carousel .prev{position:absolute;top:0;width:100%}.carousel .next{left:100%}.carousel .prev{left:-100%}.carousel .next.left,.carousel .prev.right{left:0}.carousel .active.left{left:-100%}.carousel .active.right{left:100%}.carousel-control{position:absolute;top:40%;left:15px;width:40px;height:40px;margin-top:-20px;font-size:60px;font-weight:100;line-height:30px;color:#fff;text-align:center;background:#222;border:3px solid #fff;-webkit-border-radius:23px;-moz-border-radius:23px;border-radius:23px;opacity:.5;filter:alpha(opacity=50)}.carousel-control.right{right:15px;left:auto}.carousel-control:hover{color:#fff;text-decoration:none;opacity:.9;filter:alpha(opacity=90)}.carousel-caption{position:absolute;right:0;bottom:0;left:0;padding:15px;background:#333;background:rgba(0,0,0,0.75)}.carousel-caption h4,.carousel-caption p{line-height:20px;color:#fff}.carousel-caption h4{margin:0 0 5px}.carousel-caption p{margin-bottom:0}.hero-unit{padding:60px;margin-bottom:30px;font-size:18px;font-weight:200;line-height:30px;color:inherit;background-color:#eee;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.hero-unit h1{margin-bottom:0;font-size:60px;line-height:1;letter-spacing:-1px;color:inherit}.hero-unit li{line-height:30px}.pull-right{float:right}.pull-left{float:left}.hide{display:none}.show{display:block}.invisible{visibility:hidden}.affix{position:fixed}.copyrights{text-indent:-9999px;height:0;line-height:0;font-size:0;overflow:hidden;} diff --git a/main/webapp/static/css/font.css b/main/webapp/static/css/font.css new file mode 100644 index 0000000..b83e5b4 --- /dev/null +++ b/main/webapp/static/css/font.css @@ -0,0 +1,16 @@ +@font-face { + font-family: 'iconfont'; + src: url('../fonts/iconfont.eot'); + src: url('../fonts/iconfont.eot?#iefix') format('embedded-opentype'), + url('../fonts/iconfont.woff') format('woff'), + url('../fonts/iconfont.ttf') format('truetype'), + url('../fonts/iconfont.svg#iconfont') format('svg'); +} +.iconfont{ + font-family:"iconfont" !important; + font-size:16px;font-style:normal; + -webkit-font-smoothing: antialiased; + -webkit-text-stroke-width: 0.2px; + -moz-osx-font-smoothing: grayscale; +} + diff --git a/main/webapp/static/css/jedate.css b/main/webapp/static/css/jedate.css new file mode 100644 index 0000000..835985c --- /dev/null +++ b/main/webapp/static/css/jedate.css @@ -0,0 +1,100 @@ +/** + @Name : jeDate V6.5.0 日期控件 + @Author: chen guojun + @QQ群:516754269 + @官网:http://www.jemui.com/ 或 https://github.com/singod/jeDate + */ +@font-face {font-family: "jedatefont"; + src: url('jedatefont.eot?t=1510763148800'); /* IE9*/ + src: url('jedatefont.eot?t=1510763148800#iefix') format('embedded-opentype'), /* IE6-IE8 */ + url('jedatefont.woff?t=1510763148800') format('woff'), + url('jedatefont.ttf?t=1510763148800') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/ + url('jedatefont.svg?t=1510763148800#jedatefont') format('svg'); /* iOS 4.1- */ +} + +.jedatefont {font-family:"jedatefont" !important;font-style:normal;-webkit-font-smoothing: antialiased;-moz-osx-font-smoothing: grayscale;} +.jedate{height:auto; font-family:'PingFangSC-Light','PingFang SC','Segoe UI','Lucida Grande','NotoSansHans-Light','Microsoft YaHei', '\5FAE\8F6F\96C5\9ED1', STHeiti, 'WenQuanYi Micro Hei', SimSun, sans-serif;font-size:12px; cursor:default;margin: 0;padding: 0;overflow: hidden;position: relative;border-radius:4px;display: inline-block;border: 1px solid #e2e2e2;box-shadow: 0 1px 6px rgba(0,0,0,.15);background-color:#fff;} +.jedate *{margin: 0;padding: 0;list-style-type:none;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;font-style:normal;font-family:'PingFangSC-Light','PingFang SC','Segoe UI','Lucida Grande','NotoSansHans-Light','Microsoft YaHei', '\5FAE\8F6F\96C5\9ED1', STHeiti, 'WenQuanYi Micro Hei', SimSun, sans-serif;} + +.jedate table thead,.jedate table td{border: 1px #fff solid;} +.jedate ul,.jedate ol,.jedate li,.jedate dl{list-style-type:none;font-style:normal;font-weight: 300;} + +.jedate .yearprev{left:0;font-size: 14px;} +.jedate .monthprev{left:25px;font-size: 14px;} +.jedate .yearnext{right:0;font-size: 14px;} +.jedate .monthnext{right:25px;font-size: 14px;} +.jedate .jedate-tips{position: absolute; top: 40%; left: 50%;z-index: 800; width: 200px; margin-left: -100px; line-height: 20px; padding: 15px; text-align: center; font-size: 12px; color: #ff0000;background-color: #FFFEF4;border: 1px rgb(247, 206, 57) solid;display: none;} +.jedate .timecontent ul::-webkit-scrollbar,.jedate-menu::-webkit-scrollbar{height:6px;width:6px;margin-right:5px;background-color: #f5f5f5;transition:all 0.3s ease-in-out;border-radius:0px} +.jedate .timecontent ul::-webkit-scrollbar-track,.jedate-menu::-webkit-scrollbar-track { -webkit-border-radius: 0px;border-radius: 0px;} +.jedate .timecontent ul::-webkit-scrollbar-thumb,.jedate-menu::-webkit-scrollbar-thumb{-webkit-border-radius: 0px;border-radius: 0px;background: rgba(0,0,0,0.5); } +.jedate .timecontent ul::-webkit-scrollbar-thumb:hover,.jedate-menu::-webkit-scrollbar-thumb:hover{background:rgba(0,0,0,0.6)} +.jedate .timecontent ul::-webkit-scrollbar-thumb:active,.jedate-menu::-webkit-scrollbar-thumb:active{background:rgba(0,0,0,0.8)} +.jedate .timecontent ul::-webkit-scrollbar-thumb:window-inactive,.jedate-menu::-webkit-scrollbar-thumb:window-inactive {background: rgba(0,0,0,0.4);} +.jedate .jedate-hmsmask{width:100%;display: block;background-color: rgba(0,0,0,.7);background-color:#fff;position: absolute;top: 0;left:0;right:0;bottom: 36px;z-index: 100;} +.jedatetipscon{color:#333; float:left; overflow:hidden;background-color: #FFFEF4; line-height:22px;padding:6px;border: 1px rgb(247, 206, 57) solid;font-style:normal;font-family: Arial, "\5b8b\4f53", 'sans-serif';font-size:12px;font-weight: 300;} +.jedatetipscon p{padding: 0;margin: 0;font-size:12px;} +.jedatetipscon p.red{color: #ff0000;} + +.jedate.leftmenu{padding-left: 90px;} +.jedate .jedate-menu{width:90px;position: absolute;top: 0;left:0;bottom: 0;z-index: 10;background: #f2f2f2;border-right: 1px solid #efefef;border-radius: 4px 0 0 4px;overflow:auto;display: block;padding:4px 0;} +.jedate .jedate-menu p{height: 30px;line-height: 30px;padding-left: 8px;overflow:hidden;font-size: 12px;cursor: pointer;} +.jedate .jedate-menu p:hover{background-color: #00A680;color:#FFFFFF;} +.jedate .jedate-wrap{min-width:230px;background: #fff;overflow: hidden;} +.jedate .jedate-pane{width: 230px;float: left;overflow: hidden;} +.jedate .jedate-header{width:100%;height:36px;line-height: 36px;float: left;background-color: #f2f2f2;text-align: center;font-size: 14px;padding: 0 50px;position: relative;} +.jedate .jedate-header em{width:25px;height:36px;line-height: 36px;position:absolute;color: #666;top:0;background-repeat: no-repeat;background-position: center center;cursor: pointer;} +.jedate .jedate-header .ymbtn{padding: 8px;border-radius: 4px;cursor:pointer;font-size: 14px;} +/* .jedate .jedate-header em:hover,.jedate .jedate-header .ymbtn:hover{color: #00A680;} */ +.jedate .jedate-content{width:100%;height: 220px;float: left;padding: 5px;overflow: hidden;} +.jedate .jedate-content.bordge{border-left: 1px #e9e9e9 solid;} +.jedate .jedate-content .yeartable,.jedate .jedate-content .monthtable{width: 100%;border-collapse: collapse;border-spacing: 0;border: 1px solid #fff;} +.jedate .jedate-content .yeartable td,.jedate .jedate-content .monthtable td{width:73px;height: 51px;line-height: 51px;text-align:center; position:relative; overflow:hidden;font-size: 14px;} +.jedate .jedate-content .yeartable td span,.jedate .jedate-content .monthtable td span{padding: 8px 10px;border: 1px solid #fff;} +.jedate .jedate-content .yeartable td.action span,.jedate .jedate-content .monthtable td.action span, +.jedate .jedate-content .yeartable td.action span:hover,.jedate .jedate-content .monthtable td.action span:hover{background-color:#00A680;border:1px #00A680 solid;color:#fff;} +.jedate .jedate-content .yeartable td span:hover,.jedate .jedate-content .monthtable td span:hover{background-color:#f2f2f2;border: 1px #f2f2f2 solid;} +.jedate .jedate-content .yeartable td.disabled span,.jedate .jedate-content .monthtable td.disabled span, +.jedate .jedate-content .yeartable td.disabled span:hover,.jedate .jedate-content .monthtable td.disabled span:hover{color:#bbb;background-color:#fff;border: 1px solid #fff;} +.jedate .jedate-content .yeartable td.contain span,.jedate .jedate-content .monthtable td.contain span, +.jedate .jedate-content .yeartable td.contain span:hover,.jedate .jedate-content .monthtable td.contain span:hover{background-color: #D0F0E3;border:1px #D0F0E3 solid;} + +.jedate.grid .daystable thead,.jedate.grid .daystable td{border: 1px #f2f2f2 solid;} +.jedate .jedate-content .daystable{width: 100%;border-collapse: collapse;border-spacing: 0;border: 1px solid #fff;} +.jedate .jedate-content .daystable thead{background-color:#fff;} +.jedate .jedate-content .daystable th{width:31px;height:27px; text-align:center; position:relative; overflow:hidden;font-size: 12px;font-weight: 400;} +.jedate .jedate-content .daystable td{width:31px;height:30px; text-align:center; position:relative; overflow:hidden;font-size: 14px;font-family: Arial, "\5b8b\4f53", 'sans-serif';} +.jedate .jedate-content .daystable td .nolunar{line-height:29px;font-size: 14px;font-family: Arial, "\5b8b\4f53", 'sans-serif';} +.jedate .jedate-content .daystable td .solar{height:14px;line-height:14px;font-size: 14px;padding-top: 2px;display: block;font-family: Arial, "\5b8b\4f53", 'sans-serif';} +.jedate .jedate-content .daystable td .lunar{height:15px;line-height:15px;font-size: 12px;overflow:hidden;display: block;font-family: Arial, "\5b8b\4f53", 'sans-serif';color: #888;transform: scale(.95);} +.jedate .jedate-content .daystable td.action,.jedate .jedate-content .daystable td.action:hover, +.jedate .jedate-content .daystable td.action .lunar{background-color: #00A680;color:#fff;} +.jedate .jedate-content .daystable td.other,.jedate .jedate-content .daystable td.other .nolunar,.jedate .jedate-content .daystable td.other .lunar{color:#00DDAA;} +.jedate .jedate-content .daystable td.disabled,.jedate .jedate-content .daystable td.disabled .nolunar,.jedate .jedate-content .daystable td.disabled .lunar{ color:#bbb;} +.jedate .jedate-content .daystable td.contain,.jedate .jedate-content .daystable td.contain:hover{background-color: #00DDAA;color:#fff;} +.jedate .jedate-content .daystable td.disabled:hover{background-color:#fff;} +.jedate .jedate-content .daystable td:hover{background-color:#f2f2f2;} +.jedate .jedate-content .daystable td.red{ color:#ff0000;} +.jedate .jedate-content .daystable td .marks{ width:5px; height:5px; background-color:#ff0000; -webkit-border-radius:50%;border-radius:50%; position:absolute; right:2px; top:4px;} +.jedate .jedate-content .daystable td.action .marks{ width:5px; height:5px; background-color:#fff; -webkit-border-radius:50%;border-radius:50%; position:absolute; right:2px; top:4px;} + +.jedate .jedate-time{overflow: hidden;padding-bottom: 4px; background-color:#fff;position: absolute;top:0;right: 0;z-index: 150;} +.jedate .jedate-time .timepane{width:230px;float:left;} +.jedate .jedate-time .timeheader{width: 100%;float:left;height: 36px;line-height: 36px;background-color: #f2f2f2;text-align: center;font-size: 14px;position: relative;} +.jedate .jedate-time .timecontent{width: 100%;float:left;} +.jedate .jedate-time .hmstitle{width: 211px;margin: 0 auto;overflow: hidden;padding-top: 4px;text-align: center;} +.jedate .jedate-time .hmstitle p{width: 33.33%;float:left;height: 30px;line-height: 30px;font-size: 13px;} +.jedate .jedate-time .hmslist{width: 211px;margin: 0 auto 6px auto;border: 1px solid #ddd;border-right: none;overflow: hidden;} +.jedate .jedate-time .hmslist .hmsauto{height: 100%;margin: 0;text-align: center;} +.jedate .jedate-time .hmslist ul {width: 70px;height: 174px;float: left;border-right: 1px solid #ddd;overflow: hidden;} +.jedate .jedate-time .hmslist .hmsauto:hover ul {overflow-y: auto} +.jedate .jedate-time .hmslist ul li {width: 130%;padding-left:26px;text-align: left;height: 25px;line-height: 25px;font-size: 14px;font-family: Arial, "\5b8b\4f53", 'sans-serif';} +.jedate .jedate-time .hmslist ul li:hover{background-color: #F2F2F2;} +.jedate .jedate-time .hmslist ul li.action,.jedate-time .hmslist ul li.action:hover{background-color: #00A680;color:#fff;} +.jedate .jedate-time .hmslist ul li.disabled{ background-color: #fbfbfb;color:#ccc;} +.jedate .jedate-time .hmslist ul li.disabled.action{ background-color: #00A680;color:#FFFFFF;filter:Alpha(opacity=30);opacity:.3; } + +.jedate .jedate-footbtn{height: 36px;padding: 0 6px;border-top: 1px #e9e9e9 solid;overflow: hidden;} +.jedate .jedate-footbtn .timecon{line-height:28px;padding:0 5px;background-color:#00A680;color:#fff;display:block;float: left;font-size: 12px;margin-top:4px;border-radius:4px;overflow: hidden;} +.jedate .jedate-footbtn .btnscon{line-height:28px;margin-top:4px;display:block;float: right;font-size: 12px;border-radius:4px;overflow: hidden;} +.jedate .jedate-footbtn .btnscon span{float:left; padding:0 5px;border-right: 1px #fff solid;background-color:#00A680;color:#fff;display:block;height:28px;line-height:28px;text-align:center;overflow:hidden;} +.jedate .jedate-footbtn .btnscon span:last-child{border-right:none;} \ No newline at end of file diff --git a/main/webapp/static/css/jedate.png b/main/webapp/static/css/jedate.png new file mode 100644 index 0000000..7c722bb Binary files /dev/null and b/main/webapp/static/css/jedate.png differ diff --git a/main/webapp/static/css/jedatefont.ttf b/main/webapp/static/css/jedatefont.ttf new file mode 100644 index 0000000..4ef0c63 Binary files /dev/null and b/main/webapp/static/css/jedatefont.ttf differ diff --git a/main/webapp/static/css/site.css b/main/webapp/static/css/site.css new file mode 100644 index 0000000..95deb6a --- /dev/null +++ b/main/webapp/static/css/site.css @@ -0,0 +1,54 @@ +/* Akira CSS by Shawn Drew */ + +/* general */ +body {padding-top:20px; padding-bottom:20px;} + +h1 {padding-bottom:0.2em;} + +.meta {color:#999; font-size:.9em; display:block;} +.meta em {font-style:normal; font-weight:bold;} + +/* login */ +#login-page.container {width:320px;} + +/* dashboard */ +.summary {display:table; width:100%; padding:0; margin:0 0 20px 0;} +.summary ul {margin:0; padding:0; overflow:hidden; display:table-row;} +.summary li {margin:0; padding:19px; display:table-cell; border-right:1px solid #eee;} +.summary li:last-child {border:none;} +.summary a {display:block; font-size:1.2em; line-height:1.4em; text-transform:uppercase; color:#000;} +.summary .count {display:block; font-size:1.75em; font-weight:bold;} + + /* dashboard - ie fixes */ + .ie7 .summary {display:block;} + .ie7 .summary ul {margin:0; padding:0; overflow:hidden; display:block;} + .ie7 .summary li {float:left;} + + .ielt9 .summary .last {border:none;} + +/* tasks */ +.tasks {margin:0; padding:0;} +.tasks li {list-style-type:none; padding-bottom:0.3em;} +.tasks input {vertical-align:baseline;} +.tasks .meta {padding-left:18px;} +.tasks.done {opacity:0.8;} + +/* messages */ +.messages {margin:0; padding:0;} +.messages .messages {padding-top:19px;} +.messages li {list-style-type:none; padding-bottom:0.3em;} +.messages .well {padding:19px;} +.messages .messages .well {border:none; -webkit-box-shadow:none;-moz-box-shadow:none; box-shadow:none; border-left:3px solid #eee;border-left:3px solid rgba(0, 0, 0, 0.05);} + +/* blank slate */ +.blank-slate {text-align:center;} + +/* files */ +.files li {list-style-type:none;} +.files .meta {padding-left:18px;} + +.zebra-list {margin:0 0 18px 0; padding:0;} +.zebra-list li {border:1px solid #DDD; padding:8px; border-bottom:none;} +.zebra-list li:nth-child(odd) {background-color:#F9F9F9;} +.zebra-list li:first-child {-webkit-border-radius:4px 4px 0 0; -moz-border-radius:4px 4px 0 0; border-radius:4px 4px 0 0;} +.zebra-list li:last-child {border-bottom:1px solid #DDD; -webkit-border-radius:0 0 4px 4px; -moz-border-radius:0 0 4px 4px; border-radius:0 0 4px 4px;} \ No newline at end of file diff --git a/main/webapp/static/css/xadmin.css b/main/webapp/static/css/xadmin.css new file mode 100644 index 0000000..16c5a77 --- /dev/null +++ b/main/webapp/static/css/xadmin.css @@ -0,0 +1,523 @@ +@charset "utf-8"; +@import url(../lib/layui/css/layui.css); +*{ + margin: 0px; + padding: 0px; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; +} +a{ + text-decoration: none; +} +html{ + width: 100%; + height: 100%; + overflow-x:hidden; + overflow-y:auto; +} +body{ + width: 100%; + min-height: 100%; +} +.login-bg{ + /*background: #eeeeee url() 0 0 no-repeat;*/ + background:url(/ssm_esms/img/bg.png) no-repeat center; + background-size: cover; + overflow: hidden; +} +.login{ + margin: 120px auto 0 auto; + min-height: 420px; + max-width: 420px; + padding: 40px; + background-color: #ffffff; + margin-left: auto; + margin-right: auto; + border-radius: 4px; + /* overflow-x: hidden; */ + box-sizing: border-box; +} +.login a.logo{ + display: block; + height: 58px; + width: 167px; + margin: 0 auto 30px auto; + background-size: 167px 42px; +} +.login .message { + margin: 10px 0 0 -58px; + padding: 18px 10px 18px 60px; + background: #189F92; + position: relative; + color: #fff; + font-size: 16px; +} +.login #darkbannerwrap { + background: url(/ssm_esms/img/aiwrap.png); + width: 18px; + height: 10px; + margin: 0 0 20px -58px; + position: relative; +} + +.login input[type=text], +.login input[type=file], +.login input[type=password], +.login input[type=email], select { + border: 1px solid #DCDEE0; + vertical-align: middle; + border-radius: 3px; + height: 50px; + padding: 0px 16px; + font-size: 14px; + color: #555555; + outline:none; + width:100%; + box-sizing: border-box; +} +.login input[type=text]:focus, +.login input[type=file]:focus, +.login input[type=password]:focus, +.login input[type=email]:focus, select:focus { + border: 1px solid #27A9E3; +} +.login input[type=submit], +.login input[type=button]{ + display: inline-block; + vertical-align: middle; + padding: 12px 24px; + margin: 0px; + font-size: 18px; + line-height: 24px; + text-align: center; + white-space: nowrap; + vertical-align: middle; + cursor: pointer; + color: #ffffff; + background-color: #189F92; + border-radius: 3px; + border: none; + -webkit-appearance: none; + outline:none; + width:100%; +} +.login hr { + background: #fff 0 0 no-repeat; +} +.login hr.hr15 { + height: 15px; + border: none; + margin: 0px; + padding: 0px; + width: 100%; +} +.login hr.hr20 { + height: 20px; + border: none; + margin: 0px; + padding: 0px; + width: 100%; +} +.x-body{ + padding: 20px; +} +.x-nav{ + padding: 0 20px; + position: relative; + z-index: 99; + border-bottom: 1px solid #e5e5e5; + line-height: 39px; + height: 39px; + overflow: hidden; +} +xblock{ + display: block; + margin-bottom: 10px; + padding: 5px; + line-height: 22px; + /* border-left: 5px solid #009688; */ + border-radius: 0 2px 2px 0; + background-color: #f2f2f2; +} +.x-right{ + float: right; +} +.x-so{ + /*text-align: center;*/ + /*background: #f2f2f2 url() 0 0 no-repeat;*/ + margin-bottom: 20px; +} +.x-so input.layui-input{ + width: 150px; +} +.x-so .layui-form-label{ + display: inline-block; +} +.x-so input.layui-input,.x-so input.layui-btn{ + display: inline-block; +} +.x-red{ + color: red; +} +.x-a{ + color: #1AA093; +} +.x-a:hover{ + color: #127F74; +} +.x-sort{ + height: 30px; +} +.x-show{ + cursor: pointer; +} +.layui-form-switch{ + margin-top: 0px; +} +.layui-input:focus, .layui-textarea:focus { + border-color: #189f92!important; +} + +.page{ + margin-top: 20px; + text-align: center; + +} +.page a{ + display: inline-block; + background: #fff 0 0 no-repeat; + color: #888; + padding: 10px; + min-width: 15px; + border: 1px solid #E2E2E2; + +} +.page span{ + display: inline-block; + padding: 10px; + min-width: 15px; + border: 1px solid #E2E2E2; +} +.page span.current{ + display: inline-block; + background: #009688 0 0 no-repeat; + color: #fff; + padding: 10px; + min-width: 15px; + border: 1px solid #009688; +} +.page .pagination li{ + display: inline-block; + margin-right: 5px; + text-align: center; +} +.page .pagination li.active span{ + background: #009688 0 0 no-repeat; + color: #fff; + border: 1px solid #009688; + +} + +/*登录样式*/ +/*头部*/ +.container{ + width: 100%; + height: 45px; + background-color: #222; + border-bottom: 1px solid rgba(255, 255, 255, 0.2); +} +.container .logo a{ + float: left; + color: #fff; + font-size: 18px; + padding-left: 20px; + line-height: 45px; + width: 200px; +} +.container .right{ + background-color:rgba(0,0,0,0); + float: right; + +} +.container .left_open{ + height: 45px; + float: left; +} +.container .left_open i{ + display: block; + background: rgba(255,255,255,0.1) 0 0 no-repeat; + color: #fff; + width: 32px; + height: 32px; + line-height: 32px; + border-radius: 3px; + text-align: center; + margin-top: 7px; + cursor: pointer; +} +.container .left_open i:hover{ + background: rgba(255,255,255,0.3) 0 0 no-repeat; +} + +.container .left{ + background-color:rgba(0,0,0,0); + float: left; + +} +.container .layui-nav-item{ + line-height: 45px; +} +.container .layui-nav-more{ + top: 20px; +} +.container .layui-nav-child{ + top: 50px; +} +.container .layui-nav-child i{ + margin-right: 10px; +} +.layui-nav .layui-nav-item a{ + color: #fff; + cursor: pointer; +} +.layui-nav .layui-nav-child a{ + color: #333; + cursor: pointer; +} +.left-nav{ + position: absolute; + top: 46px; + bottom: 42px; + left: 0; + z-index: 2; + padding-top: 10px; + background-color: #EEEEEE; + width: 220px; + max-width: 220px; + overflow: auto; + overflow-x:hidden; + border-right: 1px solid #e5e5e5; + + /*width: 0px;*/ +} +.left-nav #nav li{ + border-bottom: 1px solid #e5e5e5; +} +.left-nav #nav li:hover > a{ + /*color: blue;*/ +} +.left-nav #nav .current{ + background-color: rgba(0, 0, 0, 0.3); +} +.left-nav #nav li a{ + font-size: 14px; + padding: 10px 15px 10px 20px; + display: block; + cursor: pointer; +} +.left-nav #nav li a cite{ + font-size: 14px; +} + +.left-nav #nav li .sub-menu{ + display: none; +} +.left-nav #nav li .opened{ + display: block; +} +.left-nav #nav li .opened:hover{ + /*background: #fff url() 0 0 no-repeat;*/ +} +.left-nav #nav li .opened .current{ + +} +.left-nav #nav li .sub-menu li:hover{ + /*color: blue;*/ + /*background: #fff url() 0 0 no-repeat;*/ +} +.left-nav #nav li .sub-menu li a{ + padding: 12px 15px 12px 30px; + font-size: 14px; + cursor: pointer; +} +.left-nav #nav li .sub-menu li .sub-menu li a{ + padding-left: 45px; +} +.left-nav #nav li .sub-menu li a:hover{ + color: #148cf1; +} +.left-nav #nav li .sub-menu li a i{ + font-size: 12px; +} +.left-nav #nav li a i{ + padding-right: 10px; + line-height: 14px; +} +.left-nav #nav li .nav_right{ + float: right; + font-size: 16px; +} +.x-slide_left { + width: 17px; + height: 61px; + background: url(/ssm_esms/img/icon.png) 0 0 no-repeat; + position: absolute; + top: 200px; + left: 221px; + cursor: pointer; + z-index: 3; +} +.page-content{ + position: absolute; + top: 46px; + right: 0; + bottom: 42px; + left: 221px; + overflow: hidden; + z-index: 1; +} +.page-content-bg{ + position: absolute; + top: 46px; + right: 0; + bottom: 42px; + left: 221px; + background: rgba(0,0,0,0.5); url() 0 0 no-repeat; + overflow: hidden; + z-index: 100; + display: none; +} + +.page-content .tab{ + height: 100%; + width: 100%; + background: #EFEEF0 0 0 no-repeat; + margin: 0px; +} +.page-content .layui-tab-title{ + /*padding-top: 5px;*/ + height: 35px; + background: #EFEEF0 0 0 no-repeat; + position: relative; + z-index: 100; +} +.page-content .layui-tab-title li.home i{ + padding-right: 5px; +} +.page-content .layui-tab-title li.home .layui-tab-close{ + display: none; +} +.page-content .layui-tab-title li{ + line-height: 35px; +} +.page-content .layui-tab-title .layui-this:after{ + height: 36px; +} +.page-content .layui-tab-title li .layui-tab-close{ + border-radius: 50%; +} +.page-content .layui-tab-title .layui-this{ + background: #fff 0 0 no-repeat; +} +.page-content .layui-tab-bar{ + height:34px; + line-height: 35px; +} +.page-content .layui-tab-content{ + position: absolute; + top: 36px; + bottom: 0px; + width: 100%; + background: #fff 0 0 no-repeat; + padding: 0px; + overflow: hidden; +} +.page-content .layui-tab-content .layui-tab-item{ + width: 100%; + height: 100%; + +} +.page-content .layui-tab-content .layui-tab-item iframe{ + width: 100%; + height: 100%; + +} +.x-admin-carousel,.layui-carousel,.x-admin-carousel>[carousel-item]>* { + background-color:#fff +} + +.x-admin-backlog .x-admin-backlog-body { + display:block; + padding:10px 15px; + background-color:#f8f8f8; + color:#999; + border-radius:2px; + transition:all .3s; + -webkit-transition:all .3s +} +.x-admin-backlog-body h3 { + padding-bottom:10px; + font-size:12px +} +.x-admin-backlog-body p cite { + font-style:normal; + font-size:30px; + font-weight:300; + color:#009688 +} +.x-admin-backlog-body:hover { + background-color:#CFCFCF; + color:#888 +} + +.welcome-footer{padding: 30px 0; line-height: 30px; text-align: center; background-color: #eee; color: #666; font-weight: 300;} +body .layui-layout-admin .footer-demo{height: auto; padding: 15px 0; line-height: 26px;} +.welcome-footer a{padding: 0 5px;} + +table th, table td { + word-break: break-all; +} + +.footer{ + position: fixed; + bottom: 0px; + width: 100%; + background-color: #222; + border-top: 1px solid rgba(255, 255, 255, 0.2); + line-height: 41px; + color: #fff; + /*padding-left: 10px;*/ +} +.footer .copyright{ + margin-left: 10px; +} + + +@media screen and (max-width: 768px){ + .fast-add{ + display: none; + } + .layui-nav .to-index{ + display: none; + } + .container .logo a{ + width: 140px; + } + .container .left_open { + /*float: right;*/ + } + .left-nav{ + left: -221px; + } + .page-content{ + left: 0px; + } + .page-content .layui-tab-content .layui-tab-item{ + -webkit-overflow-scrolling: touch; + overflow-y: scroll; + } + .x-so input.layui-input{ + width: 100%; + margin: 10px; + } +} + diff --git a/main/webapp/static/fonts/iconfont.eot b/main/webapp/static/fonts/iconfont.eot new file mode 100644 index 0000000..df5334a Binary files /dev/null and b/main/webapp/static/fonts/iconfont.eot differ diff --git a/main/webapp/static/fonts/iconfont.svg b/main/webapp/static/fonts/iconfont.svg new file mode 100644 index 0000000..f6f082e --- /dev/null +++ b/main/webapp/static/fonts/iconfont.svg @@ -0,0 +1,477 @@ + + + + + +Created by iconfont + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/main/webapp/static/fonts/iconfont.ttf b/main/webapp/static/fonts/iconfont.ttf new file mode 100644 index 0000000..ccf533e Binary files /dev/null and b/main/webapp/static/fonts/iconfont.ttf differ diff --git a/main/webapp/static/fonts/iconfont.woff b/main/webapp/static/fonts/iconfont.woff new file mode 100644 index 0000000..d06756c Binary files /dev/null and b/main/webapp/static/fonts/iconfont.woff differ diff --git a/main/webapp/static/js/bootstrap.js b/main/webapp/static/js/bootstrap.js new file mode 100644 index 0000000..8a2e99a --- /dev/null +++ b/main/webapp/static/js/bootstrap.js @@ -0,0 +1,2377 @@ +/*! + * Bootstrap v3.3.7 (http://getbootstrap.com) + * Copyright 2011-2016 Twitter, Inc. + * Licensed under the MIT license + */ + +if (typeof jQuery === 'undefined') { + throw new Error('Bootstrap\'s JavaScript requires jQuery') +} + ++function ($) { + 'use strict'; + var version = $.fn.jquery.split(' ')[0].split('.') + if ((version[0] < 2 && version[1] < 9) || (version[0] == 1 && version[1] == 9 && version[2] < 1) || (version[0] > 3)) { + throw new Error('Bootstrap\'s JavaScript requires jQuery version 1.9.1 or higher, but lower than version 4') + } +}(jQuery); + +/* ======================================================================== + * Bootstrap: transition.js v3.3.7 + * http://getbootstrap.com/javascript/#transitions + * ======================================================================== + * Copyright 2011-2016 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // CSS TRANSITION SUPPORT (Shoutout: http://www.modernizr.com/) + // ============================================================ + + function transitionEnd() { + var el = document.createElement('bootstrap') + + var transEndEventNames = { + WebkitTransition : 'webkitTransitionEnd', + MozTransition : 'transitionend', + OTransition : 'oTransitionEnd otransitionend', + transition : 'transitionend' + } + + for (var name in transEndEventNames) { + if (el.style[name] !== undefined) { + return { end: transEndEventNames[name] } + } + } + + return false // explicit for ie8 ( ._.) + } + + // http://blog.alexmaccaw.com/css-transitions + $.fn.emulateTransitionEnd = function (duration) { + var called = false + var $el = this + $(this).one('bsTransitionEnd', function () { called = true }) + var callback = function () { if (!called) $($el).trigger($.support.transition.end) } + setTimeout(callback, duration) + return this + } + + $(function () { + $.support.transition = transitionEnd() + + if (!$.support.transition) return + + $.event.special.bsTransitionEnd = { + bindType: $.support.transition.end, + delegateType: $.support.transition.end, + handle: function (e) { + if ($(e.target).is(this)) return e.handleObj.handler.apply(this, arguments) + } + } + }) + +}(jQuery); + +/* ======================================================================== + * Bootstrap: alert.js v3.3.7 + * http://getbootstrap.com/javascript/#alerts + * ======================================================================== + * Copyright 2011-2016 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // ALERT CLASS DEFINITION + // ====================== + + var dismiss = '[data-dismiss="alert"]' + var Alert = function (el) { + $(el).on('click', dismiss, this.close) + } + + Alert.VERSION = '3.3.7' + + Alert.TRANSITION_DURATION = 150 + + Alert.prototype.close = function (e) { + var $this = $(this) + var selector = $this.attr('data-target') + + if (!selector) { + selector = $this.attr('href') + selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7 + } + + var $parent = $(selector === '#' ? [] : selector) + + if (e) e.preventDefault() + + if (!$parent.length) { + $parent = $this.closest('.alert') + } + + $parent.trigger(e = $.Event('close.bs.alert')) + + if (e.isDefaultPrevented()) return + + $parent.removeClass('in') + + function removeElement() { + // detach from parent, fire event then clean up data + $parent.detach().trigger('closed.bs.alert').remove() + } + + $.support.transition && $parent.hasClass('fade') ? + $parent + .one('bsTransitionEnd', removeElement) + .emulateTransitionEnd(Alert.TRANSITION_DURATION) : + removeElement() + } + + + // ALERT PLUGIN DEFINITION + // ======================= + + function Plugin(option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.alert') + + if (!data) $this.data('bs.alert', (data = new Alert(this))) + if (typeof option == 'string') data[option].call($this) + }) + } + + var old = $.fn.alert + + $.fn.alert = Plugin + $.fn.alert.Constructor = Alert + + + // ALERT NO CONFLICT + // ================= + + $.fn.alert.noConflict = function () { + $.fn.alert = old + return this + } + + + // ALERT DATA-API + // ============== + + $(document).on('click.bs.alert.data-api', dismiss, Alert.prototype.close) + +}(jQuery); + +/* ======================================================================== + * Bootstrap: button.js v3.3.7 + * http://getbootstrap.com/javascript/#buttons + * ======================================================================== + * Copyright 2011-2016 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // BUTTON PUBLIC CLASS DEFINITION + // ============================== + + var Button = function (element, options) { + this.$element = $(element) + this.options = $.extend({}, Button.DEFAULTS, options) + this.isLoading = false + } + + Button.VERSION = '3.3.7' + + Button.DEFAULTS = { + loadingText: 'loading...' + } + + Button.prototype.setState = function (state) { + var d = 'disabled' + var $el = this.$element + var val = $el.is('input') ? 'val' : 'html' + var data = $el.data() + + state += 'Text' + + if (data.resetText == null) $el.data('resetText', $el[val]()) + + // push to event loop to allow forms to submit + setTimeout($.proxy(function () { + $el[val](data[state] == null ? this.options[state] : data[state]) + + if (state == 'loadingText') { + this.isLoading = true + $el.addClass(d).attr(d, d).prop(d, true) + } else if (this.isLoading) { + this.isLoading = false + $el.removeClass(d).removeAttr(d).prop(d, false) + } + }, this), 0) + } + + Button.prototype.toggle = function () { + var changed = true + var $parent = this.$element.closest('[data-toggle="buttons"]') + + if ($parent.length) { + var $input = this.$element.find('input') + if ($input.prop('type') == 'radio') { + if ($input.prop('checked')) changed = false + $parent.find('.active').removeClass('active') + this.$element.addClass('active') + } else if ($input.prop('type') == 'checkbox') { + if (($input.prop('checked')) !== this.$element.hasClass('active')) changed = false + this.$element.toggleClass('active') + } + $input.prop('checked', this.$element.hasClass('active')) + if (changed) $input.trigger('change') + } else { + this.$element.attr('aria-pressed', !this.$element.hasClass('active')) + this.$element.toggleClass('active') + } + } + + + // BUTTON PLUGIN DEFINITION + // ======================== + + function Plugin(option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.button') + var options = typeof option == 'object' && option + + if (!data) $this.data('bs.button', (data = new Button(this, options))) + + if (option == 'toggle') data.toggle() + else if (option) data.setState(option) + }) + } + + var old = $.fn.button + + $.fn.button = Plugin + $.fn.button.Constructor = Button + + + // BUTTON NO CONFLICT + // ================== + + $.fn.button.noConflict = function () { + $.fn.button = old + return this + } + + + // BUTTON DATA-API + // =============== + + $(document) + .on('click.bs.button.data-api', '[data-toggle^="button"]', function (e) { + var $btn = $(e.target).closest('.btn') + Plugin.call($btn, 'toggle') + if (!($(e.target).is('input[type="radio"], input[type="checkbox"]'))) { + // Prevent double click on radios, and the double selections (so cancellation) on checkboxes + e.preventDefault() + // The target component still receive the focus + if ($btn.is('input,button')) $btn.trigger('focus') + else $btn.find('input:visible,button:visible').first().trigger('focus') + } + }) + .on('focus.bs.button.data-api blur.bs.button.data-api', '[data-toggle^="button"]', function (e) { + $(e.target).closest('.btn').toggleClass('focus', /^focus(in)?$/.test(e.type)) + }) + +}(jQuery); + +/* ======================================================================== + * Bootstrap: carousel.js v3.3.7 + * http://getbootstrap.com/javascript/#carousel + * ======================================================================== + * Copyright 2011-2016 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // CAROUSEL CLASS DEFINITION + // ========================= + + var Carousel = function (element, options) { + this.$element = $(element) + this.$indicators = this.$element.find('.carousel-indicators') + this.options = options + this.paused = null + this.sliding = null + this.interval = null + this.$active = null + this.$items = null + + this.options.keyboard && this.$element.on('keydown.bs.carousel', $.proxy(this.keydown, this)) + + this.options.pause == 'hover' && !('ontouchstart' in document.documentElement) && this.$element + .on('mouseenter.bs.carousel', $.proxy(this.pause, this)) + .on('mouseleave.bs.carousel', $.proxy(this.cycle, this)) + } + + Carousel.VERSION = '3.3.7' + + Carousel.TRANSITION_DURATION = 600 + + Carousel.DEFAULTS = { + interval: 5000, + pause: 'hover', + wrap: true, + keyboard: true + } + + Carousel.prototype.keydown = function (e) { + if (/input|textarea/i.test(e.target.tagName)) return + switch (e.which) { + case 37: this.prev(); break + case 39: this.next(); break + default: return + } + + e.preventDefault() + } + + Carousel.prototype.cycle = function (e) { + e || (this.paused = false) + + this.interval && clearInterval(this.interval) + + this.options.interval + && !this.paused + && (this.interval = setInterval($.proxy(this.next, this), this.options.interval)) + + return this + } + + Carousel.prototype.getItemIndex = function (item) { + this.$items = item.parent().children('.item') + return this.$items.index(item || this.$active) + } + + Carousel.prototype.getItemForDirection = function (direction, active) { + var activeIndex = this.getItemIndex(active) + var willWrap = (direction == 'prev' && activeIndex === 0) + || (direction == 'next' && activeIndex == (this.$items.length - 1)) + if (willWrap && !this.options.wrap) return active + var delta = direction == 'prev' ? -1 : 1 + var itemIndex = (activeIndex + delta) % this.$items.length + return this.$items.eq(itemIndex) + } + + Carousel.prototype.to = function (pos) { + var that = this + var activeIndex = this.getItemIndex(this.$active = this.$element.find('.item.active')) + + if (pos > (this.$items.length - 1) || pos < 0) return + + if (this.sliding) return this.$element.one('slid.bs.carousel', function () { that.to(pos) }) // yes, "slid" + if (activeIndex == pos) return this.pause().cycle() + + return this.slide(pos > activeIndex ? 'next' : 'prev', this.$items.eq(pos)) + } + + Carousel.prototype.pause = function (e) { + e || (this.paused = true) + + if (this.$element.find('.next, .prev').length && $.support.transition) { + this.$element.trigger($.support.transition.end) + this.cycle(true) + } + + this.interval = clearInterval(this.interval) + + return this + } + + Carousel.prototype.next = function () { + if (this.sliding) return + return this.slide('next') + } + + Carousel.prototype.prev = function () { + if (this.sliding) return + return this.slide('prev') + } + + Carousel.prototype.slide = function (type, next) { + var $active = this.$element.find('.item.active') + var $next = next || this.getItemForDirection(type, $active) + var isCycling = this.interval + var direction = type == 'next' ? 'left' : 'right' + var that = this + + if ($next.hasClass('active')) return (this.sliding = false) + + var relatedTarget = $next[0] + var slideEvent = $.Event('slide.bs.carousel', { + relatedTarget: relatedTarget, + direction: direction + }) + this.$element.trigger(slideEvent) + if (slideEvent.isDefaultPrevented()) return + + this.sliding = true + + isCycling && this.pause() + + if (this.$indicators.length) { + this.$indicators.find('.active').removeClass('active') + var $nextIndicator = $(this.$indicators.children()[this.getItemIndex($next)]) + $nextIndicator && $nextIndicator.addClass('active') + } + + var slidEvent = $.Event('slid.bs.carousel', { relatedTarget: relatedTarget, direction: direction }) // yes, "slid" + if ($.support.transition && this.$element.hasClass('slide')) { + $next.addClass(type) + $next[0].offsetWidth // force reflow + $active.addClass(direction) + $next.addClass(direction) + $active + .one('bsTransitionEnd', function () { + $next.removeClass([type, direction].join(' ')).addClass('active') + $active.removeClass(['active', direction].join(' ')) + that.sliding = false + setTimeout(function () { + that.$element.trigger(slidEvent) + }, 0) + }) + .emulateTransitionEnd(Carousel.TRANSITION_DURATION) + } else { + $active.removeClass('active') + $next.addClass('active') + this.sliding = false + this.$element.trigger(slidEvent) + } + + isCycling && this.cycle() + + return this + } + + + // CAROUSEL PLUGIN DEFINITION + // ========================== + + function Plugin(option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.carousel') + var options = $.extend({}, Carousel.DEFAULTS, $this.data(), typeof option == 'object' && option) + var action = typeof option == 'string' ? option : options.slide + + if (!data) $this.data('bs.carousel', (data = new Carousel(this, options))) + if (typeof option == 'number') data.to(option) + else if (action) data[action]() + else if (options.interval) data.pause().cycle() + }) + } + + var old = $.fn.carousel + + $.fn.carousel = Plugin + $.fn.carousel.Constructor = Carousel + + + // CAROUSEL NO CONFLICT + // ==================== + + $.fn.carousel.noConflict = function () { + $.fn.carousel = old + return this + } + + + // CAROUSEL DATA-API + // ================= + + var clickHandler = function (e) { + var href + var $this = $(this) + var $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) // strip for ie7 + if (!$target.hasClass('carousel')) return + var options = $.extend({}, $target.data(), $this.data()) + var slideIndex = $this.attr('data-slide-to') + if (slideIndex) options.interval = false + + Plugin.call($target, options) + + if (slideIndex) { + $target.data('bs.carousel').to(slideIndex) + } + + e.preventDefault() + } + + $(document) + .on('click.bs.carousel.data-api', '[data-slide]', clickHandler) + .on('click.bs.carousel.data-api', '[data-slide-to]', clickHandler) + + $(window).on('load', function () { + $('[data-ride="carousel"]').each(function () { + var $carousel = $(this) + Plugin.call($carousel, $carousel.data()) + }) + }) + +}(jQuery); + +/* ======================================================================== + * Bootstrap: collapse.js v3.3.7 + * http://getbootstrap.com/javascript/#collapse + * ======================================================================== + * Copyright 2011-2016 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + +/* jshint latedef: false */ + ++function ($) { + 'use strict'; + + // COLLAPSE PUBLIC CLASS DEFINITION + // ================================ + + var Collapse = function (element, options) { + this.$element = $(element) + this.options = $.extend({}, Collapse.DEFAULTS, options) + this.$trigger = $('[data-toggle="collapse"][href="#' + element.id + '"],' + + '[data-toggle="collapse"][data-target="#' + element.id + '"]') + this.transitioning = null + + if (this.options.parent) { + this.$parent = this.getParent() + } else { + this.addAriaAndCollapsedClass(this.$element, this.$trigger) + } + + if (this.options.toggle) this.toggle() + } + + Collapse.VERSION = '3.3.7' + + Collapse.TRANSITION_DURATION = 350 + + Collapse.DEFAULTS = { + toggle: true + } + + Collapse.prototype.dimension = function () { + var hasWidth = this.$element.hasClass('width') + return hasWidth ? 'width' : 'height' + } + + Collapse.prototype.show = function () { + if (this.transitioning || this.$element.hasClass('in')) return + + var activesData + var actives = this.$parent && this.$parent.children('.panel').children('.in, .collapsing') + + if (actives && actives.length) { + activesData = actives.data('bs.collapse') + if (activesData && activesData.transitioning) return + } + + var startEvent = $.Event('show.bs.collapse') + this.$element.trigger(startEvent) + if (startEvent.isDefaultPrevented()) return + + if (actives && actives.length) { + Plugin.call(actives, 'hide') + activesData || actives.data('bs.collapse', null) + } + + var dimension = this.dimension() + + this.$element + .removeClass('collapse') + .addClass('collapsing')[dimension](0) + .attr('aria-expanded', true) + + this.$trigger + .removeClass('collapsed') + .attr('aria-expanded', true) + + this.transitioning = 1 + + var complete = function () { + this.$element + .removeClass('collapsing') + .addClass('collapse in')[dimension]('') + this.transitioning = 0 + this.$element + .trigger('shown.bs.collapse') + } + + if (!$.support.transition) return complete.call(this) + + var scrollSize = $.camelCase(['scroll', dimension].join('-')) + + this.$element + .one('bsTransitionEnd', $.proxy(complete, this)) + .emulateTransitionEnd(Collapse.TRANSITION_DURATION)[dimension](this.$element[0][scrollSize]) + } + + Collapse.prototype.hide = function () { + if (this.transitioning || !this.$element.hasClass('in')) return + + var startEvent = $.Event('hide.bs.collapse') + this.$element.trigger(startEvent) + if (startEvent.isDefaultPrevented()) return + + var dimension = this.dimension() + + this.$element[dimension](this.$element[dimension]())[0].offsetHeight + + this.$element + .addClass('collapsing') + .removeClass('collapse in') + .attr('aria-expanded', false) + + this.$trigger + .addClass('collapsed') + .attr('aria-expanded', false) + + this.transitioning = 1 + + var complete = function () { + this.transitioning = 0 + this.$element + .removeClass('collapsing') + .addClass('collapse') + .trigger('hidden.bs.collapse') + } + + if (!$.support.transition) return complete.call(this) + + this.$element + [dimension](0) + .one('bsTransitionEnd', $.proxy(complete, this)) + .emulateTransitionEnd(Collapse.TRANSITION_DURATION) + } + + Collapse.prototype.toggle = function () { + this[this.$element.hasClass('in') ? 'hide' : 'show']() + } + + Collapse.prototype.getParent = function () { + return $(this.options.parent) + .find('[data-toggle="collapse"][data-parent="' + this.options.parent + '"]') + .each($.proxy(function (i, element) { + var $element = $(element) + this.addAriaAndCollapsedClass(getTargetFromTrigger($element), $element) + }, this)) + .end() + } + + Collapse.prototype.addAriaAndCollapsedClass = function ($element, $trigger) { + var isOpen = $element.hasClass('in') + + $element.attr('aria-expanded', isOpen) + $trigger + .toggleClass('collapsed', !isOpen) + .attr('aria-expanded', isOpen) + } + + function getTargetFromTrigger($trigger) { + var href + var target = $trigger.attr('data-target') + || (href = $trigger.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') // strip for ie7 + + return $(target) + } + + + // COLLAPSE PLUGIN DEFINITION + // ========================== + + function Plugin(option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.collapse') + var options = $.extend({}, Collapse.DEFAULTS, $this.data(), typeof option == 'object' && option) + + if (!data && options.toggle && /show|hide/.test(option)) options.toggle = false + if (!data) $this.data('bs.collapse', (data = new Collapse(this, options))) + if (typeof option == 'string') data[option]() + }) + } + + var old = $.fn.collapse + + $.fn.collapse = Plugin + $.fn.collapse.Constructor = Collapse + + + // COLLAPSE NO CONFLICT + // ==================== + + $.fn.collapse.noConflict = function () { + $.fn.collapse = old + return this + } + + + // COLLAPSE DATA-API + // ================= + + $(document).on('click.bs.collapse.data-api', '[data-toggle="collapse"]', function (e) { + var $this = $(this) + + if (!$this.attr('data-target')) e.preventDefault() + + var $target = getTargetFromTrigger($this) + var data = $target.data('bs.collapse') + var option = data ? 'toggle' : $this.data() + + Plugin.call($target, option) + }) + +}(jQuery); + +/* ======================================================================== + * Bootstrap: dropdown.js v3.3.7 + * http://getbootstrap.com/javascript/#dropdowns + * ======================================================================== + * Copyright 2011-2016 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // DROPDOWN CLASS DEFINITION + // ========================= + + var backdrop = '.dropdown-backdrop' + var toggle = '[data-toggle="dropdown"]' + var Dropdown = function (element) { + $(element).on('click.bs.dropdown', this.toggle) + } + + Dropdown.VERSION = '3.3.7' + + function getParent($this) { + var selector = $this.attr('data-target') + + if (!selector) { + selector = $this.attr('href') + selector = selector && /#[A-Za-z]/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7 + } + + var $parent = selector && $(selector) + + return $parent && $parent.length ? $parent : $this.parent() + } + + function clearMenus(e) { + if (e && e.which === 3) return + $(backdrop).remove() + $(toggle).each(function () { + var $this = $(this) + var $parent = getParent($this) + var relatedTarget = { relatedTarget: this } + + if (!$parent.hasClass('open')) return + + if (e && e.type == 'click' && /input|textarea/i.test(e.target.tagName) && $.contains($parent[0], e.target)) return + + $parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget)) + + if (e.isDefaultPrevented()) return + + $this.attr('aria-expanded', 'false') + $parent.removeClass('open').trigger($.Event('hidden.bs.dropdown', relatedTarget)) + }) + } + + Dropdown.prototype.toggle = function (e) { + var $this = $(this) + + if ($this.is('.disabled, :disabled')) return + + var $parent = getParent($this) + var isActive = $parent.hasClass('open') + + clearMenus() + + if (!isActive) { + if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) { + // if mobile we use a backdrop because click events don't delegate + $(document.createElement('div')) + .addClass('dropdown-backdrop') + .insertAfter($(this)) + .on('click', clearMenus) + } + + var relatedTarget = { relatedTarget: this } + $parent.trigger(e = $.Event('show.bs.dropdown', relatedTarget)) + + if (e.isDefaultPrevented()) return + + $this + .trigger('focus') + .attr('aria-expanded', 'true') + + $parent + .toggleClass('open') + .trigger($.Event('shown.bs.dropdown', relatedTarget)) + } + + return false + } + + Dropdown.prototype.keydown = function (e) { + if (!/(38|40|27|32)/.test(e.which) || /input|textarea/i.test(e.target.tagName)) return + + var $this = $(this) + + e.preventDefault() + e.stopPropagation() + + if ($this.is('.disabled, :disabled')) return + + var $parent = getParent($this) + var isActive = $parent.hasClass('open') + + if (!isActive && e.which != 27 || isActive && e.which == 27) { + if (e.which == 27) $parent.find(toggle).trigger('focus') + return $this.trigger('click') + } + + var desc = ' li:not(.disabled):visible a' + var $items = $parent.find('.dropdown-menu' + desc) + + if (!$items.length) return + + var index = $items.index(e.target) + + if (e.which == 38 && index > 0) index-- // up + if (e.which == 40 && index < $items.length - 1) index++ // down + if (!~index) index = 0 + + $items.eq(index).trigger('focus') + } + + + // DROPDOWN PLUGIN DEFINITION + // ========================== + + function Plugin(option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.dropdown') + + if (!data) $this.data('bs.dropdown', (data = new Dropdown(this))) + if (typeof option == 'string') data[option].call($this) + }) + } + + var old = $.fn.dropdown + + $.fn.dropdown = Plugin + $.fn.dropdown.Constructor = Dropdown + + + // DROPDOWN NO CONFLICT + // ==================== + + $.fn.dropdown.noConflict = function () { + $.fn.dropdown = old + return this + } + + + // APPLY TO STANDARD DROPDOWN ELEMENTS + // =================================== + + $(document) + .on('click.bs.dropdown.data-api', clearMenus) + .on('click.bs.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() }) + .on('click.bs.dropdown.data-api', toggle, Dropdown.prototype.toggle) + .on('keydown.bs.dropdown.data-api', toggle, Dropdown.prototype.keydown) + .on('keydown.bs.dropdown.data-api', '.dropdown-menu', Dropdown.prototype.keydown) + +}(jQuery); + +/* ======================================================================== + * Bootstrap: modal.js v3.3.7 + * http://getbootstrap.com/javascript/#modals + * ======================================================================== + * Copyright 2011-2016 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // MODAL CLASS DEFINITION + // ====================== + + var Modal = function (element, options) { + this.options = options + this.$body = $(document.body) + this.$element = $(element) + this.$dialog = this.$element.find('.modal-dialog') + this.$backdrop = null + this.isShown = null + this.originalBodyPad = null + this.scrollbarWidth = 0 + this.ignoreBackdropClick = false + + if (this.options.remote) { + this.$element + .find('.modal-content') + .load(this.options.remote, $.proxy(function () { + this.$element.trigger('loaded.bs.modal') + }, this)) + } + } + + Modal.VERSION = '3.3.7' + + Modal.TRANSITION_DURATION = 300 + Modal.BACKDROP_TRANSITION_DURATION = 150 + + Modal.DEFAULTS = { + backdrop: true, + keyboard: true, + show: true + } + + Modal.prototype.toggle = function (_relatedTarget) { + return this.isShown ? this.hide() : this.show(_relatedTarget) + } + + Modal.prototype.show = function (_relatedTarget) { + var that = this + var e = $.Event('show.bs.modal', { relatedTarget: _relatedTarget }) + + this.$element.trigger(e) + + if (this.isShown || e.isDefaultPrevented()) return + + this.isShown = true + + this.checkScrollbar() + this.setScrollbar() + this.$body.addClass('modal-open') + + this.escape() + this.resize() + + this.$element.on('click.dismiss.bs.modal', '[data-dismiss="modal"]', $.proxy(this.hide, this)) + + this.$dialog.on('mousedown.dismiss.bs.modal', function () { + that.$element.one('mouseup.dismiss.bs.modal', function (e) { + if ($(e.target).is(that.$element)) that.ignoreBackdropClick = true + }) + }) + + this.backdrop(function () { + var transition = $.support.transition && that.$element.hasClass('fade') + + if (!that.$element.parent().length) { + that.$element.appendTo(that.$body) // don't move modals dom position + } + + that.$element + .show() + .scrollTop(0) + + that.adjustDialog() + + if (transition) { + that.$element[0].offsetWidth // force reflow + } + + that.$element.addClass('in') + + that.enforceFocus() + + var e = $.Event('shown.bs.modal', { relatedTarget: _relatedTarget }) + + transition ? + that.$dialog // wait for modal to slide in + .one('bsTransitionEnd', function () { + that.$element.trigger('focus').trigger(e) + }) + .emulateTransitionEnd(Modal.TRANSITION_DURATION) : + that.$element.trigger('focus').trigger(e) + }) + } + + Modal.prototype.hide = function (e) { + if (e) e.preventDefault() + + e = $.Event('hide.bs.modal') + + this.$element.trigger(e) + + if (!this.isShown || e.isDefaultPrevented()) return + + this.isShown = false + + this.escape() + this.resize() + + $(document).off('focusin.bs.modal') + + this.$element + .removeClass('in') + .off('click.dismiss.bs.modal') + .off('mouseup.dismiss.bs.modal') + + this.$dialog.off('mousedown.dismiss.bs.modal') + + $.support.transition && this.$element.hasClass('fade') ? + this.$element + .one('bsTransitionEnd', $.proxy(this.hideModal, this)) + .emulateTransitionEnd(Modal.TRANSITION_DURATION) : + this.hideModal() + } + + Modal.prototype.enforceFocus = function () { + $(document) + .off('focusin.bs.modal') // guard against infinite focus loop + .on('focusin.bs.modal', $.proxy(function (e) { + if (document !== e.target && + this.$element[0] !== e.target && + !this.$element.has(e.target).length) { + this.$element.trigger('focus') + } + }, this)) + } + + Modal.prototype.escape = function () { + if (this.isShown && this.options.keyboard) { + this.$element.on('keydown.dismiss.bs.modal', $.proxy(function (e) { + e.which == 27 && this.hide() + }, this)) + } else if (!this.isShown) { + this.$element.off('keydown.dismiss.bs.modal') + } + } + + Modal.prototype.resize = function () { + if (this.isShown) { + $(window).on('resize.bs.modal', $.proxy(this.handleUpdate, this)) + } else { + $(window).off('resize.bs.modal') + } + } + + Modal.prototype.hideModal = function () { + var that = this + this.$element.hide() + this.backdrop(function () { + that.$body.removeClass('modal-open') + that.resetAdjustments() + that.resetScrollbar() + that.$element.trigger('hidden.bs.modal') + }) + } + + Modal.prototype.removeBackdrop = function () { + this.$backdrop && this.$backdrop.remove() + this.$backdrop = null + } + + Modal.prototype.backdrop = function (callback) { + var that = this + var animate = this.$element.hasClass('fade') ? 'fade' : '' + + if (this.isShown && this.options.backdrop) { + var doAnimate = $.support.transition && animate + + this.$backdrop = $(document.createElement('div')) + .addClass('modal-backdrop ' + animate) + .appendTo(this.$body) + + this.$element.on('click.dismiss.bs.modal', $.proxy(function (e) { + if (this.ignoreBackdropClick) { + this.ignoreBackdropClick = false + return + } + if (e.target !== e.currentTarget) return + this.options.backdrop == 'static' + ? this.$element[0].focus() + : this.hide() + }, this)) + + if (doAnimate) this.$backdrop[0].offsetWidth // force reflow + + this.$backdrop.addClass('in') + + if (!callback) return + + doAnimate ? + this.$backdrop + .one('bsTransitionEnd', callback) + .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) : + callback() + + } else if (!this.isShown && this.$backdrop) { + this.$backdrop.removeClass('in') + + var callbackRemove = function () { + that.removeBackdrop() + callback && callback() + } + $.support.transition && this.$element.hasClass('fade') ? + this.$backdrop + .one('bsTransitionEnd', callbackRemove) + .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) : + callbackRemove() + + } else if (callback) { + callback() + } + } + + // these following methods are used to handle overflowing modals + + Modal.prototype.handleUpdate = function () { + this.adjustDialog() + } + + Modal.prototype.adjustDialog = function () { + var modalIsOverflowing = this.$element[0].scrollHeight > document.documentElement.clientHeight + + this.$element.css({ + paddingLeft: !this.bodyIsOverflowing && modalIsOverflowing ? this.scrollbarWidth : '', + paddingRight: this.bodyIsOverflowing && !modalIsOverflowing ? this.scrollbarWidth : '' + }) + } + + Modal.prototype.resetAdjustments = function () { + this.$element.css({ + paddingLeft: '', + paddingRight: '' + }) + } + + Modal.prototype.checkScrollbar = function () { + var fullWindowWidth = window.innerWidth + if (!fullWindowWidth) { // workaround for missing window.innerWidth in IE8 + var documentElementRect = document.documentElement.getBoundingClientRect() + fullWindowWidth = documentElementRect.right - Math.abs(documentElementRect.left) + } + this.bodyIsOverflowing = document.body.clientWidth < fullWindowWidth + this.scrollbarWidth = this.measureScrollbar() + } + + Modal.prototype.setScrollbar = function () { + var bodyPad = parseInt((this.$body.css('padding-right') || 0), 10) + this.originalBodyPad = document.body.style.paddingRight || '' + if (this.bodyIsOverflowing) this.$body.css('padding-right', bodyPad + this.scrollbarWidth) + } + + Modal.prototype.resetScrollbar = function () { + this.$body.css('padding-right', this.originalBodyPad) + } + + Modal.prototype.measureScrollbar = function () { // thx walsh + var scrollDiv = document.createElement('div') + scrollDiv.className = 'modal-scrollbar-measure' + this.$body.append(scrollDiv) + var scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth + this.$body[0].removeChild(scrollDiv) + return scrollbarWidth + } + + + // MODAL PLUGIN DEFINITION + // ======================= + + function Plugin(option, _relatedTarget) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.modal') + var options = $.extend({}, Modal.DEFAULTS, $this.data(), typeof option == 'object' && option) + + if (!data) $this.data('bs.modal', (data = new Modal(this, options))) + if (typeof option == 'string') data[option](_relatedTarget) + else if (options.show) data.show(_relatedTarget) + }) + } + + var old = $.fn.modal + + $.fn.modal = Plugin + $.fn.modal.Constructor = Modal + + + // MODAL NO CONFLICT + // ================= + + $.fn.modal.noConflict = function () { + $.fn.modal = old + return this + } + + + // MODAL DATA-API + // ============== + + $(document).on('click.bs.modal.data-api', '[data-toggle="modal"]', function (e) { + var $this = $(this) + var href = $this.attr('href') + var $target = $($this.attr('data-target') || (href && href.replace(/.*(?=#[^\s]+$)/, ''))) // strip for ie7 + var option = $target.data('bs.modal') ? 'toggle' : $.extend({ remote: !/#/.test(href) && href }, $target.data(), $this.data()) + + if ($this.is('a')) e.preventDefault() + + $target.one('show.bs.modal', function (showEvent) { + if (showEvent.isDefaultPrevented()) return // only register focus restorer if modal will actually get shown + $target.one('hidden.bs.modal', function () { + $this.is(':visible') && $this.trigger('focus') + }) + }) + Plugin.call($target, option, this) + }) + +}(jQuery); + +/* ======================================================================== + * Bootstrap: tooltip.js v3.3.7 + * http://getbootstrap.com/javascript/#tooltip + * Inspired by the original jQuery.tipsy by Jason Frame + * ======================================================================== + * Copyright 2011-2016 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // TOOLTIP PUBLIC CLASS DEFINITION + // =============================== + + var Tooltip = function (element, options) { + this.type = null + this.options = null + this.enabled = null + this.timeout = null + this.hoverState = null + this.$element = null + this.inState = null + + this.init('tooltip', element, options) + } + + Tooltip.VERSION = '3.3.7' + + Tooltip.TRANSITION_DURATION = 150 + + Tooltip.DEFAULTS = { + animation: true, + placement: 'top', + selector: false, + template: '', + trigger: 'hover focus', + title: '', + delay: 0, + html: false, + container: false, + viewport: { + selector: 'body', + padding: 0 + } + } + + Tooltip.prototype.init = function (type, element, options) { + this.enabled = true + this.type = type + this.$element = $(element) + this.options = this.getOptions(options) + this.$viewport = this.options.viewport && $($.isFunction(this.options.viewport) ? this.options.viewport.call(this, this.$element) : (this.options.viewport.selector || this.options.viewport)) + this.inState = { click: false, hover: false, focus: false } + + if (this.$element[0] instanceof document.constructor && !this.options.selector) { + throw new Error('`selector` option must be specified when initializing ' + this.type + ' on the window.document object!') + } + + var triggers = this.options.trigger.split(' ') + + for (var i = triggers.length; i--;) { + var trigger = triggers[i] + + if (trigger == 'click') { + this.$element.on('click.' + this.type, this.options.selector, $.proxy(this.toggle, this)) + } else if (trigger != 'manual') { + var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin' + var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout' + + this.$element.on(eventIn + '.' + this.type, this.options.selector, $.proxy(this.enter, this)) + this.$element.on(eventOut + '.' + this.type, this.options.selector, $.proxy(this.leave, this)) + } + } + + this.options.selector ? + (this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) : + this.fixTitle() + } + + Tooltip.prototype.getDefaults = function () { + return Tooltip.DEFAULTS + } + + Tooltip.prototype.getOptions = function (options) { + options = $.extend({}, this.getDefaults(), this.$element.data(), options) + + if (options.delay && typeof options.delay == 'number') { + options.delay = { + show: options.delay, + hide: options.delay + } + } + + return options + } + + Tooltip.prototype.getDelegateOptions = function () { + var options = {} + var defaults = this.getDefaults() + + this._options && $.each(this._options, function (key, value) { + if (defaults[key] != value) options[key] = value + }) + + return options + } + + Tooltip.prototype.enter = function (obj) { + var self = obj instanceof this.constructor ? + obj : $(obj.currentTarget).data('bs.' + this.type) + + if (!self) { + self = new this.constructor(obj.currentTarget, this.getDelegateOptions()) + $(obj.currentTarget).data('bs.' + this.type, self) + } + + if (obj instanceof $.Event) { + self.inState[obj.type == 'focusin' ? 'focus' : 'hover'] = true + } + + if (self.tip().hasClass('in') || self.hoverState == 'in') { + self.hoverState = 'in' + return + } + + clearTimeout(self.timeout) + + self.hoverState = 'in' + + if (!self.options.delay || !self.options.delay.show) return self.show() + + self.timeout = setTimeout(function () { + if (self.hoverState == 'in') self.show() + }, self.options.delay.show) + } + + Tooltip.prototype.isInStateTrue = function () { + for (var key in this.inState) { + if (this.inState[key]) return true + } + + return false + } + + Tooltip.prototype.leave = function (obj) { + var self = obj instanceof this.constructor ? + obj : $(obj.currentTarget).data('bs.' + this.type) + + if (!self) { + self = new this.constructor(obj.currentTarget, this.getDelegateOptions()) + $(obj.currentTarget).data('bs.' + this.type, self) + } + + if (obj instanceof $.Event) { + self.inState[obj.type == 'focusout' ? 'focus' : 'hover'] = false + } + + if (self.isInStateTrue()) return + + clearTimeout(self.timeout) + + self.hoverState = 'out' + + if (!self.options.delay || !self.options.delay.hide) return self.hide() + + self.timeout = setTimeout(function () { + if (self.hoverState == 'out') self.hide() + }, self.options.delay.hide) + } + + Tooltip.prototype.show = function () { + var e = $.Event('show.bs.' + this.type) + + if (this.hasContent() && this.enabled) { + this.$element.trigger(e) + + var inDom = $.contains(this.$element[0].ownerDocument.documentElement, this.$element[0]) + if (e.isDefaultPrevented() || !inDom) return + var that = this + + var $tip = this.tip() + + var tipId = this.getUID(this.type) + + this.setContent() + $tip.attr('id', tipId) + this.$element.attr('aria-describedby', tipId) + + if (this.options.animation) $tip.addClass('fade') + + var placement = typeof this.options.placement == 'function' ? + this.options.placement.call(this, $tip[0], this.$element[0]) : + this.options.placement + + var autoToken = /\s?auto?\s?/i + var autoPlace = autoToken.test(placement) + if (autoPlace) placement = placement.replace(autoToken, '') || 'top' + + $tip + .detach() + .css({ top: 0, left: 0, display: 'block' }) + .addClass(placement) + .data('bs.' + this.type, this) + + this.options.container ? $tip.appendTo(this.options.container) : $tip.insertAfter(this.$element) + this.$element.trigger('inserted.bs.' + this.type) + + var pos = this.getPosition() + var actualWidth = $tip[0].offsetWidth + var actualHeight = $tip[0].offsetHeight + + if (autoPlace) { + var orgPlacement = placement + var viewportDim = this.getPosition(this.$viewport) + + placement = placement == 'bottom' && pos.bottom + actualHeight > viewportDim.bottom ? 'top' : + placement == 'top' && pos.top - actualHeight < viewportDim.top ? 'bottom' : + placement == 'right' && pos.right + actualWidth > viewportDim.width ? 'left' : + placement == 'left' && pos.left - actualWidth < viewportDim.left ? 'right' : + placement + + $tip + .removeClass(orgPlacement) + .addClass(placement) + } + + var calculatedOffset = this.getCalculatedOffset(placement, pos, actualWidth, actualHeight) + + this.applyPlacement(calculatedOffset, placement) + + var complete = function () { + var prevHoverState = that.hoverState + that.$element.trigger('shown.bs.' + that.type) + that.hoverState = null + + if (prevHoverState == 'out') that.leave(that) + } + + $.support.transition && this.$tip.hasClass('fade') ? + $tip + .one('bsTransitionEnd', complete) + .emulateTransitionEnd(Tooltip.TRANSITION_DURATION) : + complete() + } + } + + Tooltip.prototype.applyPlacement = function (offset, placement) { + var $tip = this.tip() + var width = $tip[0].offsetWidth + var height = $tip[0].offsetHeight + + // manually read margins because getBoundingClientRect includes difference + var marginTop = parseInt($tip.css('margin-top'), 10) + var marginLeft = parseInt($tip.css('margin-left'), 10) + + // we must check for NaN for ie 8/9 + if (isNaN(marginTop)) marginTop = 0 + if (isNaN(marginLeft)) marginLeft = 0 + + offset.top += marginTop + offset.left += marginLeft + + // $.fn.offset doesn't round pixel values + // so we use setOffset directly with our own function B-0 + $.offset.setOffset($tip[0], $.extend({ + using: function (props) { + $tip.css({ + top: Math.round(props.top), + left: Math.round(props.left) + }) + } + }, offset), 0) + + $tip.addClass('in') + + // check to see if placing tip in new offset caused the tip to resize itself + var actualWidth = $tip[0].offsetWidth + var actualHeight = $tip[0].offsetHeight + + if (placement == 'top' && actualHeight != height) { + offset.top = offset.top + height - actualHeight + } + + var delta = this.getViewportAdjustedDelta(placement, offset, actualWidth, actualHeight) + + if (delta.left) offset.left += delta.left + else offset.top += delta.top + + var isVertical = /top|bottom/.test(placement) + var arrowDelta = isVertical ? delta.left * 2 - width + actualWidth : delta.top * 2 - height + actualHeight + var arrowOffsetPosition = isVertical ? 'offsetWidth' : 'offsetHeight' + + $tip.offset(offset) + this.replaceArrow(arrowDelta, $tip[0][arrowOffsetPosition], isVertical) + } + + Tooltip.prototype.replaceArrow = function (delta, dimension, isVertical) { + this.arrow() + .css(isVertical ? 'left' : 'top', 50 * (1 - delta / dimension) + '%') + .css(isVertical ? 'top' : 'left', '') + } + + Tooltip.prototype.setContent = function () { + var $tip = this.tip() + var title = this.getTitle() + + $tip.find('.tooltip-inner')[this.options.html ? 'html' : 'text'](title) + $tip.removeClass('fade in top bottom left right') + } + + Tooltip.prototype.hide = function (callback) { + var that = this + var $tip = $(this.$tip) + var e = $.Event('hide.bs.' + this.type) + + function complete() { + if (that.hoverState != 'in') $tip.detach() + if (that.$element) { // TODO: Check whether guarding this code with this `if` is really necessary. + that.$element + .removeAttr('aria-describedby') + .trigger('hidden.bs.' + that.type) + } + callback && callback() + } + + this.$element.trigger(e) + + if (e.isDefaultPrevented()) return + + $tip.removeClass('in') + + $.support.transition && $tip.hasClass('fade') ? + $tip + .one('bsTransitionEnd', complete) + .emulateTransitionEnd(Tooltip.TRANSITION_DURATION) : + complete() + + this.hoverState = null + + return this + } + + Tooltip.prototype.fixTitle = function () { + var $e = this.$element + if ($e.attr('title') || typeof $e.attr('data-original-title') != 'string') { + $e.attr('data-original-title', $e.attr('title') || '').attr('title', '') + } + } + + Tooltip.prototype.hasContent = function () { + return this.getTitle() + } + + Tooltip.prototype.getPosition = function ($element) { + $element = $element || this.$element + + var el = $element[0] + var isBody = el.tagName == 'BODY' + + var elRect = el.getBoundingClientRect() + if (elRect.width == null) { + // width and height are missing in IE8, so compute them manually; see https://github.com/twbs/bootstrap/issues/14093 + elRect = $.extend({}, elRect, { width: elRect.right - elRect.left, height: elRect.bottom - elRect.top }) + } + var isSvg = window.SVGElement && el instanceof window.SVGElement + // Avoid using $.offset() on SVGs since it gives incorrect results in jQuery 3. + // See https://github.com/twbs/bootstrap/issues/20280 + var elOffset = isBody ? { top: 0, left: 0 } : (isSvg ? null : $element.offset()) + var scroll = { scroll: isBody ? document.documentElement.scrollTop || document.body.scrollTop : $element.scrollTop() } + var outerDims = isBody ? { width: $(window).width(), height: $(window).height() } : null + + return $.extend({}, elRect, scroll, outerDims, elOffset) + } + + Tooltip.prototype.getCalculatedOffset = function (placement, pos, actualWidth, actualHeight) { + return placement == 'bottom' ? { top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2 } : + placement == 'top' ? { top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2 } : + placement == 'left' ? { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth } : + /* placement == 'right' */ { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width } + + } + + Tooltip.prototype.getViewportAdjustedDelta = function (placement, pos, actualWidth, actualHeight) { + var delta = { top: 0, left: 0 } + if (!this.$viewport) return delta + + var viewportPadding = this.options.viewport && this.options.viewport.padding || 0 + var viewportDimensions = this.getPosition(this.$viewport) + + if (/right|left/.test(placement)) { + var topEdgeOffset = pos.top - viewportPadding - viewportDimensions.scroll + var bottomEdgeOffset = pos.top + viewportPadding - viewportDimensions.scroll + actualHeight + if (topEdgeOffset < viewportDimensions.top) { // top overflow + delta.top = viewportDimensions.top - topEdgeOffset + } else if (bottomEdgeOffset > viewportDimensions.top + viewportDimensions.height) { // bottom overflow + delta.top = viewportDimensions.top + viewportDimensions.height - bottomEdgeOffset + } + } else { + var leftEdgeOffset = pos.left - viewportPadding + var rightEdgeOffset = pos.left + viewportPadding + actualWidth + if (leftEdgeOffset < viewportDimensions.left) { // left overflow + delta.left = viewportDimensions.left - leftEdgeOffset + } else if (rightEdgeOffset > viewportDimensions.right) { // right overflow + delta.left = viewportDimensions.left + viewportDimensions.width - rightEdgeOffset + } + } + + return delta + } + + Tooltip.prototype.getTitle = function () { + var title + var $e = this.$element + var o = this.options + + title = $e.attr('data-original-title') + || (typeof o.title == 'function' ? o.title.call($e[0]) : o.title) + + return title + } + + Tooltip.prototype.getUID = function (prefix) { + do prefix += ~~(Math.random() * 1000000) + while (document.getElementById(prefix)) + return prefix + } + + Tooltip.prototype.tip = function () { + if (!this.$tip) { + this.$tip = $(this.options.template) + if (this.$tip.length != 1) { + throw new Error(this.type + ' `template` option must consist of exactly 1 top-level element!') + } + } + return this.$tip + } + + Tooltip.prototype.arrow = function () { + return (this.$arrow = this.$arrow || this.tip().find('.tooltip-arrow')) + } + + Tooltip.prototype.enable = function () { + this.enabled = true + } + + Tooltip.prototype.disable = function () { + this.enabled = false + } + + Tooltip.prototype.toggleEnabled = function () { + this.enabled = !this.enabled + } + + Tooltip.prototype.toggle = function (e) { + var self = this + if (e) { + self = $(e.currentTarget).data('bs.' + this.type) + if (!self) { + self = new this.constructor(e.currentTarget, this.getDelegateOptions()) + $(e.currentTarget).data('bs.' + this.type, self) + } + } + + if (e) { + self.inState.click = !self.inState.click + if (self.isInStateTrue()) self.enter(self) + else self.leave(self) + } else { + self.tip().hasClass('in') ? self.leave(self) : self.enter(self) + } + } + + Tooltip.prototype.destroy = function () { + var that = this + clearTimeout(this.timeout) + this.hide(function () { + that.$element.off('.' + that.type).removeData('bs.' + that.type) + if (that.$tip) { + that.$tip.detach() + } + that.$tip = null + that.$arrow = null + that.$viewport = null + that.$element = null + }) + } + + + // TOOLTIP PLUGIN DEFINITION + // ========================= + + function Plugin(option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.tooltip') + var options = typeof option == 'object' && option + + if (!data && /destroy|hide/.test(option)) return + if (!data) $this.data('bs.tooltip', (data = new Tooltip(this, options))) + if (typeof option == 'string') data[option]() + }) + } + + var old = $.fn.tooltip + + $.fn.tooltip = Plugin + $.fn.tooltip.Constructor = Tooltip + + + // TOOLTIP NO CONFLICT + // =================== + + $.fn.tooltip.noConflict = function () { + $.fn.tooltip = old + return this + } + +}(jQuery); + +/* ======================================================================== + * Bootstrap: popover.js v3.3.7 + * http://getbootstrap.com/javascript/#popovers + * ======================================================================== + * Copyright 2011-2016 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // POPOVER PUBLIC CLASS DEFINITION + // =============================== + + var Popover = function (element, options) { + this.init('popover', element, options) + } + + if (!$.fn.tooltip) throw new Error('Popover requires tooltip.js') + + Popover.VERSION = '3.3.7' + + Popover.DEFAULTS = $.extend({}, $.fn.tooltip.Constructor.DEFAULTS, { + placement: 'right', + trigger: 'click', + content: '', + template: '' + }) + + + // NOTE: POPOVER EXTENDS tooltip.js + // ================================ + + Popover.prototype = $.extend({}, $.fn.tooltip.Constructor.prototype) + + Popover.prototype.constructor = Popover + + Popover.prototype.getDefaults = function () { + return Popover.DEFAULTS + } + + Popover.prototype.setContent = function () { + var $tip = this.tip() + var title = this.getTitle() + var content = this.getContent() + + $tip.find('.popover-title')[this.options.html ? 'html' : 'text'](title) + $tip.find('.popover-content').children().detach().end()[ // we use append for html objects to maintain js events + this.options.html ? (typeof content == 'string' ? 'html' : 'append') : 'text' + ](content) + + $tip.removeClass('fade top bottom left right in') + + // IE8 doesn't accept hiding via the `:empty` pseudo selector, we have to do + // this manually by checking the contents. + if (!$tip.find('.popover-title').html()) $tip.find('.popover-title').hide() + } + + Popover.prototype.hasContent = function () { + return this.getTitle() || this.getContent() + } + + Popover.prototype.getContent = function () { + var $e = this.$element + var o = this.options + + return $e.attr('data-content') + || (typeof o.content == 'function' ? + o.content.call($e[0]) : + o.content) + } + + Popover.prototype.arrow = function () { + return (this.$arrow = this.$arrow || this.tip().find('.arrow')) + } + + + // POPOVER PLUGIN DEFINITION + // ========================= + + function Plugin(option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.popover') + var options = typeof option == 'object' && option + + if (!data && /destroy|hide/.test(option)) return + if (!data) $this.data('bs.popover', (data = new Popover(this, options))) + if (typeof option == 'string') data[option]() + }) + } + + var old = $.fn.popover + + $.fn.popover = Plugin + $.fn.popover.Constructor = Popover + + + // POPOVER NO CONFLICT + // =================== + + $.fn.popover.noConflict = function () { + $.fn.popover = old + return this + } + +}(jQuery); + +/* ======================================================================== + * Bootstrap: scrollspy.js v3.3.7 + * http://getbootstrap.com/javascript/#scrollspy + * ======================================================================== + * Copyright 2011-2016 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // SCROLLSPY CLASS DEFINITION + // ========================== + + function ScrollSpy(element, options) { + this.$body = $(document.body) + this.$scrollElement = $(element).is(document.body) ? $(window) : $(element) + this.options = $.extend({}, ScrollSpy.DEFAULTS, options) + this.selector = (this.options.target || '') + ' .nav li > a' + this.offsets = [] + this.targets = [] + this.activeTarget = null + this.scrollHeight = 0 + + this.$scrollElement.on('scroll.bs.scrollspy', $.proxy(this.process, this)) + this.refresh() + this.process() + } + + ScrollSpy.VERSION = '3.3.7' + + ScrollSpy.DEFAULTS = { + offset: 10 + } + + ScrollSpy.prototype.getScrollHeight = function () { + return this.$scrollElement[0].scrollHeight || Math.max(this.$body[0].scrollHeight, document.documentElement.scrollHeight) + } + + ScrollSpy.prototype.refresh = function () { + var that = this + var offsetMethod = 'offset' + var offsetBase = 0 + + this.offsets = [] + this.targets = [] + this.scrollHeight = this.getScrollHeight() + + if (!$.isWindow(this.$scrollElement[0])) { + offsetMethod = 'position' + offsetBase = this.$scrollElement.scrollTop() + } + + this.$body + .find(this.selector) + .map(function () { + var $el = $(this) + var href = $el.data('target') || $el.attr('href') + var $href = /^#./.test(href) && $(href) + + return ($href + && $href.length + && $href.is(':visible') + && [[$href[offsetMethod]().top + offsetBase, href]]) || null + }) + .sort(function (a, b) { return a[0] - b[0] }) + .each(function () { + that.offsets.push(this[0]) + that.targets.push(this[1]) + }) + } + + ScrollSpy.prototype.process = function () { + var scrollTop = this.$scrollElement.scrollTop() + this.options.offset + var scrollHeight = this.getScrollHeight() + var maxScroll = this.options.offset + scrollHeight - this.$scrollElement.height() + var offsets = this.offsets + var targets = this.targets + var activeTarget = this.activeTarget + var i + + if (this.scrollHeight != scrollHeight) { + this.refresh() + } + + if (scrollTop >= maxScroll) { + return activeTarget != (i = targets[targets.length - 1]) && this.activate(i) + } + + if (activeTarget && scrollTop < offsets[0]) { + this.activeTarget = null + return this.clear() + } + + for (i = offsets.length; i--;) { + activeTarget != targets[i] + && scrollTop >= offsets[i] + && (offsets[i + 1] === undefined || scrollTop < offsets[i + 1]) + && this.activate(targets[i]) + } + } + + ScrollSpy.prototype.activate = function (target) { + this.activeTarget = target + + this.clear() + + var selector = this.selector + + '[data-target="' + target + '"],' + + this.selector + '[href="' + target + '"]' + + var active = $(selector) + .parents('li') + .addClass('active') + + if (active.parent('.dropdown-menu').length) { + active = active + .closest('li.dropdown') + .addClass('active') + } + + active.trigger('activate.bs.scrollspy') + } + + ScrollSpy.prototype.clear = function () { + $(this.selector) + .parentsUntil(this.options.target, '.active') + .removeClass('active') + } + + + // SCROLLSPY PLUGIN DEFINITION + // =========================== + + function Plugin(option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.scrollspy') + var options = typeof option == 'object' && option + + if (!data) $this.data('bs.scrollspy', (data = new ScrollSpy(this, options))) + if (typeof option == 'string') data[option]() + }) + } + + var old = $.fn.scrollspy + + $.fn.scrollspy = Plugin + $.fn.scrollspy.Constructor = ScrollSpy + + + // SCROLLSPY NO CONFLICT + // ===================== + + $.fn.scrollspy.noConflict = function () { + $.fn.scrollspy = old + return this + } + + + // SCROLLSPY DATA-API + // ================== + + $(window).on('load.bs.scrollspy.data-api', function () { + $('[data-spy="scroll"]').each(function () { + var $spy = $(this) + Plugin.call($spy, $spy.data()) + }) + }) + +}(jQuery); + +/* ======================================================================== + * Bootstrap: tab.js v3.3.7 + * http://getbootstrap.com/javascript/#tabs + * ======================================================================== + * Copyright 2011-2016 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // TAB CLASS DEFINITION + // ==================== + + var Tab = function (element) { + // jscs:disable requireDollarBeforejQueryAssignment + this.element = $(element) + // jscs:enable requireDollarBeforejQueryAssignment + } + + Tab.VERSION = '3.3.7' + + Tab.TRANSITION_DURATION = 150 + + Tab.prototype.show = function () { + var $this = this.element + var $ul = $this.closest('ul:not(.dropdown-menu)') + var selector = $this.data('target') + + if (!selector) { + selector = $this.attr('href') + selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7 + } + + if ($this.parent('li').hasClass('active')) return + + var $previous = $ul.find('.active:last a') + var hideEvent = $.Event('hide.bs.tab', { + relatedTarget: $this[0] + }) + var showEvent = $.Event('show.bs.tab', { + relatedTarget: $previous[0] + }) + + $previous.trigger(hideEvent) + $this.trigger(showEvent) + + if (showEvent.isDefaultPrevented() || hideEvent.isDefaultPrevented()) return + + var $target = $(selector) + + this.activate($this.closest('li'), $ul) + this.activate($target, $target.parent(), function () { + $previous.trigger({ + type: 'hidden.bs.tab', + relatedTarget: $this[0] + }) + $this.trigger({ + type: 'shown.bs.tab', + relatedTarget: $previous[0] + }) + }) + } + + Tab.prototype.activate = function (element, container, callback) { + var $active = container.find('> .active') + var transition = callback + && $.support.transition + && ($active.length && $active.hasClass('fade') || !!container.find('> .fade').length) + + function next() { + $active + .removeClass('active') + .find('> .dropdown-menu > .active') + .removeClass('active') + .end() + .find('[data-toggle="tab"]') + .attr('aria-expanded', false) + + element + .addClass('active') + .find('[data-toggle="tab"]') + .attr('aria-expanded', true) + + if (transition) { + element[0].offsetWidth // reflow for transition + element.addClass('in') + } else { + element.removeClass('fade') + } + + if (element.parent('.dropdown-menu').length) { + element + .closest('li.dropdown') + .addClass('active') + .end() + .find('[data-toggle="tab"]') + .attr('aria-expanded', true) + } + + callback && callback() + } + + $active.length && transition ? + $active + .one('bsTransitionEnd', next) + .emulateTransitionEnd(Tab.TRANSITION_DURATION) : + next() + + $active.removeClass('in') + } + + + // TAB PLUGIN DEFINITION + // ===================== + + function Plugin(option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.tab') + + if (!data) $this.data('bs.tab', (data = new Tab(this))) + if (typeof option == 'string') data[option]() + }) + } + + var old = $.fn.tab + + $.fn.tab = Plugin + $.fn.tab.Constructor = Tab + + + // TAB NO CONFLICT + // =============== + + $.fn.tab.noConflict = function () { + $.fn.tab = old + return this + } + + + // TAB DATA-API + // ============ + + var clickHandler = function (e) { + e.preventDefault() + Plugin.call($(this), 'show') + } + + $(document) + .on('click.bs.tab.data-api', '[data-toggle="tab"]', clickHandler) + .on('click.bs.tab.data-api', '[data-toggle="pill"]', clickHandler) + +}(jQuery); + +/* ======================================================================== + * Bootstrap: affix.js v3.3.7 + * http://getbootstrap.com/javascript/#affix + * ======================================================================== + * Copyright 2011-2016 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // AFFIX CLASS DEFINITION + // ====================== + + var Affix = function (element, options) { + this.options = $.extend({}, Affix.DEFAULTS, options) + + this.$target = $(this.options.target) + .on('scroll.bs.affix.data-api', $.proxy(this.checkPosition, this)) + .on('click.bs.affix.data-api', $.proxy(this.checkPositionWithEventLoop, this)) + + this.$element = $(element) + this.affixed = null + this.unpin = null + this.pinnedOffset = null + + this.checkPosition() + } + + Affix.VERSION = '3.3.7' + + Affix.RESET = 'affix affix-top affix-bottom' + + Affix.DEFAULTS = { + offset: 0, + target: window + } + + Affix.prototype.getState = function (scrollHeight, height, offsetTop, offsetBottom) { + var scrollTop = this.$target.scrollTop() + var position = this.$element.offset() + var targetHeight = this.$target.height() + + if (offsetTop != null && this.affixed == 'top') return scrollTop < offsetTop ? 'top' : false + + if (this.affixed == 'bottom') { + if (offsetTop != null) return (scrollTop + this.unpin <= position.top) ? false : 'bottom' + return (scrollTop + targetHeight <= scrollHeight - offsetBottom) ? false : 'bottom' + } + + var initializing = this.affixed == null + var colliderTop = initializing ? scrollTop : position.top + var colliderHeight = initializing ? targetHeight : height + + if (offsetTop != null && scrollTop <= offsetTop) return 'top' + if (offsetBottom != null && (colliderTop + colliderHeight >= scrollHeight - offsetBottom)) return 'bottom' + + return false + } + + Affix.prototype.getPinnedOffset = function () { + if (this.pinnedOffset) return this.pinnedOffset + this.$element.removeClass(Affix.RESET).addClass('affix') + var scrollTop = this.$target.scrollTop() + var position = this.$element.offset() + return (this.pinnedOffset = position.top - scrollTop) + } + + Affix.prototype.checkPositionWithEventLoop = function () { + setTimeout($.proxy(this.checkPosition, this), 1) + } + + Affix.prototype.checkPosition = function () { + if (!this.$element.is(':visible')) return + + var height = this.$element.height() + var offset = this.options.offset + var offsetTop = offset.top + var offsetBottom = offset.bottom + var scrollHeight = Math.max($(document).height(), $(document.body).height()) + + if (typeof offset != 'object') offsetBottom = offsetTop = offset + if (typeof offsetTop == 'function') offsetTop = offset.top(this.$element) + if (typeof offsetBottom == 'function') offsetBottom = offset.bottom(this.$element) + + var affix = this.getState(scrollHeight, height, offsetTop, offsetBottom) + + if (this.affixed != affix) { + if (this.unpin != null) this.$element.css('top', '') + + var affixType = 'affix' + (affix ? '-' + affix : '') + var e = $.Event(affixType + '.bs.affix') + + this.$element.trigger(e) + + if (e.isDefaultPrevented()) return + + this.affixed = affix + this.unpin = affix == 'bottom' ? this.getPinnedOffset() : null + + this.$element + .removeClass(Affix.RESET) + .addClass(affixType) + .trigger(affixType.replace('affix', 'affixed') + '.bs.affix') + } + + if (affix == 'bottom') { + this.$element.offset({ + top: scrollHeight - height - offsetBottom + }) + } + } + + + // AFFIX PLUGIN DEFINITION + // ======================= + + function Plugin(option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.affix') + var options = typeof option == 'object' && option + + if (!data) $this.data('bs.affix', (data = new Affix(this, options))) + if (typeof option == 'string') data[option]() + }) + } + + var old = $.fn.affix + + $.fn.affix = Plugin + $.fn.affix.Constructor = Affix + + + // AFFIX NO CONFLICT + // ================= + + $.fn.affix.noConflict = function () { + $.fn.affix = old + return this + } + + + // AFFIX DATA-API + // ============== + + $(window).on('load', function () { + $('[data-spy="affix"]').each(function () { + var $spy = $(this) + var data = $spy.data() + + data.offset = data.offset || {} + + if (data.offsetBottom != null) data.offset.bottom = data.offsetBottom + if (data.offsetTop != null) data.offset.top = data.offsetTop + + Plugin.call($spy, data) + }) + }) + +}(jQuery); diff --git a/main/webapp/static/js/echarts.js b/main/webapp/static/js/echarts.js new file mode 100644 index 0000000..e62e837 --- /dev/null +++ b/main/webapp/static/js/echarts.js @@ -0,0 +1,90347 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : + typeof define === 'function' && define.amd ? define(['exports'], factory) : + (factory((global.echarts = {}))); +}(this, (function (exports) { 'use strict'; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +// (1) The code `if (__DEV__) ...` can be removed by build tool. +// (2) If intend to use `__DEV__`, this module should be imported. Use a global +// variable `__DEV__` may cause that miss the declaration (see #6535), or the +// declaration is behind of the using position (for example in `Model.extent`, +// And tools like rollup can not analysis the dependency if not import). + +var dev; + +// In browser +if (typeof window !== 'undefined') { + dev = window.__DEV__; +} +// In node +else if (typeof global !== 'undefined') { + dev = global.__DEV__; +} + +if (typeof dev === 'undefined') { + dev = true; +} + +var __DEV__ = dev; + +/** + * zrender: 生成唯一id + * + * @author errorrik (errorrik@gmail.com) + */ + +var idStart = 0x0907; + +var guid = function () { + return idStart++; +}; + +/** + * echarts设备环境识别 + * + * @desc echarts基于Canvas,纯Javascript图表库,提供直观,生动,可交互,可个性化定制的数据统计图表。 + * @author firede[firede@firede.us] + * @desc thanks zepto. + */ + +var env = {}; + +if (typeof wx === 'object' && typeof wx.getSystemInfoSync === 'function') { + // In Weixin Application + env = { + browser: {}, + os: {}, + node: false, + wxa: true, // Weixin Application + canvasSupported: true, + svgSupported: false, + touchEventsSupported: true + }; +} +else if (typeof document === 'undefined' && typeof self !== 'undefined') { + // In worker + env = { + browser: {}, + os: {}, + node: false, + worker: true, + canvasSupported: true + }; +} +else if (typeof navigator === 'undefined') { + // In node + env = { + browser: {}, + os: {}, + node: true, + worker: false, + // Assume canvas is supported + canvasSupported: true, + svgSupported: true + }; +} +else { + env = detect(navigator.userAgent); +} + +var env$1 = 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+ + + var weChat = (/micromessenger/i).test(ua); + + // 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 (edge) { + browser.edge = true; + browser.version = edge[1]; + } + + // It is difficult to detect WeChat in Win Phone precisely, because ua can + // not be set on win phone. So we do not consider Win Phone. + if (weChat) { + browser.weChat = true; + } + + // 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, + svgSupported: typeof SVGRect !== 'undefined', + // 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. So we dont use that on other browsers unless tested sufficiently. + // Although IE 10 supports pointer event, it use old style and is different from the + // standard. So we exclude that. (IE 10 is hardly used on touch device) + && (browser.edge || (browser.ie && browser.version >= 11)) + // passiveSupported: detectPassiveSupport() + }; +} + +// See https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md#feature-detection +// function detectPassiveSupport() { +// // Test via a getter in the options object to see if the passive property is accessed +// var supportsPassive = false; +// try { +// var opts = Object.defineProperty({}, 'passive', { +// get: function() { +// supportsPassive = true; +// } +// }); +// window.addEventListener('testPassive', function() {}, opts); +// } catch (e) { +// } +// return supportsPassive; +// } + +/** + * @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, + // For node-canvas + '[object Image]': 1, + '[object Canvas]': 1 +}; + +var TYPED_ARRAY = { + '[object Int8Array]': 1, + '[object Uint8Array]': 1, + '[object Uint8ClampedArray]': 1, + '[object Int16Array]': 1, + '[object Uint16Array]': 1, + '[object Int32Array]': 1, + '[object Uint32Array]': 1, + '[object Float32Array]': 1, + '[object Float64Array]': 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; + +// Avoid assign to an exported variable, for transforming to cjs. +var methods = {}; + +function $override(name, fn) { + // Clear ctx instance for different environment + if (name === 'createCanvas') { + _ctx = null; + } + + methods[name] = fn; +} + +/** + * Those data types can be cloned: + * Plain object, Array, TypedArray, number, string, null, undefined. + * Those data types will be assgined using the orginal data: + * BUILTIN_OBJECT + * Instance of user defined class will be cloned to a plain object, without + * properties in prototype. + * Other data types is not supported (not sure what will happen). + * + * Caution: do not support clone Date, for performance consideration. + * (There might be a large number of date in `series.data`). + * So date should not be modified in and out of echarts. + * + * @param {*} source + * @return {*} new + */ +function clone(source) { + if (source == null || typeof source != 'object') { + return source; + } + + var result = source; + var typeStr = objToString.call(source); + + if (typeStr === '[object Array]') { + if (!isPrimitive(source)) { + result = []; + for (var i = 0, len = source.length; i < len; i++) { + result[i] = clone(source[i]); + } + } + } + else if (TYPED_ARRAY[typeStr]) { + if (!isPrimitive(source)) { + var Ctor = source.constructor; + if (source.constructor.from) { + result = Ctor.from(source); + } + else { + result = new Ctor(source.length); + for (var i = 0, len = source.length; i < len; i++) { + result[i] = clone(source[i]); + } + } + } + } + else if (!BUILTIN_OBJECT[typeStr] && !isPrimitive(source) && !isDom(source)) { + result = {}; + for (var key in source) { + if (source.hasOwnProperty(key)) { + result[key] = clone(source[key]); + } + } + } + + return result; +} + +/** + * @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$1(source) || !isObject$1(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$1(sourceProp) + && isObject$1(targetProp) + && !isArray(sourceProp) + && !isArray(targetProp) + && !isDom(sourceProp) + && !isDom(targetProp) + && !isBuiltInObject(sourceProp) + && !isBuiltInObject(targetProp) + && !isPrimitive(sourceProp) + && !isPrimitive(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 {boolean} [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; +} + +var createCanvas = function () { + return methods.createCanvas(); +}; + +methods.createCanvas = function () { + 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 = 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); +} + +/** + * Consider typed array. + * @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$1(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 {*} + */ +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$1(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$1(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 isBuiltInObject(value) { + return !!BUILTIN_OBJECT[objToString.call(value)]; +} + +/** + * @memberOf module:zrender/core/util + * @param {*} value + * @return {boolean} + */ +function isTypedArray(value) { + return !!TYPED_ARRAY[objToString.call(value)]; +} + +/** + * @memberOf module:zrender/core/util + * @param {*} value + * @return {boolean} + */ +function isDom(value) { + return typeof value === 'object' + && typeof value.nodeType === 'number' + && typeof value.ownerDocument === 'object'; +} + +/** + * Whether is exactly NaN. Notice isNaN('a') returns true. + * @param {*} value + * @return {boolean} + */ +function eqNaN(value) { + return value !== value; +} + +/** + * If value1 is not null, then return value1, otherwise judget rest of values. + * Low performance. + * @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]; + } + } +} + +function retrieve2(value0, value1) { + return value0 != null + ? value0 + : value1; +} + +function retrieve3(value0, value1, value2) { + return value0 != null + ? value0 + : value1 != null + ? value1 + : value2; +} + +/** + * @memberOf module:zrender/core/util + * @param {Array} arr + * @param {number} startIndex + * @param {number} endIndex + * @return {Array} + */ +function slice() { + return Function.call.apply(nativeSlice, arguments); +} + +/** + * 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 + * @return {Array.} + */ +function normalizeCssArray(val) { + if (typeof (val) === 'number') { + return [val, val, val, val]; + } + var len = val.length; + 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; +} + +/** + * @memberOf module:zrender/core/util + * @param {boolean} condition + * @param {string} message + */ +function assert$1(condition, message) { + if (!condition) { + throw new Error(message); + } +} + +/** + * @memberOf module:zrender/core/util + * @param {string} str string to be trimed + * @return {string} trimed string + */ +function trim(str) { + if (str == null) { + return null; + } + else if (typeof str.trim === 'function') { + return str.trim(); + } + else { + return str.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, ''); + } +} + +var primitiveKey = '__ec_primitive__'; +/** + * Set an object as primitive to be ignored traversing children in clone or merge + */ +function setAsPrimitive(obj) { + obj[primitiveKey] = true; +} + +function isPrimitive(obj) { + return obj[primitiveKey]; +} + +/** + * @constructor + * @param {Object} obj Only apply `ownProperty`. + */ +function HashMap(obj) { + var isArr = isArray(obj); + var thisMap = this; + + (obj instanceof HashMap) + ? obj.each(visit) + : (obj && each$1(obj, visit)); + + function visit(value, key) { + isArr ? thisMap.set(value, key) : thisMap.set(key, value); + } +} + +// Add prefix to avoid conflict with Object.prototype. + +HashMap.prototype = { + constructor: HashMap, + // Do not provide `has` method to avoid defining what is `has`. + // (We usually treat `null` and `undefined` as the same, different + // from ES6 Map). + get: function (key) { + return this.hasOwnProperty(key) ? this[key] : null; + }, + set: function (key, value) { + // Comparing with invocation chaining, `return value` is more commonly + // used in this case: `var someVal = map.set('a', genVal());` + return (this[key] = value); + }, + // Although util.each can be performed on this hashMap directly, user + // should not use the exposed keys, who are prefixed. + each: function (cb, context) { + context !== void 0 && (cb = bind(cb, context)); + for (var key in this) { + this.hasOwnProperty(key) && cb(this[key], key); + } + }, + // Do not use this method if performance sensitive. + removeKey: function (key) { + delete this[key]; + } +}; + +function createHashMap(obj) { + return new HashMap(obj); +} + +function concatArray(a, b) { + var newArray = new a.constructor(a.length + b.length); + for (var i = 0; i < a.length; i++) { + newArray[i] = a[i]; + } + var offset = a.length; + for (i = 0; i < b.length; i++) { + newArray[i + offset] = b[i]; + } + return newArray; +} + + +function noop() {} + + +var zrUtil = (Object.freeze || Object)({ + $override: $override, + clone: clone, + merge: merge, + mergeAll: mergeAll, + extend: extend, + defaults: defaults, + createCanvas: createCanvas, + getContext: getContext, + indexOf: indexOf, + inherits: inherits, + mixin: mixin, + isArrayLike: isArrayLike, + each: each$1, + map: map, + reduce: reduce, + filter: filter, + find: find, + bind: bind, + curry: curry, + isArray: isArray, + isFunction: isFunction$1, + isString: isString, + isObject: isObject$1, + isBuiltInObject: isBuiltInObject, + isTypedArray: isTypedArray, + isDom: isDom, + eqNaN: eqNaN, + retrieve: retrieve, + retrieve2: retrieve2, + retrieve3: retrieve3, + slice: slice, + normalizeCssArray: normalizeCssArray, + assert: assert$1, + trim: trim, + setAsPrimitive: setAsPrimitive, + isPrimitive: isPrimitive, + createHashMap: createHashMap, + concatArray: concatArray, + noop: noop +}); + +var ArrayCtor = typeof Float32Array === 'undefined' + ? Array + : Float32Array; + +/** + * 创建一个向量 + * @param {number} [x=0] + * @param {number} [y=0] + * @return {Vector2} + */ +function create(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} + */ +function copy(out, v) { + out[0] = v[0]; + out[1] = v[1]; + return out; +} + +/** + * 克隆一个向量 + * @param {Vector2} v + * @return {Vector2} + */ +function clone$1(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} 结果 + */ +function set(out, a, b) { + out[0] = a; + out[1] = b; + return out; +} + +/** + * 向量相加 + * @param {Vector2} out + * @param {Vector2} v1 + * @param {Vector2} v2 + */ +function add(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 + */ +function scaleAndAdd(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 + */ +function sub(out, v1, v2) { + out[0] = v1[0] - v2[0]; + out[1] = v1[1] - v2[1]; + return out; +} + +/** + * 向量长度 + * @param {Vector2} v + * @return {number} + */ +function len(v) { + return Math.sqrt(lenSquare(v)); +} +var length = len; // jshint ignore:line + +/** + * 向量长度平方 + * @param {Vector2} v + * @return {number} + */ +function lenSquare(v) { + return v[0] * v[0] + v[1] * v[1]; +} +var lengthSquare = lenSquare; + +/** + * 向量乘法 + * @param {Vector2} out + * @param {Vector2} v1 + * @param {Vector2} v2 + */ +function mul(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 + */ +function div(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} + */ +function dot(v1, v2) { + return v1[0] * v2[0] + v1[1] * v2[1]; +} + +/** + * 向量缩放 + * @param {Vector2} out + * @param {Vector2} v + * @param {number} s + */ +function scale(out, v, s) { + out[0] = v[0] * s; + out[1] = v[1] * s; + return out; +} + +/** + * 向量归一化 + * @param {Vector2} out + * @param {Vector2} v + */ +function normalize(out, v) { + var d = 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} + */ +function distance(v1, v2) { + return Math.sqrt( + (v1[0] - v2[0]) * (v1[0] - v2[0]) + + (v1[1] - v2[1]) * (v1[1] - v2[1]) + ); +} +var dist = distance; + +/** + * 向量距离平方 + * @param {Vector2} v1 + * @param {Vector2} v2 + * @return {number} + */ +function distanceSquare(v1, v2) { + return (v1[0] - v2[0]) * (v1[0] - v2[0]) + + (v1[1] - v2[1]) * (v1[1] - v2[1]); +} +var distSquare = distanceSquare; + +/** + * 求负向量 + * @param {Vector2} out + * @param {Vector2} v + */ +function negate(out, v) { + out[0] = -v[0]; + out[1] = -v[1]; + return out; +} + +/** + * 插值两个点 + * @param {Vector2} out + * @param {Vector2} v1 + * @param {Vector2} v2 + * @param {number} t + */ +function lerp(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 + */ +function applyTransform(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 + */ +function min(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 + */ +function max(out, v1, v2) { + out[0] = Math.max(v1[0], v2[0]); + out[1] = Math.max(v1[1], v2[1]); + return out; +} + + +var vector = (Object.freeze || Object)({ + create: create, + copy: copy, + clone: clone$1, + set: set, + add: add, + scaleAndAdd: scaleAndAdd, + sub: sub, + len: len, + length: length, + lenSquare: lenSquare, + lengthSquare: lengthSquare, + mul: mul, + div: div, + dot: dot, + scale: scale, + normalize: normalize, + distance: distance, + dist: dist, + distanceSquare: distanceSquare, + distSquare: distSquare, + negate: negate, + lerp: lerp, + applyTransform: applyTransform, + min: min, + max: max +}); + +// 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(param(draggingTarget, e), '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(param(draggingTarget, e), 'drag', e.event); + + var dropTarget = this.findHover(x, y, draggingTarget).target; + var lastDropTarget = this._dropTarget; + this._dropTarget = dropTarget; + + if (draggingTarget !== dropTarget) { + if (lastDropTarget && dropTarget !== lastDropTarget) { + this.dispatchToElement(param(lastDropTarget, e), 'dragleave', e.event); + } + if (dropTarget && dropTarget !== lastDropTarget) { + this.dispatchToElement(param(dropTarget, e), 'dragenter', e.event); + } + } + } + }, + + _dragEnd: function (e) { + var draggingTarget = this._draggingTarget; + + if (draggingTarget) { + draggingTarget.dragging = false; + } + + this.dispatchToElement(param(draggingTarget, e), 'dragend', e.event); + + if (this._dropTarget) { + this.dispatchToElement(param(this._dropTarget, e), 'drop', e.event); + } + + this._draggingTarget = null; + this._dropTarget = null; + } + +}; + +function param(target, e) { + return {target: target, topTarget: e && e.topTarget}; +} + +/** + * 事件扩展 + * @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; + } +}; + +var SILENT = 'silent'; + +function makeEventPacket(eveType, targetInfo, event) { + return { + type: eveType, + event: event, + // target can only be an element that is not silent. + target: targetInfo.target, + // topTarget can be a silent element. + topTarget: targetInfo.topTarget, + cancelBubble: false, + offsetX: event.zrX, + offsetY: event.zrY, + gestureEvent: event.gestureEvent, + pinchX: event.pinchX, + pinchY: event.pinchY, + pinchScale: event.pinchScale, + wheelDelta: event.zrDelta, + zrByTouch: event.zrByTouch, + which: event.which + }; +} + +function EmptyProxy () {} +EmptyProxy.prototype.dispose = function () {}; + +var handlerNames = [ + 'click', 'dblclick', 'mousewheel', 'mouseout', + 'mouseup', 'mousedown', 'mousemove', 'contextmenu' +]; +/** + * @alias module:zrender/Handler + * @constructor + * @extends module:zrender/mixin/Eventful + * @param {module:zrender/Storage} storage Storage instance. + * @param {module:zrender/Painter} painter Painter instance. + * @param {module:zrender/dom/HandlerProxy} proxy HandlerProxy instance. + * @param {HTMLElement} painterRoot painter.root (not painter.getViewportRoot()). + */ +var Handler = function(storage, painter, proxy, painterRoot) { + Eventful.call(this); + + this.storage = storage; + + this.painter = painter; + + this.painterRoot = painterRoot; + + proxy = proxy || new EmptyProxy(); + + /** + * Proxy of event. can be Dom, WebGLSurface, etc. + */ + this.proxy = null; + + /** + * {target, topTarget, x, y} + * @private + * @type {Object} + */ + this._hovered = {}; + + /** + * @private + * @type {Date} + */ + this._lastTouchMoment; + + /** + * @private + * @type {number} + */ + this._lastX; + + /** + * @private + * @type {number} + */ + this._lastY; + + + Draggable.call(this); + + this.setHandlerProxy(proxy); +}; + +Handler.prototype = { + + constructor: Handler, + + setHandlerProxy: function (proxy) { + if (this.proxy) { + this.proxy.dispose(); + } + + if (proxy) { + each$1(handlerNames, function (name) { + proxy.on && proxy.on(name, this[name], this); + }, this); + // Attach handler + proxy.handler = this; + } + this.proxy = proxy; + }, + + mousemove: function (event) { + var x = event.zrX; + var y = event.zrY; + + var lastHovered = this._hovered; + var lastHoveredTarget = lastHovered.target; + + // If lastHoveredTarget is removed from zr (detected by '__zr') by some API call + // (like 'setOption' or 'dispatchAction') in event handlers, we should find + // lastHovered again here. Otherwise 'mouseout' can not be triggered normally. + // See #6198. + if (lastHoveredTarget && !lastHoveredTarget.__zr) { + lastHovered = this.findHover(lastHovered.x, lastHovered.y); + lastHoveredTarget = lastHovered.target; + } + + var hovered = this._hovered = this.findHover(x, y); + var hoveredTarget = hovered.target; + + var proxy = this.proxy; + proxy.setCursor && proxy.setCursor(hoveredTarget ? hoveredTarget.cursor : 'default'); + + // Mouse out on previous hovered element + if (lastHoveredTarget && hoveredTarget !== lastHoveredTarget) { + this.dispatchToElement(lastHovered, 'mouseout', event); + } + + // Mouse moving on one element + this.dispatchToElement(hovered, 'mousemove', event); + + // Mouse over on a new element + if (hoveredTarget && hoveredTarget !== lastHoveredTarget) { + this.dispatchToElement(hovered, 'mouseover', event); + } + }, + + mouseout: function (event) { + this.dispatchToElement(this._hovered, 'mouseout', event); + + // There might be some doms created by upper layer application + // at the same level of painter.getViewportRoot() (e.g., tooltip + // dom created by echarts), where 'globalout' event should not + // be triggered when mouse enters these doms. (But 'mouseout' + // should be triggered at the original hovered element as usual). + var element = event.toElement || event.relatedTarget; + var innerDom; + do { + element = element && element.parentNode; + } + while (element && element.nodeType != 9 && !( + innerDom = element === this.painterRoot + )); + + !innerDom && this.trigger('globalout', {event: event}); + }, + + /** + * Resize + */ + resize: function (event) { + this._hovered = {}; + }, + + /** + * 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} targetInfo {target, topTarget} 目标图形元素 + * @param {string} eventName 事件名称 + * @param {Object} event 事件对象 + */ + dispatchToElement: function (targetInfo, eventName, event) { + targetInfo = targetInfo || {}; + var el = targetInfo.target; + if (el && el.silent) { + return; + } + var eventHandler = 'on' + eventName; + var eventPacket = makeEventPacket(eventName, targetInfo, event); + + 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 + * @return {model:zrender/Element} + * @method + */ + findHover: function(x, y, exclude) { + var list = this.storage.getDisplayList(); + var out = {x: x, y: y}; + + for (var i = list.length - 1; i >= 0 ; i--) { + var hoverCheckResult; + if (list[i] !== exclude + // getDisplayList may include ignored item in VML mode + && !list[i].ignore + && (hoverCheckResult = isHover(list[i], x, y)) + ) { + !out.topTarget && (out.topTarget = list[i]); + if (hoverCheckResult !== SILENT) { + out.target = list[i]; + break; + } + } + } + + return out; + } +}; + +// Common handlers +each$1(['click', 'mousedown', 'mouseup', 'mousewheel', 'dblclick', 'contextmenu'], 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); + var hoveredTarget = hovered.target; + + if (name === 'mousedown') { + this._downEl = hoveredTarget; + this._downPoint = [event.zrX, event.zrY]; + // In case click triggered before mouseup + this._upEl = hoveredTarget; + } + else if (name === 'mouseup') { + this._upEl = hoveredTarget; + } + else if (name === 'click') { + if (this._downEl !== this._upEl + // Original click event is triggered on the whole canvas element, + // including the case that `mousedown` - `mousemove` - `mouseup`, + // which should be filtered, otherwise it will bring trouble to + // pan and zoom. + || !this._downPoint + // Arbitrary value + || dist(this._downPoint, [event.zrX, event.zrY]) > 4 + ) { + return; + } + this._downPoint = null; + } + + this.dispatchToElement(hovered, name, event); + }; +}); + +function isHover(displayable, x, y) { + if (displayable[displayable.rectHover ? 'rectContain' : 'contain'](x, y)) { + var el = displayable; + var isSilent; + while (el) { + // If clipped by ancestor. + // FIXME: If clipPath has neither stroke nor fill, + // el.clipPath.contain(x, y) will always return false. + if (el.clipPath && !el.clipPath.contain(x, y)) { + return false; + } + if (el.silent) { + isSilent = true; + } + el = el.parent; + } + return isSilent ? SILENT : true; + } + + return false; +} + +mixin(Handler, Eventful); +mixin(Handler, Draggable); + +/** + * 3x2矩阵操作类 + * @exports zrender/tool/matrix + */ + +var ArrayCtor$1 = typeof Float32Array === 'undefined' + ? Array + : Float32Array; + +/** + * Create a identity matrix. + * @return {Float32Array|Array.} + */ +function create$1() { + var out = new ArrayCtor$1(6); + identity(out); + + return out; +} + +/** + * 设置矩阵为单位矩阵 + * @param {Float32Array|Array.} out + */ +function identity(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 + */ +function copy$1(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 + */ +function mul$1(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 + */ +function translate(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 + */ +function rotate(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 + */ +function scale$1(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 + */ +function invert(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; +} + +/** + * Clone a new matrix. + * @param {Float32Array|Array.} a + */ +function clone$2(a) { + var b = create$1(); + copy$1(b, a); + return b; +} + +var matrix = (Object.freeze || Object)({ + create: create$1, + identity: identity, + copy: copy$1, + mul: mul$1, + translate: translate, + rotate: rotate, + scale: scale$1, + invert: invert, + clone: clone$2 +}); + +/** + * 提供变换扩展 + * @module zrender/mixin/Transformable + * @author pissang (https://www.github.com/pissang) + */ + +var mIdentity = 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 || create$1(); + + if (needLocalTransform) { + this.getLocalTransform(m); + } + else { + mIdentity(m); + } + + // 应用父节点变换 + if (parentHasTransform) { + if (needLocalTransform) { + mul$1(m, parent.transform, m); + } + else { + copy$1(m, parent.transform); + } + } + // 保存这个变换矩阵 + this.transform = m; + + this.invTransform = this.invTransform || create$1(); + invert(this.invTransform, m); +}; + +transformableProto.getLocalTransform = function (m) { + return Transformable.getLocalTransform(this, m); +}; + +/** + * 将自己的transform应用到context上 + * @param {CanvasRenderingContext2D} 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 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 + mul$1(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$$1 = 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$$1[0] = sx; + scale$$1[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) { + 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) { + applyTransform(v2, v2, transform); + } + return v2; +}; + +/** + * @static + * @param {Object} target + * @param {Array.} target.origin + * @param {number} target.rotation + * @param {Array.} target.position + * @param {Array.} [m] + */ +Transformable.getLocalTransform = function (target, m) { + m = m || []; + mIdentity(m); + + var origin = target.origin; + var scale$$1 = target.scale || [1, 1]; + var rotation = target.rotation || 0; + var position = target.position || [0, 0]; + + if (origin) { + // Translate to origin + m[4] -= origin[0]; + m[5] -= origin[1]; + } + scale$1(m, m, scale$$1); + if (rotation) { + 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; +}; + +/** + * 缓动代码来自 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; + } +}; + +/** + * 动画主控制器 + * @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 + */ + +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; + + this._pausedTime = 0; + this._paused = false; +} + +Clip.prototype = { + + constructor: Clip, + + step: function (globalTime, deltaTime) { + // 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; + } + + if (this._paused) { + this._pausedTime += deltaTime; + return; + } + + var percent = (globalTime - this._startTime - this._pausedTime) / this._life; + + // 还没开始 + if (percent < 0) { + return; + } + + percent = Math.min(percent, 1); + + var easing$$1 = this.easing; + var easingFunc = typeof easing$$1 == 'string' ? easing[easing$$1] : easing$$1; + 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._pausedTime) % this._life; + this._startTime = globalTime - remainder + this.gap; + this._pausedTime = 0; + + this._needsRemove = false; + }, + + fire: function (eventType, arg) { + eventType = 'on' + eventType; + if (this[eventType]) { + this[eventType](this._target, arg); + } + }, + + pause: function () { + this._paused = true; + }, + + resume: function () { + this._paused = false; + } +}; + +// 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; + entry.next = null; + 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; +}; + +/** + * Clear list + */ +linkedListProto.clear = function () { + this.head = this.tail = null; + this._len = 0; +}; + +/** + * @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; + + this._lastRemovedEntry = null; +}; + +var LRUProto = LRU.prototype; + +/** + * @param {string} key + * @param {} value + * @return {} Removed value + */ +LRUProto.put = function (key, value) { + var list = this._list; + var map = this._map; + var removed = null; + if (map[key] == null) { + var len = list.len(); + // Reuse last removed entry + var entry = this._lastRemovedEntry; + + if (len >= this._maxSize && len > 0) { + // Remove the least recently used + var leastUsedEntry = list.head; + list.remove(leastUsedEntry); + delete map[leastUsedEntry.key]; + + removed = leastUsedEntry.value; + this._lastRemovedEntry = leastUsedEntry; + } + + if (entry) { + entry.value = value; + } + else { + entry = new Entry(value); + } + entry.key = key; + list.insertEntry(entry); + map[key] = entry; + } + + return removed; +}; + +/** + * @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 = {}; +}; + +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 lerpNumber(a, b, p) { + return a + (b - a) * p; +} + +function setRgba(out, r, g, b, a) { + out[0] = r; out[1] = g; out[2] = b; out[3] = a; + return out; +} +function copyRgba(out, a) { + out[0] = a[0]; out[1] = a[1]; out[2] = a[2]; out[3] = a[3]; + return out; +} + +var colorCache = new LRU(20); +var lastRemovedArr = null; + +function putToCache(colorStr, rgbaArr) { + // Reuse removed array + if (lastRemovedArr) { + copyRgba(lastRemovedArr, rgbaArr); + } + lastRemovedArr = colorCache.put(colorStr, lastRemovedArr || (rgbaArr.slice())); +} + +/** + * @param {string} colorStr + * @param {Array.} out + * @return {Array.} + * @memberOf module:zrender/util/color + */ +function parse(colorStr, rgbaArr) { + if (!colorStr) { + return; + } + rgbaArr = rgbaArr || []; + + var cached = colorCache.get(colorStr); + if (cached) { + return copyRgba(rgbaArr, cached); + } + + // 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) { + copyRgba(rgbaArr, kCSSColorTable[str]); + putToCache(colorStr, rgbaArr); + return rgbaArr; + } + + // #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)) { + setRgba(rgbaArr, 0, 0, 0, 1); + return; // Covers NaN. + } + setRgba(rgbaArr, + ((iv & 0xf00) >> 4) | ((iv & 0xf00) >> 8), + (iv & 0xf0) | ((iv & 0xf0) >> 4), + (iv & 0xf) | ((iv & 0xf) << 4), + 1 + ); + putToCache(colorStr, rgbaArr); + return rgbaArr; + } + else if (str.length === 7) { + var iv = parseInt(str.substr(1), 16); // TODO(deanm): Stricter parsing. + if (!(iv >= 0 && iv <= 0xffffff)) { + setRgba(rgbaArr, 0, 0, 0, 1); + return; // Covers NaN. + } + setRgba(rgbaArr, + (iv & 0xff0000) >> 16, + (iv & 0xff00) >> 8, + iv & 0xff, + 1 + ); + putToCache(colorStr, rgbaArr); + return rgbaArr; + } + + 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) { + setRgba(rgbaArr, 0, 0, 0, 1); + return; + } + alpha = parseCssFloat(params.pop()); // jshint ignore:line + // Fall through. + case 'rgb': + if (params.length !== 3) { + setRgba(rgbaArr, 0, 0, 0, 1); + return; + } + setRgba(rgbaArr, + parseCssInt(params[0]), + parseCssInt(params[1]), + parseCssInt(params[2]), + alpha + ); + putToCache(colorStr, rgbaArr); + return rgbaArr; + case 'hsla': + if (params.length !== 4) { + setRgba(rgbaArr, 0, 0, 0, 1); + return; + } + params[3] = parseCssFloat(params[3]); + hsla2rgba(params, rgbaArr); + putToCache(colorStr, rgbaArr); + return rgbaArr; + case 'hsl': + if (params.length !== 3) { + setRgba(rgbaArr, 0, 0, 0, 1); + return; + } + hsla2rgba(params, rgbaArr); + putToCache(colorStr, rgbaArr); + return rgbaArr; + default: + return; + } + } + + setRgba(rgbaArr, 0, 0, 0, 1); + return; +} + +/** + * @param {Array.} hsla + * @param {Array.} rgba + * @return {Array.} rgba + */ +function hsla2rgba(hsla, rgba) { + 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; + + rgba = rgba || []; + setRgba(rgba, + clampCssByte(cssHueToRgb(m1, m2, h + 1 / 3) * 255), + clampCssByte(cssHueToRgb(m1, m2, h) * 255), + clampCssByte(cssHueToRgb(m1, m2, h - 1 / 3) * 255), + 1 + ); + + 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; + } + if (colorArr[i] > 255) { + colorArr[i] = 255; + } + else if (color[i] < 0) { + colorArr[i] = 0; + } + } + return stringify(colorArr, colorArr.length === 4 ? 'rgba' : 'rgb'); + } +} + +/** + * @param {string} color + * @return {string} + * @memberOf module:zrender/util/color + */ +function toHex(color) { + 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 lerp 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.} will be null/undefined if input illegal. + */ +function fastLerp(normalizedValue, colors, out) { + if (!(colors && colors.length) + || !(normalizedValue >= 0 && normalizedValue <= 1) + ) { + return; + } + + out = out || []; + + 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(lerpNumber(leftColor[0], rightColor[0], dv)); + out[1] = clampCssByte(lerpNumber(leftColor[1], rightColor[1], dv)); + out[2] = clampCssByte(lerpNumber(leftColor[2], rightColor[2], dv)); + out[3] = clampCssFloat(lerpNumber(leftColor[3], rightColor[3], dv)); + + return out; +} + +/** + * @deprecated + */ +var fastMapToColor = fastLerp; + +/** + * @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 lerp$1(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(lerpNumber(leftColor[0], rightColor[0], dv)), + clampCssByte(lerpNumber(leftColor[1], rightColor[1], dv)), + clampCssByte(lerpNumber(leftColor[2], rightColor[2], dv)), + clampCssFloat(lerpNumber(leftColor[3], rightColor[3], dv)) + ], + 'rgba' + ); + + return fullOutput + ? { + color: color, + leftIndex: leftIndex, + rightIndex: rightIndex, + value: value + } + : color; +} + +/** + * @deprecated + */ +var mapToColor = lerp$1; + +/** + * @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.} arrColor like [12,33,44,0.4] + * @param {string} type 'rgba', 'hsva', ... + * @return {string} Result color. (If input illegal, return undefined). + */ +function stringify(arrColor, type) { + if (!arrColor || !arrColor.length) { + return; + } + var colorStr = arrColor[0] + ',' + arrColor[1] + ',' + arrColor[2]; + if (type === 'rgba' || type === 'hsva' || type === 'hsla') { + colorStr += ',' + arrColor[3]; + } + return type + '(' + colorStr + ')'; +} + + +var color = (Object.freeze || Object)({ + parse: parse, + lift: lift, + toHex: toHex, + fastLerp: fastLerp, + fastMapToColor: fastMapToColor, + lerp: lerp$1, + mapToColor: mapToColor, + modifyHSL: modifyHSL, + modifyAlpha: modifyAlpha, + stringify: stringify +}); + +/** + * @module echarts/animation/Animator + */ + +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 = len && 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 getArrayDim(keyframes) { + var lastValue = keyframes[keyframes.length - 1].value; + return isArrayLike(lastValue && lastValue[0]) ? 2 : 1; +} + +function createTrackClip(animator, easing, oneTrackDone, keyframes, propName, forceAnimate) { + 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 ? getArrayDim(keyframes) : 0; + + 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 = parse(value); + if (colorArray) { + value = colorArray; + isValueColor = true; + } + else { + isValueString = true; + } + } + kfValues.push(value); + } + if (!forceAnimate && 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 (!props.hasOwnProperty(propName)) { + continue; + } + + 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; + }, + + pause: function () { + for (var i = 0; i < this._clipList.length; i++) { + this._clipList[i].pause(); + } + this._paused = true; + }, + + resume: function () { + for (var i = 0; i < this._clipList.length; i++) { + this._clipList[i].resume(); + } + this._paused = false; + }, + + isPaused: function () { + return !!this._paused; + }, + + _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} + * @param {boolean} forceAnimate + * @return {module:zrender/animation/Animator} + */ + start: function (easing, forceAnimate) { + + var self = this; + var clipCount = 0; + + var oneTrackDone = function() { + clipCount--; + if (!clipCount) { + self._doneCallback(); + } + }; + + var lastClip; + for (var propName in this._tracks) { + if (!this._tracks.hasOwnProperty(propName)) { + continue; + } + var clip = createTrackClip( + this, easing, oneTrackDone, + this._tracks[propName], propName, forceAnimate + ); + 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); + } + }; + } + + // This optimization will help the case that in the upper application + // the view may be refreshed frequently, where animation will be + // called repeatly but nothing changed. + 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; + } +}; + +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) + */ + +/** + * debug日志选项:catchBrushException为true下有效 + * 0 : 不生成debug数据,发布用 + * 1 : 异常抛出,调试用 + * 2 : 控制台输出,调试用 + */ +var debugMode = 0; + +// retina 屏幕优化 +var devicePixelRatio = dpr; + +var log = function () { +}; + +if (debugMode === 1) { + log = function () { + for (var k in arguments) { + throw new Error(arguments[k]); + } + }; +} +else if (debugMode > 1) { + log = function () { + for (var k in arguments) { + console.log(arguments[k]); + } + }; +} + +var zrLog = log; + +/** + * @alias modue:zrender/mixin/Animatable + * @constructor + */ +var Animatable = function () { + + /** + * @type {Array.} + * @readOnly + */ + this.animators = []; +}; + +Animatable.prototype = { + + constructor: Animatable, + + /** + * 动画 + * + * @param {string} path The path to fetch value from object, like 'a.b.c'. + * @param {boolean} [loop] Whether to loop animation. + * @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) { + zrLog( + '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(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; + }, + + /** + * Caution: this method will stop previous animation. + * So do not use this method to one element twice before + * animation starts, unless you know what you are doing. + * @param {Object} target + * @param {number} [time=500] Time in ms + * @param {string} [easing='linear'] + * @param {number} [delay=0] + * @param {Function} [callback] + * @param {Function} [forceAnimate] Prevent stop animation and callback + * immediently when target values are the same as current values. + * + * @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, forceAnimate) { + // animateTo(target, time, easing, callback); + if (isString(delay)) { + callback = easing; + easing = delay; + delay = 0; + } + // animateTo(target, time, delay, callback); + else if (isFunction$1(easing)) { + callback = easing; + easing = 'linear'; + delay = 0; + } + // animateTo(target, time, callback); + else if (isFunction$1(delay)) { + callback = delay; + delay = 0; + } + // animateTo(target, callback) + else if (isFunction$1(time)) { + callback = time; + time = 500; + } + // animateTo(target) + else if (!time) { + time = 500; + } + // Stop all previous animations + this.stopAnimation(); + this._animateToShallow('', this, target, time, delay); + + // 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, forceAnimate); + } + }, + + /** + * @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 (!target.hasOwnProperty(name)) { + continue; + } + + if (source[name] != null) { + if (isObject$1(target[name]) && !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; + } +}; + +/** + * @alias module:zrender/Element + * @constructor + * @extends {module:zrender/mixin/Animatable} + * @extends {module:zrender/mixin/Transformable} + * @extends {module:zrender/mixin/Eventful} + */ +var Element = function (opts) { // jshint ignore:line + + 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, + + /** + * 是否是 Group + * @type {boolean} + */ + isGroup: false, + + /** + * 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 (isObject$1(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); + } + } +}; + +mixin(Element, Animatable); +mixin(Element, Transformable); +mixin(Element, Eventful); + +/** + * @module echarts/core/BoundingRect + */ + +var v2ApplyTransform = applyTransform; +var mathMin = Math.min; +var mathMax = Math.max; + +/** + * @alias module:echarts/core/BoundingRect + */ +function BoundingRect(x, y, width, height) { + + if (width < 0) { + x = x + width; + width = -width; + } + if (height < 0) { + y = y + height; + height = -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 lt = []; + var rb = []; + var lb = []; + var rt = []; + return function (m) { + // In case usage like this + // el.getBoundingRect().applyTransform(el.transform) + // And element has no transform + if (!m) { + return; + } + lt[0] = lb[0] = this.x; + lt[1] = rt[1] = this.y; + rb[0] = rt[0] = this.x + this.width; + rb[1] = lb[1] = this.y + this.height; + + v2ApplyTransform(lt, lt, m); + v2ApplyTransform(rb, rb, m); + v2ApplyTransform(lb, lb, m); + v2ApplyTransform(rt, rt, m); + + this.x = mathMin(lt[0], rb[0], lb[0], rt[0]); + this.y = mathMin(lt[1], rb[1], lb[1], rt[1]); + var maxX = mathMax(lt[0], rb[0], lb[0], rt[0]); + var maxY = mathMax(lt[1], rb[1], lb[1], rt[1]); + this.width = maxX - this.x; + this.height = maxY - this.y; + }; + })(), + + /** + * 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 = create$1(); + + // 矩阵右乘 + translate(m, m, [-a.x, -a.y]); + scale$1(m, m, [sx, sy]); + translate(m, m, [b.x, b.y]); + + return m; + }, + + /** + * @param {(module:echarts/core/BoundingRect|Object)} b + * @return {boolean} + */ + intersect: function (b) { + if (!b) { + return false; + } + + if (!(b instanceof BoundingRect)) { + // Normalize negative width/height. + b = BoundingRect.create(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; + }, + + plain: function () { + return { + x: this.x, + y: this.y, + width: this.width, + height: this.height + }; + } +}; + +/** + * @param {Object|module:zrender/core/BoundingRect} rect + * @param {number} rect.x + * @param {number} rect.y + * @param {number} rect.width + * @param {number} rect.height + * @return {module:zrender/core/BoundingRect} + */ +BoundingRect.create = function (rect) { + return new BoundingRect(rect.x, rect.y, rect.width, rect.height); +}; + +/** + * Group是一个容器,可以插入子节点,Group的变换也会被应用到子节点上 + * @module zrender/graphic/Group + * @example + * var Group = require('zrender/container/Group'); + * var Circle = require('zrender/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); + */ + +/** + * @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) { + if (opts.hasOwnProperty(key)) { + 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.addToStorage(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 = indexOf(children, child); + if (idx < 0) { + return this; + } + children.splice(idx, 1); + + child.parent = null; + + if (storage) { + + storage.delFromStorage(child); + + 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.delFromStorage(child); + 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.addToStorage(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.delFromStorage(child); + 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 + 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); + // TODO + // The boundingRect cacluated by transforming original + // rect may be bigger than the actual bundingRect when rotation + // is used. (Consider a circle rotated aginst its center, where + // the actual boundingRect should be the same as that not be + // rotated.) But we can not find better approach to calculate + // actual boundingRect yet, considering performance. + 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; + } +}; + +inherits(Group, Element); + +// https://github.com/mziccard/node-timsort +var DEFAULT_MIN_MERGE = 32; + +var DEFAULT_MIN_GALLOPING = 7; + +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 runStart; + var runLength; + var stackSize = 0; + + var tmp = []; + + 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(); +} + +// Use timsort because in most case elements are partially sorted +// https://jsfiddle.net/pissang/jr4x7mdm/8/ +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 () { // jshint ignore:line + 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; + + env$1.canvasSupported && sort(displayList, shapeCompareFunc); + }, + + _updateAndAddDisplayable: function (el, clipPaths, includeIgnore) { + + if (el.ignore && !includeIgnore) { + return; + } + + el.beforeUpdate(); + + if (el.__dirty) { + + el.update(); + + } + + el.afterUpdate(); + + var userSetClipPath = el.clipPath; + if (userSetClipPath) { + + // FIXME 效率影响 + if (clipPaths) { + clipPaths = clipPaths.slice(); + } + else { + clipPaths = []; + } + + var currentClipPath = userSetClipPath; + var parentClipPath = el; + // Recursively add clip path + while (currentClipPath) { + // clipPath 的变换是基于使用这个 clipPath 的元素 + currentClipPath.parent = parentClipPath; + currentClipPath.updateTransform(); + + clipPaths.push(currentClipPath); + + parentClipPath = currentClipPath; + currentClipPath = currentClipPath.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) { + if (el.__storage === this) { + return; + } + + if (el instanceof Group) { + el.addChildrenToStorage(this); + } + + this.addToStorage(el); + this._roots.push(el); + }, + + /** + * 删除指定的图形(Shape)或者组(Group) + * @param {string|Array.} [el] 如果为空清空整个Storage + */ + delRoot: function (el) { + if (el == null) { + // 不指定el清空 + for (var i = 0; i < this._roots.length; i++) { + var root = this._roots[i]; + if (root instanceof Group) { + root.delChildrenFromStorage(this); + } + } + + this._roots = []; + this._displayList = []; + this._displayListLen = 0; + + return; + } + + if (el instanceof Array) { + for (var i = 0, l = el.length; i < l; i++) { + this.delRoot(el[i]); + } + return; + } + + + var idx = indexOf(this._roots, el); + if (idx >= 0) { + this.delFromStorage(el); + this._roots.splice(idx, 1); + if (el instanceof Group) { + el.delChildrenFromStorage(this); + } + } + }, + + addToStorage: function (el) { + if (el) { + el.__storage = this; + el.dirty(false); + } + return this; + }, + + delFromStorage: function (el) { + if (el) { + el.__storage = null; + } + + return this; + }, + + /** + * 清空并且释放Storage + */ + dispose: function () { + this._renderList = + this._roots = null; + }, + + displayableSortFunc: shapeCompareFunc +}; + +var SHADOW_PROPS = { + 'shadowBlur': 1, + 'shadowOffsetX': 1, + 'shadowOffsetY': 1, + 'textShadowBlur': 1, + 'textShadowOffsetX': 1, + 'textShadowOffsetY': 1, + 'textBoxShadowBlur': 1, + 'textBoxShadowOffsetX': 1, + 'textBoxShadowOffsetY': 1 +}; + +var fixShadow = function (ctx, propName, value) { + if (SHADOW_PROPS.hasOwnProperty(propName)) { + return value *= ctx.dpr; + } + return value; +}; + +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, host) { + this.extendFrom(opts, false); + this.host = host; +}; + +function createLinearGradient(ctx, obj, rect) { + var x = obj.x == null ? 0 : obj.x; + var x2 = obj.x2 == null ? 1 : obj.x2; + var y = obj.y == null ? 0 : obj.y; + var y2 = obj.y2 == null ? 0 : 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; + } + + // Fix NaN when rect is Infinity + x = isNaN(x) ? 0 : x; + x2 = isNaN(x2) ? 1 : x2; + y = isNaN(y) ? 0 : y; + y2 = isNaN(y2) ? 0 : y2; + + 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 == null ? 0.5 : obj.x; + var y = obj.y == null ? 0.5 : obj.y; + var r = obj.r == null ? 0.5 : 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 {module:zrender/graphic/Displayable} + */ + host: null, + + /** + * @type {string} + */ + fill: '#000', + + /** + * @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, + + /** + * If `fontSize` or `fontFamily` exists, `font` will be reset by + * `fontSize`, `fontStyle`, `fontWeight`, `fontFamily`. + * So do not visit it directly in upper application (like echarts), + * but use `contain/text#makeFont` instead. + * @type {string} + */ + font: null, + + /** + * The same as font. Use font please. + * @deprecated + * @type {string} + */ + textFont: null, + + /** + * It helps merging respectively, rather than parsing an entire font string. + * @type {string} + */ + fontStyle: null, + + /** + * It helps merging respectively, rather than parsing an entire font string. + * @type {string} + */ + fontWeight: null, + + /** + * It helps merging respectively, rather than parsing an entire font string. + * Should be 12 but not '12px'. + * @type {number} + */ + fontSize: null, + + /** + * It helps merging respectively, rather than parsing an entire font string. + * @type {string} + */ + fontFamily: null, + + /** + * Reserved for special functinality, like 'hr'. + * @type {string} + */ + textTag: null, + + /** + * @type {string} + */ + textFill: '#000', + + /** + * @type {string} + */ + textStroke: null, + + /** + * @type {number} + */ + textWidth: null, + + /** + * Only for textBackground. + * @type {number} + */ + textHeight: null, + + /** + * textStroke may be set as some color as a default + * value in upper applicaion, where the default value + * of textStrokeWidth should be 0 to make sure that + * user can choose to do not use text stroke. + * @type {number} + */ + textStrokeWidth: 0, + + /** + * @type {number} + */ + textLineHeight: null, + + /** + * 'inside', 'left', 'right', 'top', 'bottom' + * [x, y] + * Based on x, y of rect. + * @type {string|Array.} + * @default 'inside' + */ + textPosition: 'inside', + + /** + * If not specified, use the boundingRect of a `displayable`. + * @type {Object} + */ + textRect: null, + + /** + * [x, y] + * @type {Array.} + */ + textOffset: null, + + /** + * @type {string} + */ + textAlign: null, + + /** + * @type {string} + */ + textVerticalAlign: null, + + /** + * @type {number} + */ + textDistance: 5, + + /** + * @type {string} + */ + textShadowColor: 'transparent', + + /** + * @type {number} + */ + textShadowBlur: 0, + + /** + * @type {number} + */ + textShadowOffsetX: 0, + + /** + * @type {number} + */ + textShadowOffsetY: 0, + + /** + * @type {string} + */ + textBoxShadowColor: 'transparent', + + /** + * @type {number} + */ + textBoxShadowBlur: 0, + + /** + * @type {number} + */ + textBoxShadowOffsetX: 0, + + /** + * @type {number} + */ + textBoxShadowOffsetY: 0, + + /** + * Whether transform text. + * Only useful in Path and Image element + * @type {boolean} + */ + transformText: false, + + /** + * Text rotate around position of Path or Image + * Only useful in Path and Image element and transformText is false. + */ + textRotation: 0, + + /** + * Text origin of text rotation, like [10, 40]. + * Based on x, y of rect. + * Useful in label rotation of circular symbol. + * By default, this origin is textPosition. + * Can be 'center'. + * @type {string|Array.} + */ + textOrigin: null, + + /** + * @type {string} + */ + textBackgroundColor: null, + + /** + * @type {string} + */ + textBorderColor: null, + + /** + * @type {number} + */ + textBorderWidth: 0, + + /** + * @type {number} + */ + textBorderRadius: 0, + + /** + * Can be `2` or `[2, 4]` or `[2, 3, 4, 5]` + * @type {number|Array.} + */ + textPadding: null, + + /** + * Text styles for rich text. + * @type {Object} + */ + rich: null, + + /** + * {outerWidth, outerHeight, ellipsis, placeholder} + * @type {Object} + */ + truncate: null, + + /** + * https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation + * @type {string} + */ + 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] = + fixShadow(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 true: overwrirte any way. + * false: overwrite only when !target.hasOwnProperty + * others: overwrite when property is not null/undefined. + */ + extendFrom: function (otherStyle, overwrite) { + if (otherStyle) { + for (var name in otherStyle) { + if (otherStyle.hasOwnProperty(name) + && (overwrite === true + || ( + overwrite === false + ? !this.hasOwnProperty(name) + : otherStyle[name] != null + ) + ) + ) { + this[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; + +var Pattern = function (image, repeat) { + // Should do nothing more in this constructor. Because gradient can be + // declard by `color: {image: ...}`, where this constructor will not be called. + + this.image = image; + this.repeat = repeat; + + // Can be cloned + this.type = 'pattern'; +}; + +Pattern.prototype.getCanvasPattern = function (ctx) { + return ctx.createPattern(this.image, this.repeat || 'repeat'); +}; + +/** + * @module zrender/Layer + * @author pissang(https://www.github.com/pissang) + */ + +function returnFalse() { + return false; +} + +/** + * 创建dom + * + * @inner + * @param {string} id dom id 待用 + * @param {Painter} painter painter instance + * @param {number} number + */ +function createDom(id, painter, dpr) { + var newDom = createCanvas(); + var width = painter.getWidth(); + var height = painter.getHeight(); + + var newDomStyle = newDom.style; + if (newDomStyle) { // In node or some other non-browser environment + newDomStyle.position = 'absolute'; + newDomStyle.left = 0; + newDomStyle.top = 0; + newDomStyle.width = width + 'px'; + newDomStyle.height = height + 'px'; + + newDom.setAttribute('data-zr-dom-id', id); + } + + newDom.width = width * dpr; + newDom.height = height * dpr; + + 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 || devicePixelRatio; + if (typeof id === 'string') { + dom = createDom(id, painter, dpr); + } + // Not using isDom because in node it will return false + else if (isObject$1(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)'; + domStyle['padding'] = 0; + domStyle['margin'] = 0; + domStyle['border-width'] = 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, + + __dirty: true, + + __used: false, + + __drawIndex: 0, + __startIndex: 0, + __endIndex: 0, + + incremental: false, + + getElementCount: function () { + return this.__endIndex - this.__startIndex; + }, + + 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, 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; + + if (domStyle) { + 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]=false Clear all with out motion blur + * @param {Color} [clearColor] + */ + clear: function (clearAll, clearColor) { + var dom = this.dom; + var ctx = this.ctx; + var width = dom.width; + var height = dom.height; + + var clearColor = 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 && clearColor !== 'transparent') { + 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(); + } + } +}; + +var requestAnimationFrame = ( + typeof window !== 'undefined' + && ( + (window.requestAnimationFrame && window.requestAnimationFrame.bind(window)) + // https://github.com/ecomfe/zrender/issues/189#issuecomment-224919809 + || (window.msRequestAnimationFrame && window.msRequestAnimationFrame.bind(window)) + || window.mozRequestAnimationFrame + || window.webkitRequestAnimationFrame + ) +) || function (func) { + setTimeout(func, 16); +}; + +var globalImageCache = new LRU(50); + +/** + * @param {string|HTMLImageElement|HTMLCanvasElement|Canvas} newImageOrSrc + * @return {HTMLImageElement|HTMLCanvasElement|Canvas} image + */ +function findExistImage(newImageOrSrc) { + if (typeof newImageOrSrc === 'string') { + var cachedImgObj = globalImageCache.get(newImageOrSrc); + return cachedImgObj && cachedImgObj.image; + } + else { + return newImageOrSrc; + } +} + +/** + * Caution: User should cache loaded images, but not just count on LRU. + * Consider if required images more than LRU size, will dead loop occur? + * + * @param {string|HTMLImageElement|HTMLCanvasElement|Canvas} newImageOrSrc + * @param {HTMLImageElement|HTMLCanvasElement|Canvas} image Existent image. + * @param {module:zrender/Element} [hostEl] For calling `dirty`. + * @param {Function} [cb] params: (image, cbPayload) + * @param {Object} [cbPayload] Payload on cb calling. + * @return {HTMLImageElement|HTMLCanvasElement|Canvas} image + */ +function createOrUpdateImage(newImageOrSrc, image, hostEl, cb, cbPayload) { + if (!newImageOrSrc) { + return image; + } + else if (typeof newImageOrSrc === 'string') { + + // Image should not be loaded repeatly. + if ((image && image.__zrImageSrc === newImageOrSrc) || !hostEl) { + return image; + } + + // Only when there is no existent image or existent image src + // is different, this method is responsible for load. + var cachedImgObj = globalImageCache.get(newImageOrSrc); + + var pendingWrap = {hostEl: hostEl, cb: cb, cbPayload: cbPayload}; + + if (cachedImgObj) { + image = cachedImgObj.image; + !isImageReady(image) && cachedImgObj.pending.push(pendingWrap); + } + else { + !image && (image = new Image()); + image.onload = imageOnLoad; + + globalImageCache.put( + newImageOrSrc, + image.__cachedImgObj = { + image: image, + pending: [pendingWrap] + } + ); + + image.src = image.__zrImageSrc = newImageOrSrc; + } + + return image; + } + // newImageOrSrc is an HTMLImageElement or HTMLCanvasElement or Canvas + else { + return newImageOrSrc; + } +} + +function imageOnLoad() { + var cachedImgObj = this.__cachedImgObj; + this.onload = this.__cachedImgObj = null; + + for (var i = 0; i < cachedImgObj.pending.length; i++) { + var pendingWrap = cachedImgObj.pending[i]; + var cb = pendingWrap.cb; + cb && cb(this, pendingWrap.cbPayload); + pendingWrap.hostEl.dirty(); + } + cachedImgObj.pending.length = 0; +} + +function isImageReady(image) { + return image && image.width && image.height; +} + +var textWidthCache = {}; +var textWidthCacheCounter = 0; + +var TEXT_CACHE_MAX = 5000; +var STYLE_REG = /\{([a-zA-Z0-9_]+)\|([^}]*)\}/g; + +var DEFAULT_FONT = '12px sans-serif'; + +// Avoid assign to an exported variable, for transforming to cjs. +var methods$1 = {}; + +function $override$1(name, fn) { + methods$1[name] = fn; +} + +/** + * @public + * @param {string} text + * @param {string} font + * @return {number} width + */ +function getWidth(text, font) { + font = font || DEFAULT_FONT; + var key = text + ':' + font; + if (textWidthCache[key]) { + return textWidthCache[key]; + } + + var textLines = (text + '').split('\n'); + var width = 0; + + for (var i = 0, l = textLines.length; i < l; i++) { + // textContain.measureText may be overrided in SVG or VML + width = Math.max(measureText(textLines[i], font).width, width); + } + + if (textWidthCacheCounter > TEXT_CACHE_MAX) { + textWidthCacheCounter = 0; + textWidthCache = {}; + } + textWidthCacheCounter++; + textWidthCache[key] = width; + + return width; +} + +/** + * @public + * @param {string} text + * @param {string} font + * @param {string} [textAlign='left'] + * @param {string} [textVerticalAlign='top'] + * @param {Array.} [textPadding] + * @param {Object} [rich] + * @param {Object} [truncate] + * @return {Object} {x, y, width, height, lineHeight} + */ +function getBoundingRect(text, font, textAlign, textVerticalAlign, textPadding, rich, truncate) { + return rich + ? getRichTextRect(text, font, textAlign, textVerticalAlign, textPadding, rich, truncate) + : getPlainTextRect(text, font, textAlign, textVerticalAlign, textPadding, truncate); +} + +function getPlainTextRect(text, font, textAlign, textVerticalAlign, textPadding, truncate) { + var contentBlock = parsePlainText(text, font, textPadding, truncate); + var outerWidth = getWidth(text, font); + if (textPadding) { + outerWidth += textPadding[1] + textPadding[3]; + } + var outerHeight = contentBlock.outerHeight; + + var x = adjustTextX(0, outerWidth, textAlign); + var y = adjustTextY(0, outerHeight, textVerticalAlign); + + var rect = new BoundingRect(x, y, outerWidth, outerHeight); + rect.lineHeight = contentBlock.lineHeight; + + return rect; +} + +function getRichTextRect(text, font, textAlign, textVerticalAlign, textPadding, rich, truncate) { + var contentBlock = parseRichText(text, { + rich: rich, + truncate: truncate, + font: font, + textAlign: textAlign, + textPadding: textPadding + }); + var outerWidth = contentBlock.outerWidth; + var outerHeight = contentBlock.outerHeight; + + var x = adjustTextX(0, outerWidth, textAlign); + var y = adjustTextY(0, outerHeight, textVerticalAlign); + + return new BoundingRect(x, y, outerWidth, outerHeight); +} + +/** + * @public + * @param {number} x + * @param {number} width + * @param {string} [textAlign='left'] + * @return {number} Adjusted x. + */ +function adjustTextX(x, width, textAlign) { + // FIXME Right to left language + if (textAlign === 'right') { + x -= width; + } + else if (textAlign === 'center') { + x -= width / 2; + } + return x; +} + +/** + * @public + * @param {number} y + * @param {number} height + * @param {string} [textVerticalAlign='top'] + * @return {number} Adjusted y. + */ +function adjustTextY(y, height, textVerticalAlign) { + if (textVerticalAlign === 'middle') { + y -= height / 2; + } + else if (textVerticalAlign === 'bottom') { + y -= height; + } + return y; +} + +/** + * @public + * @param {stirng} textPosition + * @param {Object} rect {x, y, width, height} + * @param {number} distance + * @return {Object} {x, y, textAlign, textVerticalAlign} + */ +function adjustTextPositionOnRect(textPosition, rect, distance) { + + var x = rect.x; + var y = rect.y; + + var height = rect.height; + var width = rect.width; + var halfHeight = height / 2; + + var textAlign = 'left'; + var textVerticalAlign = 'top'; + + switch (textPosition) { + case 'left': + x -= distance; + y += halfHeight; + textAlign = 'right'; + textVerticalAlign = 'middle'; + break; + case 'right': + x += distance + width; + y += halfHeight; + textVerticalAlign = 'middle'; + break; + case 'top': + x += width / 2; + y -= distance; + textAlign = 'center'; + textVerticalAlign = 'bottom'; + break; + case 'bottom': + x += width / 2; + y += height + distance; + textAlign = 'center'; + break; + case 'inside': + x += width / 2; + y += halfHeight; + textAlign = 'center'; + textVerticalAlign = 'middle'; + break; + case 'insideLeft': + x += distance; + y += halfHeight; + textVerticalAlign = 'middle'; + break; + case 'insideRight': + x += width - distance; + y += halfHeight; + textAlign = 'right'; + textVerticalAlign = 'middle'; + break; + case 'insideTop': + x += width / 2; + y += distance; + textAlign = 'center'; + break; + case 'insideBottom': + x += width / 2; + y += height - distance; + textAlign = 'center'; + textVerticalAlign = 'bottom'; + break; + case 'insideTopLeft': + x += distance; + y += distance; + break; + case 'insideTopRight': + x += width - distance; + y += distance; + textAlign = 'right'; + break; + case 'insideBottomLeft': + x += distance; + y += height - distance; + textVerticalAlign = 'bottom'; + break; + case 'insideBottomRight': + x += width - distance; + y += height - distance; + textAlign = 'right'; + textVerticalAlign = 'bottom'; + break; + } + + return { + x: x, + y: y, + textAlign: textAlign, + textVerticalAlign: textVerticalAlign + }; +} + +/** + * Show ellipsis if overflow. + * + * @public + * @param {string} text + * @param {string} containerWidth + * @param {string} font + * @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, font, ellipsis, options) { + if (!containerWidth) { + return ''; + } + + var textLines = (text + '').split('\n'); + options = prepareTruncateOptions(containerWidth, font, ellipsis, options); + + // FIXME + // It is not appropriate that every line has '...' when truncate multiple lines. + for (var i = 0, len = textLines.length; i < len; i++) { + textLines[i] = truncateSingleLine(textLines[i], options); + } + + return textLines.join('\n'); +} + +function prepareTruncateOptions(containerWidth, font, ellipsis, options) { + options = extend({}, options); + + options.font = font; + var ellipsis = retrieve2(ellipsis, '...'); + options.maxIterations = retrieve2(options.maxIterations, 2); + var minChar = options.minChar = retrieve2(options.minChar, 0); + // FIXME + // Other languages? + options.cnCharWidth = getWidth('国', font); + // FIXME + // Consider proportional font? + var ascCharWidth = options.ascCharWidth = getWidth('a', font); + options.placeholder = retrieve2(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 = getWidth(ellipsis); + if (ellipsisWidth > contentWidth) { + ellipsis = ''; + ellipsisWidth = 0; + } + + contentWidth = containerWidth - ellipsisWidth; + + options.ellipsis = ellipsis; + options.ellipsisWidth = ellipsisWidth; + options.contentWidth = contentWidth; + options.containerWidth = containerWidth; + + return options; +} + +function truncateSingleLine(textLine, options) { + var containerWidth = options.containerWidth; + var font = options.font; + var contentWidth = options.contentWidth; + + if (!containerWidth) { + return ''; + } + + var lineWidth = getWidth(textLine, font); + + if (lineWidth <= containerWidth) { + return textLine; + } + + for (var j = 0;; j++) { + if (lineWidth <= contentWidth || j >= options.maxIterations) { + textLine += options.ellipsis; + break; + } + + var subLength = j === 0 + ? estimateLength(textLine, contentWidth, options.ascCharWidth, options.cnCharWidth) + : lineWidth > 0 + ? Math.floor(textLine.length * contentWidth / lineWidth) + : 0; + + textLine = textLine.substr(0, subLength); + lineWidth = getWidth(textLine, font); + } + + if (textLine === '') { + textLine = options.placeholder; + } + + return textLine; +} + +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; +} + +/** + * @public + * @param {string} font + * @return {number} line height + */ +function getLineHeight(font) { + // FIXME A rough approach. + return getWidth('国', font); +} + +/** + * @public + * @param {string} text + * @param {string} font + * @return {Object} width + */ +function measureText(text, font) { + return methods$1.measureText(text, font); +} + +// Avoid assign to an exported variable, for transforming to cjs. +methods$1.measureText = function (text, font) { + var ctx = getContext(); + ctx.font = font || DEFAULT_FONT; + return ctx.measureText(text); +}; + +/** + * @public + * @param {string} text + * @param {string} font + * @param {Object} [truncate] + * @return {Object} block: {lineHeight, lines, height, outerHeight} + * Notice: for performance, do not calculate outerWidth util needed. + */ +function parsePlainText(text, font, padding, truncate) { + text != null && (text += ''); + + var lineHeight = getLineHeight(font); + var lines = text ? text.split('\n') : []; + var height = lines.length * lineHeight; + var outerHeight = height; + + if (padding) { + outerHeight += padding[0] + padding[2]; + } + + if (text && truncate) { + var truncOuterHeight = truncate.outerHeight; + var truncOuterWidth = truncate.outerWidth; + if (truncOuterHeight != null && outerHeight > truncOuterHeight) { + text = ''; + lines = []; + } + else if (truncOuterWidth != null) { + var options = prepareTruncateOptions( + truncOuterWidth - (padding ? padding[1] + padding[3] : 0), + font, + truncate.ellipsis, + {minChar: truncate.minChar, placeholder: truncate.placeholder} + ); + + // FIXME + // It is not appropriate that every line has '...' when truncate multiple lines. + for (var i = 0, len = lines.length; i < len; i++) { + lines[i] = truncateSingleLine(lines[i], options); + } + } + } + + return { + lines: lines, + height: height, + outerHeight: outerHeight, + lineHeight: lineHeight + }; +} + +/** + * For example: 'some text {a|some text}other text{b|some text}xxx{c|}xxx' + * Also consider 'bbbb{a|xxx\nzzz}xxxx\naaaa'. + * + * @public + * @param {string} text + * @param {Object} style + * @return {Object} block + * { + * width, + * height, + * lines: [{ + * lineHeight, + * width, + * tokens: [[{ + * styleName, + * text, + * width, // include textPadding + * height, // include textPadding + * textWidth, // pure text width + * textHeight, // pure text height + * lineHeihgt, + * font, + * textAlign, + * textVerticalAlign + * }], [...], ...] + * }, ...] + * } + * If styleName is undefined, it is plain text. + */ +function parseRichText(text, style) { + var contentBlock = {lines: [], width: 0, height: 0}; + + text != null && (text += ''); + if (!text) { + return contentBlock; + } + + var lastIndex = STYLE_REG.lastIndex = 0; + var result; + while ((result = STYLE_REG.exec(text)) != null) { + var matchedIndex = result.index; + if (matchedIndex > lastIndex) { + pushTokens(contentBlock, text.substring(lastIndex, matchedIndex)); + } + pushTokens(contentBlock, result[2], result[1]); + lastIndex = STYLE_REG.lastIndex; + } + + if (lastIndex < text.length) { + pushTokens(contentBlock, text.substring(lastIndex, text.length)); + } + + var lines = contentBlock.lines; + var contentHeight = 0; + var contentWidth = 0; + // For `textWidth: 100%` + var pendingList = []; + + var stlPadding = style.textPadding; + + var truncate = style.truncate; + var truncateWidth = truncate && truncate.outerWidth; + var truncateHeight = truncate && truncate.outerHeight; + if (stlPadding) { + truncateWidth != null && (truncateWidth -= stlPadding[1] + stlPadding[3]); + truncateHeight != null && (truncateHeight -= stlPadding[0] + stlPadding[2]); + } + + // Calculate layout info of tokens. + for (var i = 0; i < lines.length; i++) { + var line = lines[i]; + var lineHeight = 0; + var lineWidth = 0; + + for (var j = 0; j < line.tokens.length; j++) { + var token = line.tokens[j]; + var tokenStyle = token.styleName && style.rich[token.styleName] || {}; + // textPadding should not inherit from style. + var textPadding = token.textPadding = tokenStyle.textPadding; + + // textFont has been asigned to font by `normalizeStyle`. + var font = token.font = tokenStyle.font || style.font; + + // textHeight can be used when textVerticalAlign is specified in token. + var tokenHeight = token.textHeight = retrieve2( + // textHeight should not be inherited, consider it can be specified + // as box height of the block. + tokenStyle.textHeight, getLineHeight(font) + ); + textPadding && (tokenHeight += textPadding[0] + textPadding[2]); + token.height = tokenHeight; + token.lineHeight = retrieve3( + tokenStyle.textLineHeight, style.textLineHeight, tokenHeight + ); + + token.textAlign = tokenStyle && tokenStyle.textAlign || style.textAlign; + token.textVerticalAlign = tokenStyle && tokenStyle.textVerticalAlign || 'middle'; + + if (truncateHeight != null && contentHeight + token.lineHeight > truncateHeight) { + return {lines: [], width: 0, height: 0}; + } + + token.textWidth = getWidth(token.text, font); + var tokenWidth = tokenStyle.textWidth; + var tokenWidthNotSpecified = tokenWidth == null || tokenWidth === 'auto'; + + // Percent width, can be `100%`, can be used in drawing separate + // line when box width is needed to be auto. + if (typeof tokenWidth === 'string' && tokenWidth.charAt(tokenWidth.length - 1) === '%') { + token.percentWidth = tokenWidth; + pendingList.push(token); + tokenWidth = 0; + // Do not truncate in this case, because there is no user case + // and it is too complicated. + } + else { + if (tokenWidthNotSpecified) { + tokenWidth = token.textWidth; + + // FIXME: If image is not loaded and textWidth is not specified, calling + // `getBoundingRect()` will not get correct result. + var textBackgroundColor = tokenStyle.textBackgroundColor; + var bgImg = textBackgroundColor && textBackgroundColor.image; + + // Use cases: + // (1) If image is not loaded, it will be loaded at render phase and call + // `dirty()` and `textBackgroundColor.image` will be replaced with the loaded + // image, and then the right size will be calculated here at the next tick. + // See `graphic/helper/text.js`. + // (2) If image loaded, and `textBackgroundColor.image` is image src string, + // use `imageHelper.findExistImage` to find cached image. + // `imageHelper.findExistImage` will always be called here before + // `imageHelper.createOrUpdateImage` in `graphic/helper/text.js#renderRichText` + // which ensures that image will not be rendered before correct size calcualted. + if (bgImg) { + bgImg = findExistImage(bgImg); + if (isImageReady(bgImg)) { + tokenWidth = Math.max(tokenWidth, bgImg.width * tokenHeight / bgImg.height); + } + } + } + + var paddingW = textPadding ? textPadding[1] + textPadding[3] : 0; + tokenWidth += paddingW; + + var remianTruncWidth = truncateWidth != null ? truncateWidth - lineWidth : null; + + if (remianTruncWidth != null && remianTruncWidth < tokenWidth) { + if (!tokenWidthNotSpecified || remianTruncWidth < paddingW) { + token.text = ''; + token.textWidth = tokenWidth = 0; + } + else { + token.text = truncateText( + token.text, remianTruncWidth - paddingW, font, truncate.ellipsis, + {minChar: truncate.minChar} + ); + token.textWidth = getWidth(token.text, font); + tokenWidth = token.textWidth + paddingW; + } + } + } + + lineWidth += (token.width = tokenWidth); + tokenStyle && (lineHeight = Math.max(lineHeight, token.lineHeight)); + } + + line.width = lineWidth; + line.lineHeight = lineHeight; + contentHeight += lineHeight; + contentWidth = Math.max(contentWidth, lineWidth); + } + + contentBlock.outerWidth = contentBlock.width = retrieve2(style.textWidth, contentWidth); + contentBlock.outerHeight = contentBlock.height = retrieve2(style.textHeight, contentHeight); + + if (stlPadding) { + contentBlock.outerWidth += stlPadding[1] + stlPadding[3]; + contentBlock.outerHeight += stlPadding[0] + stlPadding[2]; + } + + for (var i = 0; i < pendingList.length; i++) { + var token = pendingList[i]; + var percentWidth = token.percentWidth; + // Should not base on outerWidth, because token can not be placed out of padding. + token.width = parseInt(percentWidth, 10) / 100 * contentWidth; + } + + return contentBlock; +} + +function pushTokens(block, str, styleName) { + var isEmptyStr = str === ''; + var strs = str.split('\n'); + var lines = block.lines; + + for (var i = 0; i < strs.length; i++) { + var text = strs[i]; + var token = { + styleName: styleName, + text: text, + isLineHolder: !text && !isEmptyStr + }; + + // The first token should be appended to the last line. + if (!i) { + var tokens = (lines[lines.length - 1] || (lines[0] = {tokens: []})).tokens; + + // Consider cases: + // (1) ''.split('\n') => ['', '\n', ''], the '' at the first item + // (which is a placeholder) should be replaced by new token. + // (2) A image backage, where token likes {a|}. + // (3) A redundant '' will affect textAlign in line. + // (4) tokens with the same tplName should not be merged, because + // they should be displayed in different box (with border and padding). + var tokensLen = tokens.length; + (tokensLen === 1 && tokens[0].isLineHolder) + ? (tokens[0] = token) + // Consider text is '', only insert when it is the "lineHolder" or + // "emptyStr". Otherwise a redundant '' will affect textAlign in line. + : ((text || !tokensLen || isEmptyStr) && tokens.push(token)); + } + // Other tokens always start a new line. + else { + // If there is '', insert it as a placeholder. + lines.push({tokens: [token]}); + } + } +} + +function makeFont(style) { + // FIXME in node-canvas fontWeight is before fontStyle + // Use `fontSize` `fontFamily` to check whether font properties are defined. + var font = (style.fontSize || style.fontFamily) && [ + style.fontStyle, + style.fontWeight, + (style.fontSize || 12) + 'px', + // If font properties are defined, `fontFamily` should not be ignored. + style.fontFamily || 'sans-serif' + ].join(' '); + return font && trim(font) || style.textFont || style.font; +} + +function buildPath(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.arc(x + width - r2, y + r2, r2, -Math.PI / 2, 0); + ctx.lineTo(x + width, y + height - r3); + r3 !== 0 && ctx.arc(x + width - r3, y + height - r3, r3, 0, Math.PI / 2); + ctx.lineTo(x + r4, y + height); + r4 !== 0 && ctx.arc(x + r4, y + height - r4, r4, Math.PI / 2, Math.PI); + ctx.lineTo(x, y + r1); + r1 !== 0 && ctx.arc(x + r1, y + r1, r1, Math.PI, Math.PI * 1.5); +} + +// TODO: Have not support 'start', 'end' yet. +var VALID_TEXT_ALIGN = {left: 1, right: 1, center: 1}; +var VALID_TEXT_VERTICAL_ALIGN = {top: 1, bottom: 1, middle: 1}; + +/** + * @param {module:zrender/graphic/Style} style + * @return {module:zrender/graphic/Style} The input style. + */ +function normalizeTextStyle(style) { + normalizeStyle(style); + each$1(style.rich, normalizeStyle); + return style; +} + +function normalizeStyle(style) { + if (style) { + + style.font = makeFont(style); + + var textAlign = style.textAlign; + textAlign === 'middle' && (textAlign = 'center'); + style.textAlign = ( + textAlign == null || VALID_TEXT_ALIGN[textAlign] + ) ? textAlign : 'left'; + + // Compatible with textBaseline. + var textVerticalAlign = style.textVerticalAlign || style.textBaseline; + textVerticalAlign === 'center' && (textVerticalAlign = 'middle'); + style.textVerticalAlign = ( + textVerticalAlign == null || VALID_TEXT_VERTICAL_ALIGN[textVerticalAlign] + ) ? textVerticalAlign : 'top'; + + var textPadding = style.textPadding; + if (textPadding) { + style.textPadding = normalizeCssArray(style.textPadding); + } + } +} + +/** + * @param {CanvasRenderingContext2D} ctx + * @param {string} text + * @param {module:zrender/graphic/Style} style + * @param {Object|boolean} [rect] {x, y, width, height} + * If set false, rect text is not used. + */ +function renderText(hostEl, ctx, text, style, rect) { + style.rich + ? renderRichText(hostEl, ctx, text, style, rect) + : renderPlainText(hostEl, ctx, text, style, rect); +} + +function renderPlainText(hostEl, ctx, text, style, rect) { + var font = setCtx(ctx, 'font', style.font || DEFAULT_FONT); + + var textPadding = style.textPadding; + + var contentBlock = hostEl.__textCotentBlock; + if (!contentBlock || hostEl.__dirty) { + contentBlock = hostEl.__textCotentBlock = parsePlainText( + text, font, textPadding, style.truncate + ); + } + + var outerHeight = contentBlock.outerHeight; + + var textLines = contentBlock.lines; + var lineHeight = contentBlock.lineHeight; + + var boxPos = getBoxPosition(outerHeight, style, rect); + var baseX = boxPos.baseX; + var baseY = boxPos.baseY; + var textAlign = boxPos.textAlign; + var textVerticalAlign = boxPos.textVerticalAlign; + + // Origin of textRotation should be the base point of text drawing. + applyTextRotation(ctx, style, rect, baseX, baseY); + + var boxY = adjustTextY(baseY, outerHeight, textVerticalAlign); + var textX = baseX; + var textY = boxY; + + var needDrawBg = needDrawBackground(style); + if (needDrawBg || textPadding) { + // Consider performance, do not call getTextWidth util necessary. + var textWidth = getWidth(text, font); + var outerWidth = textWidth; + textPadding && (outerWidth += textPadding[1] + textPadding[3]); + var boxX = adjustTextX(baseX, outerWidth, textAlign); + + needDrawBg && drawBackground(hostEl, ctx, style, boxX, boxY, outerWidth, outerHeight); + + if (textPadding) { + textX = getTextXForPadding(baseX, textAlign, textPadding); + textY += textPadding[0]; + } + } + + setCtx(ctx, 'textAlign', textAlign || 'left'); + // Force baseline to be "middle". Otherwise, if using "top", the + // text will offset downward a little bit in font "Microsoft YaHei". + setCtx(ctx, 'textBaseline', 'middle'); + + // Always set shadowBlur and shadowOffset to avoid leak from displayable. + setCtx(ctx, 'shadowBlur', style.textShadowBlur || 0); + setCtx(ctx, 'shadowColor', style.textShadowColor || 'transparent'); + setCtx(ctx, 'shadowOffsetX', style.textShadowOffsetX || 0); + setCtx(ctx, 'shadowOffsetY', style.textShadowOffsetY || 0); + + // `textBaseline` is set as 'middle'. + textY += lineHeight / 2; + + var textStrokeWidth = style.textStrokeWidth; + var textStroke = getStroke(style.textStroke, textStrokeWidth); + var textFill = getFill(style.textFill); + + if (textStroke) { + setCtx(ctx, 'lineWidth', textStrokeWidth); + setCtx(ctx, 'strokeStyle', textStroke); + } + if (textFill) { + setCtx(ctx, 'fillStyle', textFill); + } + + for (var i = 0; i < textLines.length; i++) { + // Fill after stroke so the outline will not cover the main part. + textStroke && ctx.strokeText(textLines[i], textX, textY); + textFill && ctx.fillText(textLines[i], textX, textY); + textY += lineHeight; + } +} + +function renderRichText(hostEl, ctx, text, style, rect) { + var contentBlock = hostEl.__textCotentBlock; + + if (!contentBlock || hostEl.__dirty) { + contentBlock = hostEl.__textCotentBlock = parseRichText(text, style); + } + + drawRichText(hostEl, ctx, contentBlock, style, rect); +} + +function drawRichText(hostEl, ctx, contentBlock, style, rect) { + var contentWidth = contentBlock.width; + var outerWidth = contentBlock.outerWidth; + var outerHeight = contentBlock.outerHeight; + var textPadding = style.textPadding; + + var boxPos = getBoxPosition(outerHeight, style, rect); + var baseX = boxPos.baseX; + var baseY = boxPos.baseY; + var textAlign = boxPos.textAlign; + var textVerticalAlign = boxPos.textVerticalAlign; + + // Origin of textRotation should be the base point of text drawing. + applyTextRotation(ctx, style, rect, baseX, baseY); + + var boxX = adjustTextX(baseX, outerWidth, textAlign); + var boxY = adjustTextY(baseY, outerHeight, textVerticalAlign); + var xLeft = boxX; + var lineTop = boxY; + if (textPadding) { + xLeft += textPadding[3]; + lineTop += textPadding[0]; + } + var xRight = xLeft + contentWidth; + + needDrawBackground(style) && drawBackground( + hostEl, ctx, style, boxX, boxY, outerWidth, outerHeight + ); + + for (var i = 0; i < contentBlock.lines.length; i++) { + var line = contentBlock.lines[i]; + var tokens = line.tokens; + var tokenCount = tokens.length; + var lineHeight = line.lineHeight; + var usedWidth = line.width; + + var leftIndex = 0; + var lineXLeft = xLeft; + var lineXRight = xRight; + var rightIndex = tokenCount - 1; + var token; + + while ( + leftIndex < tokenCount + && (token = tokens[leftIndex], !token.textAlign || token.textAlign === 'left') + ) { + placeToken(hostEl, ctx, token, style, lineHeight, lineTop, lineXLeft, 'left'); + usedWidth -= token.width; + lineXLeft += token.width; + leftIndex++; + } + + while ( + rightIndex >= 0 + && (token = tokens[rightIndex], token.textAlign === 'right') + ) { + placeToken(hostEl, ctx, token, style, lineHeight, lineTop, lineXRight, 'right'); + usedWidth -= token.width; + lineXRight -= token.width; + rightIndex--; + } + + // The other tokens are placed as textAlign 'center' if there is enough space. + lineXLeft += (contentWidth - (lineXLeft - xLeft) - (xRight - lineXRight) - usedWidth) / 2; + while (leftIndex <= rightIndex) { + token = tokens[leftIndex]; + // Consider width specified by user, use 'center' rather than 'left'. + placeToken(hostEl, ctx, token, style, lineHeight, lineTop, lineXLeft + token.width / 2, 'center'); + lineXLeft += token.width; + leftIndex++; + } + + lineTop += lineHeight; + } +} + +function applyTextRotation(ctx, style, rect, x, y) { + // textRotation only apply in RectText. + if (rect && style.textRotation) { + var origin = style.textOrigin; + if (origin === 'center') { + x = rect.width / 2 + rect.x; + y = rect.height / 2 + rect.y; + } + else if (origin) { + x = origin[0] + rect.x; + y = origin[1] + rect.y; + } + + ctx.translate(x, y); + // Positive: anticlockwise + ctx.rotate(-style.textRotation); + ctx.translate(-x, -y); + } +} + +function placeToken(hostEl, ctx, token, style, lineHeight, lineTop, x, textAlign) { + var tokenStyle = style.rich[token.styleName] || {}; + + // 'ctx.textBaseline' is always set as 'middle', for sake of + // the bias of "Microsoft YaHei". + var textVerticalAlign = token.textVerticalAlign; + var y = lineTop + lineHeight / 2; + if (textVerticalAlign === 'top') { + y = lineTop + token.height / 2; + } + else if (textVerticalAlign === 'bottom') { + y = lineTop + lineHeight - token.height / 2; + } + + !token.isLineHolder && needDrawBackground(tokenStyle) && drawBackground( + hostEl, + ctx, + tokenStyle, + textAlign === 'right' + ? x - token.width + : textAlign === 'center' + ? x - token.width / 2 + : x, + y - token.height / 2, + token.width, + token.height + ); + + var textPadding = token.textPadding; + if (textPadding) { + x = getTextXForPadding(x, textAlign, textPadding); + y -= token.height / 2 - textPadding[2] - token.textHeight / 2; + } + + setCtx(ctx, 'shadowBlur', retrieve3(tokenStyle.textShadowBlur, style.textShadowBlur, 0)); + setCtx(ctx, 'shadowColor', tokenStyle.textShadowColor || style.textShadowColor || 'transparent'); + setCtx(ctx, 'shadowOffsetX', retrieve3(tokenStyle.textShadowOffsetX, style.textShadowOffsetX, 0)); + setCtx(ctx, 'shadowOffsetY', retrieve3(tokenStyle.textShadowOffsetY, style.textShadowOffsetY, 0)); + + setCtx(ctx, 'textAlign', textAlign); + // Force baseline to be "middle". Otherwise, if using "top", the + // text will offset downward a little bit in font "Microsoft YaHei". + setCtx(ctx, 'textBaseline', 'middle'); + + setCtx(ctx, 'font', token.font || DEFAULT_FONT); + + var textStroke = getStroke(tokenStyle.textStroke || style.textStroke, textStrokeWidth); + var textFill = getFill(tokenStyle.textFill || style.textFill); + var textStrokeWidth = retrieve2(tokenStyle.textStrokeWidth, style.textStrokeWidth); + + // Fill after stroke so the outline will not cover the main part. + if (textStroke) { + setCtx(ctx, 'lineWidth', textStrokeWidth); + setCtx(ctx, 'strokeStyle', textStroke); + ctx.strokeText(token.text, x, y); + } + if (textFill) { + setCtx(ctx, 'fillStyle', textFill); + ctx.fillText(token.text, x, y); + } +} + +function needDrawBackground(style) { + return style.textBackgroundColor + || (style.textBorderWidth && style.textBorderColor); +} + +// style: {textBackgroundColor, textBorderWidth, textBorderColor, textBorderRadius} +// shape: {x, y, width, height} +function drawBackground(hostEl, ctx, style, x, y, width, height) { + var textBackgroundColor = style.textBackgroundColor; + var textBorderWidth = style.textBorderWidth; + var textBorderColor = style.textBorderColor; + var isPlainBg = isString(textBackgroundColor); + + setCtx(ctx, 'shadowBlur', style.textBoxShadowBlur || 0); + setCtx(ctx, 'shadowColor', style.textBoxShadowColor || 'transparent'); + setCtx(ctx, 'shadowOffsetX', style.textBoxShadowOffsetX || 0); + setCtx(ctx, 'shadowOffsetY', style.textBoxShadowOffsetY || 0); + + if (isPlainBg || (textBorderWidth && textBorderColor)) { + ctx.beginPath(); + var textBorderRadius = style.textBorderRadius; + if (!textBorderRadius) { + ctx.rect(x, y, width, height); + } + else { + buildPath(ctx, { + x: x, y: y, width: width, height: height, r: textBorderRadius + }); + } + ctx.closePath(); + } + + if (isPlainBg) { + setCtx(ctx, 'fillStyle', textBackgroundColor); + ctx.fill(); + } + else if (isObject$1(textBackgroundColor)) { + var image = textBackgroundColor.image; + + image = createOrUpdateImage( + image, null, hostEl, onBgImageLoaded, textBackgroundColor + ); + if (image && isImageReady(image)) { + ctx.drawImage(image, x, y, width, height); + } + } + + if (textBorderWidth && textBorderColor) { + setCtx(ctx, 'lineWidth', textBorderWidth); + setCtx(ctx, 'strokeStyle', textBorderColor); + ctx.stroke(); + } +} + +function onBgImageLoaded(image, textBackgroundColor) { + // Replace image, so that `contain/text.js#parseRichText` + // will get correct result in next tick. + textBackgroundColor.image = image; +} + +function getBoxPosition(blockHeiht, style, rect) { + var baseX = style.x || 0; + var baseY = style.y || 0; + var textAlign = style.textAlign; + var textVerticalAlign = style.textVerticalAlign; + + // Text position represented by coord + if (rect) { + var textPosition = style.textPosition; + if (textPosition instanceof Array) { + // Percent + baseX = rect.x + parsePercent(textPosition[0], rect.width); + baseY = rect.y + parsePercent(textPosition[1], rect.height); + } + else { + var res = adjustTextPositionOnRect( + textPosition, rect, style.textDistance + ); + baseX = res.x; + baseY = res.y; + // Default align and baseline when has textPosition + textAlign = textAlign || res.textAlign; + textVerticalAlign = textVerticalAlign || res.textVerticalAlign; + } + + // textOffset is only support in RectText, otherwise + // we have to adjust boundingRect for textOffset. + var textOffset = style.textOffset; + if (textOffset) { + baseX += textOffset[0]; + baseY += textOffset[1]; + } + } + + return { + baseX: baseX, + baseY: baseY, + textAlign: textAlign, + textVerticalAlign: textVerticalAlign + }; +} + +function setCtx(ctx, prop, value) { + ctx[prop] = fixShadow(ctx, prop, value); + return ctx[prop]; +} + +/** + * @param {string} [stroke] If specified, do not check style.textStroke. + * @param {string} [lineWidth] If specified, do not check style.textStroke. + * @param {number} style + */ +function getStroke(stroke, lineWidth) { + return (stroke == null || lineWidth <= 0 || stroke === 'transparent' || stroke === 'none') + ? null + // TODO pattern and gradient? + : (stroke.image || stroke.colorStops) + ? '#000' + : stroke; +} + +function getFill(fill) { + return (fill == null || fill === 'none') + ? null + // TODO pattern and gradient? + : (fill.image || fill.colorStops) + ? '#000' + : fill; +} + +function parsePercent(value, maxValue) { + if (typeof value === 'string') { + if (value.lastIndexOf('%') >= 0) { + return parseFloat(value) / 100 * maxValue; + } + return parseFloat(value); + } + return value; +} + +function getTextXForPadding(x, textAlign, textPadding) { + return textAlign === 'right' + ? (x - textPadding[1]) + : textAlign === 'center' + ? (x + textPadding[3] / 2 - textPadding[1] / 2) + : (x + textPadding[3]); +} + +/** + * @param {string} text + * @param {module:zrender/Style} style + * @return {boolean} + */ +function needDrawText(text, style) { + return text != null + && (text + || style.textBackgroundColor + || (style.textBorderWidth && style.textBorderColor) + || style.textPadding + ); +} + +/** + * Mixin for drawing text in a element bounding rect + * @module zrender/mixin/RectText + */ + +var tmpRect$1 = new BoundingRect(); + +var RectText = function () {}; + +RectText.prototype = { + + constructor: RectText, + + /** + * Draw text in a rect with specified position. + * @param {CanvasRenderingContext2D} ctx + * @param {Object} rect Displayable rect + */ + drawRectText: function (ctx, rect) { + var style = this.style; + + rect = style.textRect || rect; + + // Optimize, avoid normalize every time. + this.__dirty && normalizeTextStyle(style, true); + + var text = style.text; + + // Convert to string + text != null && (text += ''); + + if (!needDrawText(text, style)) { + return; + } + + // FIXME + ctx.save(); + + // Transform rect to view space + var transform = this.transform; + if (!style.transformText) { + if (transform) { + tmpRect$1.copy(rect); + tmpRect$1.applyTransform(transform); + rect = tmpRect$1; + } + } + else { + this.setTransform(ctx); + } + + // transformText and textRotation can not be used at the same time. + renderText(this, ctx, text, style, rect); + + ctx.restore(); + } +}; + +/** + * 可绘制的图形基类 + * Base class of all displayable graphic objects + * @module zrender/graphic/Displayable + */ + + +/** + * @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); + + 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 {boolean} + */ + progressive: false, + + /** + * @type {boolean} + */ + incremental: false, + // inplace is used with incremental + inplace: false, + + beforeBrush: function (ctx) {}, + + afterBrush: function (ctx) {}, + + /** + * 图形绘制方法 + * @param {CanvasRenderingContext2D} 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); + this.dirty(false); + return this; + } +}; + +inherits(Displayable, Element); + +mixin(Displayable, RectText); + +/** + * @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; + + // Must bind each time + style.bind(ctx, this, prevEl); + + var image = this._image = createOrUpdateImage( + src, + this._image, + this, + this.onload + ); + + if (!image || !isImageReady(image)) { + return; + } + + // 图片已经加载完成 + // if (image.nodeName.toUpperCase() == 'IMG') { + // if (!image.complete) { + // return; + // } + // } + // Else is canvas + + var x = style.x || 0; + var y = style.y || 0; + var width = style.width; + var height = style.height; + var aspect = image.width / image.height; + if (width == null && height != null) { + // Keep image/height ratio + width = height * aspect; + } + else if (height == null && width != null) { + height = width / aspect; + } + else if (width == null && height == null) { + width = image.width; + height = image.height; + } + + // 设置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); + } + + // Draw rect text + if (style.text != null) { + // Only restore transform when needs draw text. + this.restoreTransform(ctx); + 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; + } +}; + +inherits(ZImage, Displayable); + +var HOVER_LAYER_ZLEVEL = 1e5; +var CANVAS_ZLEVEL = 314159; + +var EL_AFTER_INCREMENTAL_INC = 0.01; +var INCREMENTAL_INC = 0.001; + +function parseInt10(val) { + return parseInt(val, 10); +} + +function isLayerValid(layer) { + if (!layer) { + return false; + } + + if (layer.__builtin__) { + return true; + } + + if (typeof(layer.resize) !== 'function' + || typeof(layer.refresh) !== 'function' + ) { + return false; + } + + return true; +} + +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]; + + clipPath.setTransform(ctx); + ctx.beginPath(); + clipPath.buildPath(ctx, clipPath.shape); + ctx.clip(); + // Transform back + clipPath.restoreTransform(ctx); + } +} + +function createRoot(width, height) { + var domRoot = document.createElement('div'); + + // domRoot.onselectstart = returnFalse; // 避免页面选中的尴尬 + domRoot.style.cssText = [ + 'position:relative', + 'overflow:hidden', + 'width:' + width + 'px', + 'height:' + height + 'px', + 'padding:0', + 'margin:0', + 'border-width:0' + ].join(';') + ';'; + + return domRoot; +} + + +/** + * @alias module:zrender/Painter + * @constructor + * @param {HTMLElement} root 绘图容器 + * @param {module:zrender/Storage} storage + * @param {Object} opts + */ +var Painter = function (root, storage, opts) { + + this.type = 'canvas'; + + // In node environment using node-canvas + var singleCanvas = !root.nodeName // In node ? + || root.nodeName.toUpperCase() === 'CANVAS'; + + this._opts = opts = extend({}, opts || {}); + + /** + * @type {number} + */ + this.dpr = opts.devicePixelRatio || 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.} + * @private + */ + this._layerConfig = {}; + + /** + * zrender will do compositing when root is a canvas and have multiple zlevels. + */ + this._needsManuallyCompositing = false; + + if (!singleCanvas) { + this._width = this._getSize(0); + this._height = this._getSize(1); + + var domRoot = this._domRoot = createRoot( + this._width, this._height + ); + root.appendChild(domRoot); + } + else { + var width = root.width; + var height = root.height; + + if (opts.width != null) { + width = opts.width; + } + if (opts.height != null) { + height = opts.height; + } + this.dpr = opts.devicePixelRatio || 1; + + // Use canvas width and height directly + root.width = width * this.dpr; + root.height = height * this.dpr; + + this._width = width; + this._height = height; + + // Create layer if only one given canvas + // Device can be specified to create a high dpi image. + var mainLayer = new Layer(root, this, this.dpr); + mainLayer.__builtin__ = true; + mainLayer.initContext(); + // FIXME Use canvas width and height + // mainLayer.resize(width, height); + layers[CANVAS_ZLEVEL] = mainLayer; + mainLayer.zlevel = CANVAS_ZLEVEL; + // Not use common zlevel. + zlevelList.push(CANVAS_ZLEVEL); + + this._domRoot = root; + } + + /** + * @type {module:zrender/Layer} + * @private + */ + this._hoverlayer = null; + + this._hoverElements = []; +}; + +Painter.prototype = { + + constructor: Painter, + + getType: function () { + return 'canvas'; + }, + + /** + * If painter use a single canvas + * @return {boolean} + */ + isSingleCanvas: function () { + return this._singleCanvas; + }, + /** + * @return {HTMLDivElement} + */ + getViewportRoot: function () { + return this._domRoot; + }, + + getViewportRootOffset: function () { + var viewportRoot = this.getViewportRoot(); + if (viewportRoot) { + return { + offsetLeft: viewportRoot.offsetLeft || 0, + offsetTop: viewportRoot.offsetTop || 0 + }; + } + }, + + /** + * 刷新 + * @param {boolean} [paintAll=false] 强制绘制所有displayable + */ + refresh: function (paintAll) { + + var list = this.storage.getDisplayList(true); + + var zlevelList = this._zlevelList; + + this._redrawId = Math.random(); + + this._paintList(list, paintAll, this._redrawId); + + // Paint custum layers + for (var i = 0; i < zlevelList.length; i++) { + var z = zlevelList[i]; + var layer = this._layers[z]; + if (!layer.__builtin__ && layer.refresh) { + var clearColor = i === 0 ? this._backgroundColor : null; + layer.refresh(clearColor); + } + } + + this.refreshHover(); + + 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 = 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; + } + sort(hoverElements, this.storage.displayableSortFunc); + + // Use a extream large zlevel + // FIXME? + if (!hoverLayer) { + hoverLayer = this._hoverlayer = this.getLayer(HOVER_LAYER_ZLEVEL); + } + + 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(); + }, + + getHoverLayer: function () { + return this.getLayer(HOVER_LAYER_ZLEVEL); + }, + + _paintList: function (list, paintAll, redrawId) { + if (this._redrawId !== redrawId) { + return; + } + + paintAll = paintAll || false; + + this._updateLayerStatus(list); + + var finished = this._doPaintList(list, paintAll); + + if (this._needsManuallyCompositing) { + this._compositeManually(); + } + + if (!finished) { + var self = this; + requestAnimationFrame(function () { + self._paintList(list, paintAll, redrawId); + }); + } + }, + + _compositeManually: function () { + var ctx = this.getLayer(CANVAS_ZLEVEL).ctx; + var width = this._domRoot.width; + var height = this._domRoot.height; + ctx.clearRect(0, 0, width, height); + // PENDING, If only builtin layer? + this.eachBuiltinLayer(function (layer) { + if (layer.virtual) { + ctx.drawImage(layer.dom, 0, 0, width, height); + } + }); + }, + + _doPaintList: function (list, paintAll) { + var layerList = []; + for (var zi = 0; zi < this._zlevelList.length; zi++) { + var zlevel = this._zlevelList[zi]; + var layer = this._layers[zlevel]; + if (layer.__builtin__ + && layer !== this._hoverlayer + && (layer.__dirty || paintAll) + ) { + layerList.push(layer); + } + } + + var finished = true; + + for (var k = 0; k < layerList.length; k++) { + var layer = layerList[k]; + var ctx = layer.ctx; + var scope = {}; + ctx.save(); + + var start = paintAll ? layer.__startIndex : layer.__drawIndex; + + var useTimer = !paintAll && layer.incremental && Date.now; + var startTime = useTimer && Date.now(); + + var clearColor = layer.zlevel === this._zlevelList[0] + ? this._backgroundColor : null; + // All elements in this layer are cleared. + if (layer.__startIndex === layer.__endIndex) { + layer.clear(false, clearColor); + } + else if (start === layer.__startIndex) { + var firstEl = list[start]; + if (!firstEl.incremental || !firstEl.notClear || paintAll) { + layer.clear(false, clearColor); + } + } + if (start === -1) { + console.error('For some unknown reason. drawIndex is -1'); + start = layer.__startIndex; + } + for (var i = start; i < layer.__endIndex; i++) { + var el = list[i]; + this._doPaintEl(el, layer, paintAll, scope); + el.__dirty = false; + + if (useTimer) { + // Date.now can be executed in 13,025,305 ops/second. + var dTime = Date.now() - startTime; + // Give 15 millisecond to draw. + // The rest elements will be drawn in the next frame. + if (dTime > 15) { + break; + } + } + } + + layer.__drawIndex = i; + + if (layer.__drawIndex < layer.__endIndex) { + finished = false; + } + + if (scope.prevElClipPaths) { + // Needs restore the state. If last drawn element is in the clipping area. + ctx.restore(); + } + + ctx.restore(); + } + + if (env$1.wxa) { + // Flush for weixin application + each$1(this._layers, function (layer) { + if (layer && layer.ctx && layer.ctx.draw) { + layer.ctx.draw(); + } + }); + } + + return finished; + }, + + _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.prevElClipPaths + || isClipPathChanged(clipPaths, scope.prevElClipPaths) + ) { + // If has previous clipping state, restore from it + if (scope.prevElClipPaths) { + currentLayer.ctx.restore(); + scope.prevElClipPaths = null; + + // Reset prevEl since context has been restored + scope.prevEl = null; + } + // New clipping state + if (clipPaths) { + ctx.save(); + doClip(clipPaths, ctx); + 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 + * @param {boolean} virtual Virtual layer will not be inserted into dom. + * @return {module:zrender/Layer} + */ + getLayer: function (zlevel, virtual) { + if (this._singleCanvas && !this._needsManuallyCompositing) { + zlevel = CANVAS_ZLEVEL; + } + var layer = this._layers[zlevel]; + if (!layer) { + // Create a new layer + layer = new Layer('zr_' + zlevel, this, this.dpr); + layer.zlevel = zlevel; + layer.__builtin__ = true; + + if (this._layerConfig[zlevel]) { + merge(layer, this._layerConfig[zlevel], true); + } + + if (virtual) { + layer.virtual = virtual; + } + + 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]) { + zrLog('ZLevel ' + zlevel + ' has been used already'); + return; + } + // Check if is a valid layer + if (!isLayerValid(layer)) { + zrLog('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); + + layersMap[zlevel] = layer; + + // Vitual layer will not directly show on the screen. + // (It can be a WebGL layer and assigned to a ZImage element) + // But it still under management of zrender. + if (!layer.virtual) { + 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); + } + } + } + }, + + // 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 + eachBuiltinLayer: 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.__builtin__) { + 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.__builtin__) { + cb.call(context, layer, z); + } + } + }, + + /** + * 获取所有已创建的层 + * @param {Array.} [prevLayer] + */ + getLayers: function () { + return this._layers; + }, + + _updateLayerStatus: function (list) { + + this.eachBuiltinLayer(function (layer, z) { + layer.__dirty = layer.__used = false; + }); + + function updatePrevLayer(idx) { + if (prevLayer) { + if (prevLayer.__endIndex !== idx) { + prevLayer.__dirty = true; + } + prevLayer.__endIndex = idx; + } + } + + if (this._singleCanvas) { + for (var i = 1; i < list.length; i++) { + var el = list[i]; + if (el.zlevel !== list[i - 1].zlevel || el.incremental) { + this._needsManuallyCompositing = true; + break; + } + } + } + + var prevLayer = null; + var incrementalLayerCount = 0; + for (var i = 0; i < list.length; i++) { + var el = list[i]; + var zlevel = el.zlevel; + var layer; + // PENDING If change one incremental element style ? + // TODO Where there are non-incremental elements between incremental elements. + if (el.incremental) { + layer = this.getLayer(zlevel + INCREMENTAL_INC, this._needsManuallyCompositing); + layer.incremental = true; + incrementalLayerCount = 1; + } + else { + layer = this.getLayer(zlevel + (incrementalLayerCount > 0 ? EL_AFTER_INCREMENTAL_INC : 0), this._needsManuallyCompositing); + } + + if (!layer.__builtin__) { + zrLog('ZLevel ' + zlevel + ' has been used by unkown layer ' + layer.id); + } + + if (layer !== prevLayer) { + layer.__used = true; + if (layer.__startIndex !== i) { + layer.__dirty = true; + } + layer.__startIndex = i; + if (!layer.incremental) { + layer.__drawIndex = i; + } + else { + // Mark layer draw index needs to update. + layer.__drawIndex = -1; + } + updatePrevLayer(i); + prevLayer = layer; + } + if (el.__dirty) { + layer.__dirty = true; + if (layer.incremental && layer.__drawIndex < 0) { + // Start draw from the first dirty element. + layer.__drawIndex = i; + } + } + } + + updatePrevLayer(i); + + this.eachBuiltinLayer(function (layer, z) { + // Used in last frame but not in this frame. Needs clear + if (!layer.__used && layer.getElementCount() > 0) { + layer.__dirty = true; + layer.__startIndex = layer.__endIndex = layer.__drawIndex = 0; + } + // For incremental layer. In case start index changed and no elements are dirty. + if (layer.__dirty && layer.__drawIndex < 0) { + layer.__drawIndex = layer.__startIndex; + } + }); + }, + + /** + * 清除hover层外所有内容 + */ + clear: function () { + this.eachBuiltinLayer(this._clearLayer); + return this; + }, + + _clearLayer: function (layer) { + layer.clear(); + }, + + setBackgroundColor: function (backgroundColor) { + this._backgroundColor = backgroundColor; + }, + + /** + * 修改指定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 { + merge(layerConfig[zlevel], config, true); + } + + for (var i = 0; i < this._zlevelList.length; i++) { + var _zlevel = this._zlevelList[i]; + if (_zlevel === zlevel || _zlevel === zlevel + EL_AFTER_INCREMENTAL_INC) { + var layer = this._layers[_zlevel]; + 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(indexOf(zlevelList, zlevel), 1); + }, + + /** + * 区域大小变化后重绘 + */ + resize: function (width, height) { + if (!this._domRoot.style) { // Maybe in node or worker + if (width == null || height == null) { + return; + } + this._width = width; + this._height = height; + + this.getLayer(CANVAS_ZLEVEL).resize(width, height); + } + else { + var domRoot = this._domRoot; + // FIXME Why ? + domRoot.style.display = 'none'; + + // Save input w/h + var opts = this._opts; + width != null && (opts.width = width); + height != null && (opts.height = height); + + width = this._getSize(0); + height = this._getSize(1); + + 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) { + if (this._layers.hasOwnProperty(id)) { + this._layers[id].resize(width, height); + } + } + each$1(this._progressiveLayers, function (layer) { + layer.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] + * @param {number} [opts.pixelRatio] + */ + getRenderedCanvas: function (opts) { + opts = opts || {}; + if (this._singleCanvas && !this._compositeManually) { + return this._layers[CANVAS_ZLEVEL].dom; + } + + var imageLayer = new Layer('image', this, opts.pixelRatio || this.dpr); + imageLayer.initContext(); + imageLayer.clear(false, opts.backgroundColor || this._backgroundColor); + + if (opts.pixelRatio <= this.dpr) { + this.refresh(); + + var width = imageLayer.dom.width; + var height = imageLayer.dom.height; + var ctx = imageLayer.ctx; + this.eachLayer(function (layer) { + if (layer.__builtin__) { + ctx.drawImage(layer.dom, 0, 0, width, height); + } + else if (layer.renderToCanvas) { + imageLayer.ctx.save(); + layer.renderToCanvas(imageLayer.ctx); + imageLayer.ctx.restore(); + } + }); + } + else { + // PENDING, echarts-gl and incremental rendering. + var scope = {}; + var displayList = this.storage.getDisplayList(true); + 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; + }, + + _getSize: function (whIdx) { + var opts = this._opts; + var wh = ['width', 'height'][whIdx]; + var cwh = ['clientWidth', 'clientHeight'][whIdx]; + var plt = ['paddingLeft', 'paddingTop'][whIdx]; + var prb = ['paddingRight', 'paddingBottom'][whIdx]; + + if (opts[wh] != null && opts[wh] !== 'auto') { + return parseFloat(opts[wh]); + } + + var root = this.root; + // IE8 does not support getComputedStyle, but it use VML. + var stl = document.defaultView.getComputedStyle(root); + + return ( + (root[cwh] || parseInt10(stl[wh]) || parseInt10(root.style[wh])) + - (parseInt10(stl[plt]) || 0) + - (parseInt10(stl[prb]) || 0) + ) | 0; + }, + + pathToImage: function (path, dpr) { + dpr = dpr || this.dpr; + + var canvas = document.createElement('canvas'); + var ctx = canvas.getContext('2d'); + var rect = path.getBoundingRect(); + var style = path.style; + var shadowBlurSize = style.shadowBlur * dpr; + var shadowOffsetX = style.shadowOffsetX * dpr; + var shadowOffsetY = style.shadowOffsetY * dpr; + var lineWidth = style.hasStroke() ? style.lineWidth : 0; + + var leftMargin = Math.max(lineWidth / 2, -shadowOffsetX + shadowBlurSize); + var rightMargin = Math.max(lineWidth / 2, shadowOffsetX + shadowBlurSize); + var topMargin = Math.max(lineWidth / 2, -shadowOffsetY + shadowBlurSize); + var bottomMargin = Math.max(lineWidth / 2, shadowOffsetY + shadowBlurSize); + var width = rect.width + leftMargin + rightMargin; + var height = rect.height + topMargin + bottomMargin; + + canvas.width = width * dpr; + canvas.height = height * dpr; + + ctx.scale(dpr, dpr); + ctx.clearRect(0, 0, width, height); + ctx.dpr = dpr; + + var pathTransform = { + position: path.position, + rotation: path.rotation, + scale: path.scale + }; + path.position = [leftMargin - rect.x, topMargin - rect.y]; + path.rotation = 0; + path.scale = [1, 1]; + path.updateTransform(); + if (path) { + path.brush(ctx); + } + + var ImageShape = ZImage; + var imgShape = new ImageShape({ + 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; + } +}; + +/** + * 事件辅助类 + * @module zrender/core/event + * @author Kener (@Kener-林峰, kener.linfeng@gmail.com) + */ + +var isDomLevel2 = (typeof window !== 'undefined') && !!window.addEventListener; + +var MOUSE_EVENT_REG = /^(?:mouse|pointer|contextmenu|drag|drop)|click/; + +function getBoundingClientRect(el) { + // BlackBerry 5, iOS 3 (original iPhone) don't have getBoundingRect + return el.getBoundingClientRect ? el.getBoundingClientRect() : {left: 0, top: 0}; +} + +// `calculate` is optional, default false +function clientToLocal(el, e, out, calculate) { + out = out || {}; + + // According to the W3C Working Draft, offsetX and offsetY should be relative + // to the padding edge of the target element. The only browser using this convention + // is IE. Webkit uses the border edge, Opera uses the content edge, and FireFox does + // not support the properties. + // (see http://www.jacklmoore.com/notes/mouse-position/) + // In zr painter.dom, padding edge equals to border edge. + + // FIXME + // When mousemove event triggered on ec tooltip, target is not zr painter.dom, and + // offsetX/Y is relative to e.target, where the calculation of zrX/Y via offsetX/Y + // is too complex. So css-transfrom dont support in this case temporarily. + if (calculate || !env$1.canvasSupported) { + defaultGetZrXY(el, e, out); + } + // Caution: In FireFox, layerX/layerY Mouse position relative to the closest positioned + // ancestor element, so we should make sure el is positioned (e.g., not position:static). + // BTW1, Webkit don't return the same results as FF in non-simple cases (like add + // zoom-factor, overflow / opacity layers, transforms ...) + // BTW2, (ev.offsetY || ev.pageY - $(ev.target).offset().top) is not correct in preserve-3d. + // + // BTW3, In ff, offsetX/offsetY is always 0. + else if (env$1.browser.firefox && e.layerX != null && e.layerX !== e.offsetX) { + out.zrX = e.layerX; + out.zrY = e.layerY; + } + // For IE6+, chrome, safari, opera. (When will ff support offsetX?) + else if (e.offsetX != null) { + out.zrX = e.offsetX; + out.zrY = e.offsetY; + } + // For some other device, e.g., IOS safari. + else { + defaultGetZrXY(el, e, out); + } + + return out; +} + +function defaultGetZrXY(el, e, out) { + // This well-known method below does not support css transform. + var box = getBoundingClientRect(el); + out.zrX = e.clientX - box.left; + out.zrY = e.clientY - box.top; +} + +/** + * 如果存在第三方嵌入的一些dom触发的事件,或touch事件,需要转换一下事件坐标. + * `calculate` is optional, default false. + */ +function normalizeEvent(el, e, calculate) { + + 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, calculate); + 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, calculate); + } + + // Add which for click: 1 === left; 2 === middle; 3 === right; otherwise: 0; + // See jQuery: https://github.com/jquery/jquery/blob/master/src/event.js + // If e.which has been defined, if may be readonly, + // see: https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/which + var button = e.button; + if (e.which == null && button !== undefined && MOUSE_EVENT_REG.test(e.type)) { + e.which = (button & 1 ? 1 : (button & 2 ? 3 : (button & 4 ? 2 : 0))); + } + + return e; +} + +/** + * @param {HTMLElement} el + * @param {string} name + * @param {Function} handler + */ +function addEventListener(el, name, handler) { + if (isDomLevel2) { + // Reproduct the console warning: + // [Violation] Added non-passive event listener to a scroll-blocking event. + // Consider marking event handler as 'passive' to make the page more responsive. + // Just set console log level: verbose in chrome dev tool. + // then the warning log will be printed when addEventListener called. + // See https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md + // We have not yet found a neat way to using passive. Because in zrender the dom event + // listener delegate all of the upper events of element. Some of those events need + // to prevent default. For example, the feature `preventDefaultMouseMove` of echarts. + // Before passive can be adopted, these issues should be considered: + // (1) Whether and how a zrender user specifies an event listener passive. And by default, + // passive or not. + // (2) How to tread that some zrender event listener is passive, and some is not. If + // we use other way but not preventDefault of mousewheel and touchmove, browser + // compatibility should be handled. + + // var opts = (env.passiveSupported && name === 'mousewheel') + // ? {passive: true} + // // By default, the third param of el.addEventListener is `capture: false`. + // : void 0; + // el.addEventListener(name, handler /* , opts */); + 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); + } +} + +/** + * preventDefault and stopPropagation. + * Notice: do not do that in zrender. Upper application + * do that if necessary. + * + * @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; + }; + +function notLeftMouse(e) { + // If e.which is undefined, considered as left mouse event. + return e.which > 1; +} + +/** + * 动画主类, 调度和管理所有动画控制器 + * + * @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 + +/** + * @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; + + Eventful.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 = 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, delta); + // 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); + + // 'frame' should be triggered before stage, because upper application + // depends on the sequence (e.g., echarts-stream and finish + // event judge) + 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 animation. + */ + start: function () { + + this._time = new Date().getTime(); + this._pausedTime = 0; + + this._startLoop(); + }, + + /** + * Stop animation. + */ + stop: function () { + this._running = false; + }, + + /** + * Pause animation. + */ + pause: function () { + if (!this._paused) { + this._pauseStart = new Date().getTime(); + this._paused = true; + } + }, + + /** + * Resume animation. + */ + resume: function () { + if (this._paused) { + this._pausedTime += (new Date().getTime()) - this._pauseStart; + this._paused = false; + } + }, + + /** + * Clear animation. + */ + clear: function () { + this._clips = []; + }, + + /** + * Whether animation finished. + */ + isFinished: function () { + return !this._clips.length; + }, + + /** + * Creat animator for a target, whose props can be animated. + * + * @param {Object} target + * @param {Object} options + * @param {boolean} [options.loop=false] Whether loop animation. + * @param {Function} [options.getter=null] Get value from target. + * @param {Function} [options.setter=null] Set value to target. + * @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 + ); + + this.addAnimator(animator); + + return animator; + } +}; + +mixin(Animation, Eventful); + +/** + * Only implements needed gestures for mobile. + */ + +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 = 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$1(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$1(pinchEnd) / dist$1(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. +}; + +var TOUCH_CLICK_DELAY = 300; + +var mouseHandlerNames = [ + 'click', 'dblclick', 'mousewheel', 'mouseout', + 'mouseup', 'mousedown', 'mousemove', 'contextmenu' +]; + +var touchHandlerNames = [ + 'touchstart', 'touchend', 'touchmove' +]; + +var pointerEventNames = { + pointerdown: 1, pointerup: 1, pointermove: 1, pointerout: 1 +}; + +var pointerHandlerNames = map(mouseHandlerNames, function (name) { + var nm = name.replace('mouse', 'pointer'); + return pointerEventNames[nm] ? nm : name; +}); + +function eventNameFix(name) { + return (name === 'mousewheel' && env$1.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).target, + proxy.dom + ); + + stage === 'end' && gestureMgr.clear(); + + // Do not do any preventDefault here. Upper application do that if necessary. + if (gestureInfo) { + var type = gestureInfo.type; + event.gestureEvent = type; + + proxy.handler.dispatchToElement({target: gestureInfo.target}, type, gestureInfo.event); + } +} + +// function onMSGestureChange(proxy, event) { +// if (event.translationX || event.translationY) { +// // mousemove is carried by MSGesture to reduce the sensitivity. +// proxy.handler.dispatchToElement(event.target, 'mousemove', event); +// } +// if (event.scale !== 1) { +// event.pinchX = event.offsetX; +// event.pinchY = event.offsetY; +// event.pinchScale = event.scale; +// proxy.handler.dispatchToElement(event.target, 'pinch', 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); +} + + +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); + + // Mark touch, which is useful in distinguish touch and + // mouse event in upper applicatoin. + event.zrByTouch = true; + + this._lastTouchMoment = new Date(); + + processGesture(this, event, 'start'); + + // In touch device, trigger `mousemove`(`mouseover`) should + // be triggered, and must before `mousedown` triggered. + 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); + + // Mark touch, which is useful in distinguish touch and + // mouse event in upper applicatoin. + event.zrByTouch = true; + + 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); + + // Mark touch, which is useful in distinguish touch and + // mouse event in upper applicatoin. + event.zrByTouch = true; + + processGesture(this, event, 'end'); + + domHandlers.mouseup.call(this, event); + + // Do not trigger `mouseout` here, in spite of `mousemove`(`mouseover`) is + // triggered in `touchstart`. This seems to be illogical, but by this mechanism, + // we can conveniently implement "hover style" in both PC and touch device just + // by listening to `mouseover` to add "hover style" and listening to `mouseout` + // to remove "hover style" on an element, without any additional code for + // compatibility. (`mouseout` will not be triggered in `touchend`, so "hover + // style" will remain for user view) + + // 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); + }, + + pointerdown: function (event) { + domHandlers.mousedown.call(this, event); + + // if (useMSGuesture(this, event)) { + // this._msGesture.addPointer(event.pointerId); + // } + }, + + pointermove: function (event) { + // FIXME + // pointermove is so sensitive that it always triggered when + // tap(click) on touch screen, which affect some judgement in + // upper application. So, we dont support mousemove on MS touch + // device yet. + if (!isPointerFromTouch(event)) { + domHandlers.mousemove.call(this, event); + } + }, + + pointerup: function (event) { + domHandlers.mouseup.call(this, event); + }, + + pointerout: function (event) { + // pointerout will be triggered when tap on touch screen + // (IE11+/Edge on MS Surface) after click event triggered, + // which is inconsistent with the mousout behavior we defined + // in touchend. So we unify them. + // (check domHandlers.touchend for detailed explanation) + if (!isPointerFromTouch(event)) { + domHandlers.mouseout.call(this, event); + } + } +}; + +function isPointerFromTouch(event) { + var pointerType = event.pointerType; + return pointerType === 'pen' || pointerType === 'touch'; +} + +// function useMSGuesture(handlerProxy, event) { +// return isPointerFromTouch(event) && !!handlerProxy._msGesture; +// } + +// Common handlers +each$1(['click', 'mousedown', 'mouseup', 'mousewheel', 'dblclick', 'contextmenu'], 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) { + each$1(touchHandlerNames, function (name) { + instance._handlers[name] = bind(domHandlers[name], instance); + }); + + each$1(pointerHandlerNames, function (name) { + instance._handlers[name] = bind(domHandlers[name], instance); + }); + + each$1(mouseHandlerNames, function (name) { + 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 (env$1.pointerEventsSupported) { // Only IE11+/Edge + // 1. On devices that both enable touch and mouse (e.g., MS Surface and lenovo X240), + // IE11+/Edge do not trigger touch event, but trigger pointer event and mouse event + // at the same time. + // 2. On MS Surface, it probablely only trigger mousedown but no mouseup when tap on + // screen, which do not occurs in pointer event. + // So we use pointer event to both detect touch gesture and mouse behavior. + mountHandlers(pointerHandlerNames, this); + + // FIXME + // Note: MS Gesture require CSS touch-action set. But touch-action is not reliable, + // which does not prevent defuault behavior occasionally (which may cause view port + // zoomed in but use can not zoom it back). And event.preventDefault() does not work. + // So we have to not to use MSGesture and not to support touchmove and pinch on MS + // touch screen. And we only support click behavior on MS touch screen now. + + // MS Gesture Event is only supported on IE11+/Edge and on Windows 8+. + // We dont support touch on IE on win7. + // See + // if (typeof MSGesture === 'function') { + // (this._msGesture = new MSGesture()).target = dom; // jshint ignore:line + // dom.addEventListener('MSGestureChange', onMSGestureChange); + // } + } + else { + if (env$1.touchEventsSupported) { + mountHandlers(touchHandlerNames, this); + // Handler of 'mouseout' event is needed in touch mode, which will be mounted below. + // addEventListener(root, 'mouseout', this._mouseoutHandler); + } + + // 1. Considering some devices that both enable touch and mouse event (like on MS Surface + // and lenovo X240, @see #2350), we make mouse event be always listened, otherwise + // mouse event can not be handle in those devices. + // 2. On MS Surface, Chrome will trigger both touch event and mouse event. How to prevent + // mouseevent after touch event triggered, see `setTouchTimer`. + mountHandlers(mouseHandlerNames, this); + } + + function mountHandlers(handlerNames, instance) { + each$1(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 && (this.dom.style.cursor = cursorStyle || 'default'); +}; + +mixin(HandlerDomProxy, Eventful); + +/*! +* 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 +*/ + +var useVML = !env$1.canvasSupported; + +var painterCtors = { + canvas: Painter +}; + +var instances$1 = {}; // ZRender实例map索引 + +/** + * @type {string} + */ +var version$1 = '4.0.4'; + +/** + * Initializing a zrender instance + * @param {HTMLElement} dom + * @param {Object} opts + * @param {string} [opts.renderer='canvas'] 'canvas' or 'svg' + * @param {number} [opts.devicePixelRatio] + * @param {number|string} [opts.width] Can be 'auto' (the same as null/undefined) + * @param {number|string} [opts.height] Can be 'auto' (the same as null/undefined) + * @return {module:zrender/ZRender} + */ +function init$1(dom, opts) { + var zr = new ZRender(guid(), dom, opts); + instances$1[zr.id] = zr; + return zr; +} + +/** + * Dispose zrender instance + * @param {module:zrender/ZRender} zr + */ +function dispose$1(zr) { + if (zr) { + zr.dispose(); + } + else { + for (var key in instances$1) { + if (instances$1.hasOwnProperty(key)) { + instances$1[key].dispose(); + } + } + instances$1 = {}; + } + + return this; +} + +/** + * Get zrender instance by id + * @param {string} id zrender instance id + * @return {module:zrender/ZRender} + */ +function getInstance(id) { + return instances$1[id]; +} + +function registerPainter(name, Ctor) { + painterCtors[name] = Ctor; +} + +function delInstance(id) { + delete instances$1[id]; +} + +/** + * @module zrender/ZRender + */ +/** + * @constructor + * @alias module:zrender/ZRender + * @param {string} id + * @param {HTMLElement} dom + * @param {Object} opts + * @param {string} [opts.renderer='canvas'] 'canvas' or 'svg' + * @param {number} [opts.devicePixelRatio] + * @param {number} [opts.width] Can be 'auto' (the same as null/undefined) + * @param {number} [opts.height] Can be 'auto' (the same as null/undefined) + */ +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; + // TODO WebGL + 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, id); + + this.storage = storage; + this.painter = painter; + + var handerProxy = (!env$1.node && !env$1.worker) ? new HandlerDomProxy(painter.getViewportRoot()) : null; + this.handler = new Handler(storage, painter, handerProxy, painter.root); + + /** + * @type {module:zrender/animation/Animation} + */ + this.animation = new Animation({ + stage: { + update: bind(this.flush, this) + } + }); + this.animation.start(); + + /** + * @type {boolean} + * @private + */ + this._needsRefresh; + + // 修改 storage.delFromStorage, 每次删除元素之前删除动画 + // FIXME 有点ugly + var oldDelFromStorage = storage.delFromStorage; + var oldAddToStorage = storage.addToStorage; + + storage.delFromStorage = function (el) { + oldDelFromStorage.call(storage, el); + + el && el.removeSelfFromZr(self); + }; + + storage.addToStorage = function (el) { + oldAddToStorage.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) { + if (this.painter.configLayer) { + this.painter.configLayer(zLevel, config); + } + this._needsRefresh = true; + }, + + /** + * Set background color + * @param {string} backgroundColor + */ + setBackgroundColor: function (backgroundColor) { + if (this.painter.setBackgroundColor) { + this.painter.setBackgroundColor(backgroundColor); + } + this._needsRefresh = true; + }, + + /** + * Repaint the canvas immediately + */ + refreshImmediately: function () { + // var start = new Date(); + // 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; + // var end = new Date(); + // var log = document.getElementById('log'); + // if (log) { + // log.innerHTML = log.innerHTML + '
' + (end - start); + // } + }, + + /** + * Mark and repaint the canvas in the next frame of browser + */ + refresh: function() { + this._needsRefresh = true; + }, + + /** + * Perform all refresh + */ + flush: function () { + var triggerRendered; + + if (this._needsRefresh) { + triggerRendered = true; + this.refreshImmediately(); + } + if (this._needsRefreshHover) { + triggerRendered = true; + this.refreshHoverImmediately(); + } + + triggerRendered && this.trigger('rendered'); + }, + + /** + * 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 + * @param {Object} [opts] + * @param {number|string} [opts.width] Can be 'auto' (the same as null/undefined) + * @param {number|string} [opts.height] Can be 'auto' (the same as null/undefined) + */ + resize: function(opts) { + opts = opts || {}; + this.painter.resize(opts.width, opts.height); + 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, dpr) { + return this.painter.pathToImage(e, dpr); + }, + + /** + * Set default cursor + * @param {string} [cursorStyle='default'] 例如 crosshair + */ + setCursorStyle: function (cursorStyle) { + this.handler.setCursorStyle(cursorStyle); + }, + + /** + * Find hovered element + * @param {number} x + * @param {number} y + * @return {Object} {target, topTarget} + */ + findHover: function (x, y) { + return this.handler.findHover(x, y); + }, + + /** + * 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); + } +}; + + + +var zrender = (Object.freeze || Object)({ + version: version$1, + init: init$1, + dispose: dispose$1, + getInstance: getInstance, + registerPainter: registerPainter +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var each$2 = each$1; +var isObject$2 = isObject$1; +var isArray$1 = isArray; + +/** + * Make the name displayable. But we should + * make sure it is not duplicated with user + * specified name, so use '\0'; + */ +var DUMMY_COMPONENT_NAME_PREFIX = 'series\0'; + +/** + * If value is not array, then translate it to array. + * @param {*} value + * @return {Array} [value] or value + */ +function normalizeToArray(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: { + * show: false, + * position: 'outside', + * fontSize: 18 + * }, + * emphasis: { + * label: { show: true } + * } + * @param {Object} opt + * @param {string} key + * @param {Array.} subOpts + */ +function defaultEmphasis(opt, key, subOpts) { + // Caution: performance sensitive. + if (opt) { + opt[key] = opt[key] || {}; + opt.emphasis = opt.emphasis || {}; + opt.emphasis[key] = opt.emphasis[key] || {}; + + // Default emphasis option from normal + for (var i = 0, len = subOpts.length; i < len; i++) { + var subOptName = subOpts[i]; + if (!opt.emphasis[key].hasOwnProperty(subOptName) + && opt[key].hasOwnProperty(subOptName) + ) { + opt.emphasis[key][subOptName] = opt[key][subOptName]; + } + } + } +} + +var TEXT_STYLE_OPTIONS = [ + 'fontStyle', 'fontWeight', 'fontSize', 'fontFamily', + 'rich', 'tag', 'color', 'textBorderColor', 'textBorderWidth', + 'width', 'height', 'lineHeight', 'align', 'verticalAlign', 'baseline', + 'shadowColor', 'shadowBlur', 'shadowOffsetX', 'shadowOffsetY', + 'textShadowColor', 'textShadowBlur', 'textShadowOffsetX', 'textShadowOffsetY', + 'backgroundColor', 'borderColor', 'borderWidth', 'borderRadius', 'padding' +]; + +// modelUtil.LABEL_OPTIONS = modelUtil.TEXT_STYLE_OPTIONS.concat([ +// 'position', 'offset', 'rotate', 'origin', 'show', 'distance', 'formatter', +// 'fontStyle', 'fontWeight', 'fontSize', 'fontFamily', +// // FIXME: deprecated, check and remove it. +// 'textStyle' +// ]); + +/** + * The method do not ensure performance. + * 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.} + */ +function getDataItemValue(dataItem) { + return (isObject$2(dataItem) && !isArray$1(dataItem) && !(dataItem instanceof Date)) + ? dataItem.value : dataItem; +} + +/** + * 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 + */ +function isDataItemOption(dataItem) { + return isObject$2(dataItem) + && !(dataItem instanceof Array); + // // markLine data can be array + // && !(dataItem[0] && isObject(dataItem[0]) && !(dataItem[0] instanceof Array)); +} + +/** + * Mapping to exists for merge. + * + * @public + * @param {Array.|Array.} exists + * @param {Object|Array.} newCptOptions + * @return {Array.} Result, like [{exist: ..., option: ...}, {}], + * index of which is the same as exists. + */ +function mappingToExists(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 = map(exists || [], function (obj, index) { + return {exist: obj}; + }); + + // Mapping by id or name if specified. + each$2(newCptOptions, function (cptOption, index) { + if (!isObject$2(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 + && !isIdInner(cptOption) + && !isIdInner(exist) + && exist.name === cptOption.name + '' + ) { + result[i].option = cptOption; + newCptOptions[index] = null; + return; + } + } + }); + + // Otherwise mapping by index. + each$2(newCptOptions, function (cptOption, index) { + if (!isObject$2(cptOption)) { + return; + } + + var i = 0; + for (; i < result.length; i++) { + var exist = result[i].exist; + if (!result[i].option + // Existing model that already has id should be able to + // mapped to (because after mapping performed model may + // be assigned with a id, whish should not affect next + // mapping), except those has inner id. + && !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; +} + +/** + * Make id and name for mapping result (result of mappingToExists) + * into `keyInfo` field. + * + * @public + * @param {Array.} Result, like [{exist: ..., option: ...}, {}], + * which order is the same as exists. + * @return {Array.} The input. + */ +function makeIdAndName(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 = createHashMap(); + + each$2(mapResult, function (item, index) { + var existCpt = item.exist; + existCpt && idMap.set(existCpt.id, item); + }); + + each$2(mapResult, function (item, index) { + var opt = item.option; + + assert$1( + !opt || opt.id == null || !idMap.get(opt.id) || idMap.get(opt.id) === item, + 'id duplicates: ' + (opt && opt.id) + ); + + opt && opt.id != null && idMap.set(opt.id, item); + !item.keyInfo && (item.keyInfo = {}); + }); + + // Make name and id. + each$2(mapResult, function (item, index) { + var existCpt = item.exist; + var opt = item.option; + var keyInfo = item.keyInfo; + + if (!isObject$2(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 + // Avoid diffferent series has the same name, + // because name may be used like in color pallet. + : DUMMY_COMPONENT_NAME_PREFIX + index; + + 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.get(keyInfo.id)); + } + + idMap.set(keyInfo.id, item); + }); +} + +function isNameSpecified(componentModel) { + var name = componentModel.name; + // Is specified when `indexOf` get -1 or > 0. + return !!(name && name.indexOf(DUMMY_COMPONENT_NAME_PREFIX)); +} + +/** + * @public + * @param {Object} cptOption + * @return {boolean} + */ +function isIdInner(cptOption) { + return isObject$2(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] + */ +function compressBatches(batchA, batchB) { + var mapA = {}; + var mapB = {}; + + makeMap(batchA || [], mapA); + makeMap(batchB || [], mapB, mapA); + + return [mapToArray(mapA), mapToArray(mapB)]; + + function makeMap(sourceBatch, map$$1, otherMap) { + for (var i = 0, len = sourceBatch.length; i < len; i++) { + var seriesId = sourceBatch[i].seriesId; + var dataIndices = 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$$1[seriesId] || (map$$1[seriesId] = {}))[dataIndex] = 1; + } + } + } + } + + function mapToArray(map$$1, isData) { + var result = []; + for (var i in map$$1) { + if (map$$1.hasOwnProperty(i) && map$$1[i] != null) { + if (isData) { + result.push(+i); + } + else { + var dataIndices = mapToArray(map$$1[i], true); + dataIndices.length && result.push({seriesId: i, dataIndex: dataIndices}); + } + } + } + return result; + } +} + +/** + * @param {module:echarts/data/List} data + * @param {Object} payload Contains dataIndex (means rawIndex) / dataIndexInside / name + * each of which can be Array or primary type. + * @return {number|Array.} dataIndex If not found, return undefined/null. + */ +function queryDataIndex(data, payload) { + if (payload.dataIndexInside != null) { + return payload.dataIndexInside; + } + else if (payload.dataIndex != null) { + return isArray(payload.dataIndex) + ? map(payload.dataIndex, function (value) { + return data.indexOfRawIndex(value); + }) + : data.indexOfRawIndex(payload.dataIndex); + } + else if (payload.name != null) { + return isArray(payload.name) + ? map(payload.name, function (value) { + return data.indexOfName(value); + }) + : data.indexOfName(payload.name); + } +} + +/** + * Enable property storage to any host object. + * Notice: Serialization is not supported. + * + * For example: + * var inner = zrUitl.makeInner(); + * + * function some1(hostObj) { + * inner(hostObj).someProperty = 1212; + * ... + * } + * function some2() { + * var fields = inner(this); + * fields.someProperty1 = 1212; + * fields.someProperty2 = 'xx'; + * ... + * } + * + * @return {Function} + */ +function makeInner() { + // Consider different scope by es module import. + var key = '__\0ec_inner_' + innerUniqueIndex++ + '_' + Math.random().toFixed(5); + return function (hostObj) { + return hostObj[key] || (hostObj[key] = {}); + }; +} +var innerUniqueIndex = 0; + +/** + * @param {module:echarts/model/Global} ecModel + * @param {string|Object} finder + * If string, e.g., 'geo', means {geoIndex: 0}. + * If Object, could contain some of these properties below: + * { + * seriesIndex, seriesId, seriesName, + * geoIndex, geoId, geoName, + * bmapIndex, bmapId, bmapName, + * xAxisIndex, xAxisId, xAxisName, + * yAxisIndex, yAxisId, yAxisName, + * gridIndex, gridId, gridName, + * ... (can be extended) + * } + * Each properties can be number|string|Array.|Array. + * For example, a finder could be + * { + * seriesIndex: 3, + * geoId: ['aa', 'cc'], + * gridName: ['xx', 'rr'] + * } + * xxxIndex can be set as 'all' (means all xxx) or 'none' (means not specify) + * If nothing or null/undefined specified, return nothing. + * @param {Object} [opt] + * @param {string} [opt.defaultMainType] + * @param {Array.} [opt.includeMainTypes] + * @return {Object} result like: + * { + * seriesModels: [seriesModel1, seriesModel2], + * seriesModel: seriesModel1, // The first model + * geoModels: [geoModel1, geoModel2], + * geoModel: geoModel1, // The first model + * ... + * } + */ +function parseFinder(ecModel, finder, opt) { + if (isString(finder)) { + var obj = {}; + obj[finder + 'Index'] = 0; + finder = obj; + } + + var defaultMainType = opt && opt.defaultMainType; + if (defaultMainType + && !has(finder, defaultMainType + 'Index') + && !has(finder, defaultMainType + 'Id') + && !has(finder, defaultMainType + 'Name') + ) { + finder[defaultMainType + 'Index'] = 0; + } + + var result = {}; + + each$2(finder, function (value, key) { + var value = finder[key]; + + // Exclude 'dataIndex' and other illgal keys. + if (key === 'dataIndex' || key === 'dataIndexInside') { + result[key] = value; + return; + } + + var parsedKey = key.match(/^(\w+)(Index|Id|Name)$/) || []; + var mainType = parsedKey[1]; + var queryType = (parsedKey[2] || '').toLowerCase(); + + if (!mainType + || !queryType + || value == null + || (queryType === 'index' && value === 'none') + || (opt && opt.includeMainTypes && indexOf(opt.includeMainTypes, mainType) < 0) + ) { + return; + } + + var queryParam = {mainType: mainType}; + if (queryType !== 'index' || value !== 'all') { + queryParam[queryType] = value; + } + + var models = ecModel.queryComponents(queryParam); + result[mainType + 'Models'] = models; + result[mainType + 'Model'] = models[0]; + }); + + return result; +} + +function has(obj, prop) { + return obj && obj.hasOwnProperty(prop); +} + +function setAttribute(dom, key, value) { + dom.setAttribute + ? dom.setAttribute(key, value) + : (dom[key] = value); +} + +function getAttribute(dom, key) { + return dom.getAttribute + ? dom.getAttribute(key) + : dom[key]; +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var TYPE_DELIMITER = '.'; +var IS_CONTAINER = '___EC__COMPONENT__CONTAINER___'; + +/** + * Notice, parseClassType('') should returns {main: '', sub: ''} + * @public + */ +function parseClassType$1(componentType) { + var ret = {main: '', sub: ''}; + if (componentType) { + componentType = componentType.split(TYPE_DELIMITER); + ret.main = componentType[0] || ''; + ret.sub = componentType[1] || ''; + } + return ret; +} + +/** + * @public + */ +function checkClassType(componentType) { + assert$1( + /^[a-zA-Z0-9_]+([.][a-zA-Z0-9_]+)?$/.test(componentType), + 'componentType "' + componentType + '" illegal' + ); +} + +/** + * @public + */ +function enableClassExtend(RootClass, mandatoryMethods) { + + RootClass.$constructor = RootClass; + RootClass.extend = function (proto) { + + if (__DEV__) { + each$1(mandatoryMethods, function (method) { + if (!proto[method]) { + console.warn( + 'Method `' + method + '` should be implemented' + + (proto.type ? ' in ' + proto.type : '') + '.' + ); + } + }); + } + + var superClass = this; + var ExtendedClass = function () { + if (!proto.$constructor) { + superClass.apply(this, arguments); + } + else { + proto.$constructor.apply(this, arguments); + } + }; + + extend(ExtendedClass.prototype, proto); + + ExtendedClass.extend = this.extend; + ExtendedClass.superCall = superCall; + ExtendedClass.superApply = superApply; + inherits(ExtendedClass, this); + ExtendedClass.superClass = superClass; + + return ExtendedClass; + }; +} + +var classBase = 0; + +/** + * Can not use instanceof, consider different scope by + * cross domain or es module import in ec extensions. + * Mount a method "isInstance()" to Clz. + */ +function enableClassCheck(Clz) { + var classAttr = ['__\0is_clz', classBase++, Math.random().toFixed(3)].join('_'); + Clz.prototype[classAttr] = true; + + if (__DEV__) { + assert$1(!Clz.isInstance, 'The method "is" can not be defined.'); + } + + Clz.isInstance = function (obj) { + return !!(obj && obj[classAttr]); + }; +} + +// 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 = 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 + */ +function enableClassManagement(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) { + checkClassType(componentType); + componentType = parseClassType$1(componentType); + + if (!componentType.sub) { + if (__DEV__) { + 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 (componentMainType, subType, throwWhenNotFound) { + var Clazz = storage[componentMainType]; + + if (Clazz && Clazz[IS_CONTAINER]) { + Clazz = subType ? Clazz[subType] : null; + } + + if (throwWhenNotFound && !Clazz) { + throw new Error( + !subType + ? componentMainType + '.' + 'type should be specified.' + : 'Component ' + componentMainType + '.' + (subType || '') + ' not exists. Load it first.' + ); + } + + return Clazz; + }; + + entity.getClassesByMainType = function (componentType) { + componentType = parseClassType$1(componentType); + + var result = []; + var obj = storage[componentType.main]; + + if (obj && obj[IS_CONTAINER]) { + each$1(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$1(componentType); + return !!storage[componentType.main]; + }; + + /** + * @return {Array.} Like ['aa', 'bb'], but can not be ['aa.xx'] + */ + entity.getAllClassMainTypes = function () { + var types = []; + each$1(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$1(componentType); + var obj = storage[componentType.main]; + return obj && obj[IS_CONTAINER]; + }; + + entity.parseClassType = parseClassType$1; + + 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 + */ + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +// TODO Parse shadow style +// TODO Only shallow path support +var makeStyleMapper = function (properties) { + // Normalize + for (var i = 0; i < properties.length; i++) { + if (!properties[i][1]) { + properties[i][1] = properties[i][0]; + } + } + return function (model, excludes, includes) { + var style = {}; + for (var i = 0; i < properties.length; i++) { + var propName = properties[i][1]; + if ((excludes && indexOf(excludes, propName) >= 0) + || (includes && indexOf(includes, propName) < 0) + ) { + continue; + } + var val = model.getShallow(propName); + if (val != null) { + style[properties[i][0]] = val; + } + } + return style; + }; +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var getLineStyle = makeStyleMapper( + [ + ['lineWidth', 'width'], + ['stroke', 'color'], + ['opacity'], + ['shadowBlur'], + ['shadowOffsetX'], + ['shadowOffsetY'], + ['shadowColor'] + ] +); + +var lineStyleMixin = { + getLineStyle: function (excludes) { + var style = getLineStyle(this, excludes); + var lineDash = this.getLineDash(style.lineWidth); + lineDash && (style.lineDash = lineDash); + return style; + }, + + getLineDash: function (lineWidth) { + if (lineWidth == null) { + lineWidth = 1; + } + var lineType = this.get('type'); + var dotSize = Math.max(lineWidth, 2); + var dashSize = lineWidth * 4; + return (lineType === 'solid' || lineType == null) ? null + : (lineType === 'dashed' ? [dashSize, dashSize] : [dotSize, dotSize]); + } +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var getAreaStyle = makeStyleMapper( + [ + ['fill', 'color'], + ['shadowBlur'], + ['shadowOffsetX'], + ['shadowOffsetY'], + ['opacity'], + ['shadowColor'] + ] +); + +var areaStyleMixin = { + getAreaStyle: function (excludes, includes) { + return getAreaStyle(this, excludes, includes); + } +}; + +/** + * 曲线辅助模块 + * @module zrender/core/curve + * @author pissang(https://www.github.com/pissang) + */ + +var mathPow = Math.pow; +var mathSqrt$2 = Math.sqrt; + +var EPSILON$1 = 1e-8; +var EPSILON_NUMERIC = 1e-4; + +var THREE_SQRT = mathSqrt$2(3); +var ONE_THIRD = 1 / 3; + +// 临时变量 +var _v0 = create(); +var _v1 = create(); +var _v2 = create(); + +function isAroundZero(val) { + return val > -EPSILON$1 && val < EPSILON$1; +} +function isNotAroundZero$1(val) { + return val > EPSILON$1 || val < -EPSILON$1; +} +/** + * 计算三次贝塞尔值 + * @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$2(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$2(A * A * A)); + var theta = Math.acos(T) / 3; + var ASqrt = mathSqrt$2(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$1(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$2(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 = distSquare(_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 = distSquare(_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 = distSquare(_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$2(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$1(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$2(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 = distSquare(_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 = distSquare(_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 = distSquare(_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$2(d); +} + +/** + * @author Yi Shen(https://github.com/pissang) + */ + +var mathMin$3 = Math.min; +var mathMax$3 = Math.max; +var mathSin$2 = Math.sin; +var mathCos$2 = Math.cos; +var PI2 = Math.PI * 2; + +var start = create(); +var end = create(); +var extremity = create(); + +/** + * 从顶点数组中计算出最小包围盒,写入`min`和`max`中 + * @module zrender/core/bbox + * @param {Array} points 顶点数组 + * @param {number} min + * @param {number} max + */ +function fromPoints(points, min$$1, max$$1) { + 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$3(left, p[0]); + right = mathMax$3(right, p[0]); + top = mathMin$3(top, p[1]); + bottom = mathMax$3(bottom, p[1]); + } + + min$$1[0] = left; + min$$1[1] = top; + max$$1[0] = right; + max$$1[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 + */ +function fromLine(x0, y0, x1, y1, min$$1, max$$1) { + min$$1[0] = mathMin$3(x0, x1); + min$$1[1] = mathMin$3(y0, y1); + max$$1[0] = mathMax$3(x0, x1); + max$$1[1] = mathMax$3(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 + */ +function fromCubic( + x0, y0, x1, y1, x2, y2, x3, y3, min$$1, max$$1 +) { + var cubicExtrema$$1 = cubicExtrema; + var cubicAt$$1 = cubicAt; + var i; + var n = cubicExtrema$$1(x0, x1, x2, x3, xDim); + min$$1[0] = Infinity; + min$$1[1] = Infinity; + max$$1[0] = -Infinity; + max$$1[1] = -Infinity; + + for (i = 0; i < n; i++) { + var x = cubicAt$$1(x0, x1, x2, x3, xDim[i]); + min$$1[0] = mathMin$3(x, min$$1[0]); + max$$1[0] = mathMax$3(x, max$$1[0]); + } + n = cubicExtrema$$1(y0, y1, y2, y3, yDim); + for (i = 0; i < n; i++) { + var y = cubicAt$$1(y0, y1, y2, y3, yDim[i]); + min$$1[1] = mathMin$3(y, min$$1[1]); + max$$1[1] = mathMax$3(y, max$$1[1]); + } + + min$$1[0] = mathMin$3(x0, min$$1[0]); + max$$1[0] = mathMax$3(x0, max$$1[0]); + min$$1[0] = mathMin$3(x3, min$$1[0]); + max$$1[0] = mathMax$3(x3, max$$1[0]); + + min$$1[1] = mathMin$3(y0, min$$1[1]); + max$$1[1] = mathMax$3(y0, max$$1[1]); + min$$1[1] = mathMin$3(y3, min$$1[1]); + max$$1[1] = mathMax$3(y3, max$$1[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 + */ +function fromQuadratic(x0, y0, x1, y1, x2, y2, min$$1, max$$1) { + var quadraticExtremum$$1 = quadraticExtremum; + var quadraticAt$$1 = quadraticAt; + // Find extremities, where derivative in x dim or y dim is zero + var tx = + mathMax$3( + mathMin$3(quadraticExtremum$$1(x0, x1, x2), 1), 0 + ); + var ty = + mathMax$3( + mathMin$3(quadraticExtremum$$1(y0, y1, y2), 1), 0 + ); + + var x = quadraticAt$$1(x0, x1, x2, tx); + var y = quadraticAt$$1(y0, y1, y2, ty); + + min$$1[0] = mathMin$3(x0, x2, x); + min$$1[1] = mathMin$3(y0, y2, y); + max$$1[0] = mathMax$3(x0, x2, x); + max$$1[1] = mathMax$3(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 + */ +function fromArc( + x, y, rx, ry, startAngle, endAngle, anticlockwise, min$$1, max$$1 +) { + var vec2Min = min; + var vec2Max = max; + + var diff = Math.abs(startAngle - endAngle); + + + if (diff % PI2 < 1e-4 && diff > 1e-4) { + // Is a circle + min$$1[0] = x - rx; + min$$1[1] = y - ry; + max$$1[0] = x + rx; + max$$1[1] = y + ry; + return; + } + + start[0] = mathCos$2(startAngle) * rx + x; + start[1] = mathSin$2(startAngle) * ry + y; + + end[0] = mathCos$2(endAngle) * rx + x; + end[1] = mathSin$2(endAngle) * ry + y; + + vec2Min(min$$1, start, end); + vec2Max(max$$1, 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$2(angle) * rx + x; + extremity[1] = mathSin$2(angle) * ry + y; + + vec2Min(min$$1, extremity, min$$1); + vec2Max(max$$1, extremity, max$$1); + } + } +} + +/** + * Path 代理,可以在`buildPath`中用于替代`ctx`, 会保存每个path操作的命令到pathCommands属性中 + * 可以用于 isInsidePath 判断以及获取boundingRect + * + * @module zrender/core/PathProxy + * @author Yi Shen (http://www.github.com/pissang) + */ + +// TODO getTotalLength, getPointAtLength + +var CMD = { + M: 1, + L: 2, + C: 3, + Q: 4, + A: 5, + Z: 6, + // Rect + R: 7 +}; + +// var CMD_MEM_SIZE = { +// M: 3, +// L: 3, +// C: 7, +// Q: 5, +// A: 9, +// R: 5, +// Z: 1 +// }; + +var min$1 = []; +var max$1 = []; +var min2 = []; +var max2 = []; +var mathMin$2 = Math.min; +var mathMax$2 = Math.max; +var mathCos$1 = Math.cos; +var mathSin$1 = Math.sin; +var mathSqrt$1 = Math.sqrt; +var mathAbs = Math.abs; + +var hasTypedArray = typeof Float32Array != 'undefined'; + +/** + * @alias module:zrender/core/PathProxy + * @constructor + */ +var PathProxy = function (notSaveData) { + + this._saveData = !(notSaveData || false); + + if (this._saveData) { + /** + * Path data. Stored as flat array + * @type {Array.} + */ + this.data = []; + } + + this._ctx = null; +}; + +/** + * 快速计算Path包围盒(并不是最小包围盒) + * @return {Object} + */ +PathProxy.prototype = { + + constructor: PathProxy, + + _xi: 0, + _yi: 0, + + _x0: 0, + _y0: 0, + // Unit x, Unit y. Provide for avoiding drawing that too short line segment + _ux: 0, + _uy: 0, + + _len: 0, + + _lineDash: null, + + _dashOffset: 0, + + _dashIdx: 0, + + _dashSum: 0, + + /** + * @readOnly + */ + setScale: function (sx, sy) { + this._ux = mathAbs(1 / devicePixelRatio / sx) || 0; + this._uy = mathAbs(1 / devicePixelRatio / 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 + if (this._saveData) { + 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$1(endAngle) * r + cx; + this._yi = mathSin$1(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$$1 = data.length; + + if (! (this.data && this.data.length == len$$1) && hasTypedArray) { + this.data = new Float32Array(len$$1); + } + + for (var i = 0; i < len$$1; i++) { + this.data[i] = data[i]; + } + + this._len = len$$1; + }, + + /** + * 添加子路径 + * @param {module:zrender/core/PathProxy|Array.} path + */ + appendPath: function (path) { + if (!(path instanceof Array)) { + path = [path]; + } + var len$$1 = path.length; + var appendSize = 0; + var offset = this._len; + for (var i = 0; i < len$$1; i++) { + appendSize += path[i].len(); + } + if (hasTypedArray && (this.data instanceof Float32Array)) { + this.data = new Float32Array(offset + appendSize); + } + for (var i = 0; i < len$$1; 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) { + if (!this._saveData) { + return; + } + + 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$$1 = mathSqrt$1(dx * dx + dy * dy); + var x = x0; + var y = y0; + var dash; + var nDash = lineDash.length; + var idx; + dx /= dist$$1; + dy /= dist$$1; + + 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$2(x, x1) : mathMax$2(x, x1), + dy >= 0 ? mathMin$2(y, y1) : mathMax$2(y, y1) + ); + } + // Offset for next lineTo + dx = x - x1; + dy = y - y1; + this._dashOffset = -mathSqrt$1(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$$1 = 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$$1(x0, x1, x2, x3, t + 0.1) + - cubicAt$$1(x0, x1, x2, x3, t); + dy = cubicAt$$1(y0, y1, y2, y3, t + 0.1) + - cubicAt$$1(y0, y1, y2, y3, t); + bezierLen += mathSqrt$1(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$$1(x0, x1, x2, x3, t); + y = cubicAt$$1(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$1(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$1[0] = min$1[1] = min2[0] = min2[1] = Number.MAX_VALUE; + max$1[0] = max$1[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: + fromLine(xi, yi, data[i], data[i + 1], min2, max2); + xi = data[i++]; + yi = data[i++]; + break; + case CMD.C: + 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: + 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$1(startAngle) * rx + cx; + y0 = mathSin$1(startAngle) * ry + cy; + } + + fromArc( + cx, cy, rx, ry, startAngle, endAngle, + anticlockwise, min2, max2 + ); + + xi = mathCos$1(endAngle) * rx + cx; + yi = mathSin$1(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 + fromLine(x0, y0, x0 + width, y0 + height, min2, max2); + break; + case CMD.Z: + xi = x0; + yi = y0; + break; + } + + // Union + min(min$1, min$1, min2); + max(max$1, max$1, max2); + } + + // No data + if (i === 0) { + min$1[0] = min$1[1] = max$1[0] = max$1[1] = 0; + } + + return new BoundingRect( + min$1[0], min$1[1], max$1[0] - min$1[0], max$1[1] - min$1[1] + ); + }, + + /** + * Rebuild path from current data + * Rebuild path will not consider javascript implemented line dash. + * @param {CanvasRenderingContext2D} 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$$1 = this._len; + for (var i = 0; i < len$$1;) { + 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 - 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$1(theta) * rx + cx; + y0 = mathSin$1(theta) * ry + cy; + } + xi = mathCos$1(endAngle) * rx + cx; + yi = mathSin$1(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; + +/** + * 线段包含判断 + * @param {number} x0 + * @param {number} y0 + * @param {number} x1 + * @param {number} y1 + * @param {number} lineWidth + * @param {number} x + * @param {number} y + * @return {boolean} + */ +function containStroke$1(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; +} + +/** + * 三次贝塞尔曲线描边包含判断 + * @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} + */ +function containStroke$2(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 = cubicProjectPoint( + x0, y0, x1, y1, x2, y2, x3, y3, + x, y, null + ); + return d <= _l / 2; +} + +/** + * 二次贝塞尔曲线描边包含判断 + * @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} + */ +function containStroke$3(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 = quadraticProjectPoint( + x0, y0, x1, y1, x2, y2, + x, y, null + ); + return d <= _l / 2; +} + +var PI2$3 = Math.PI * 2; + +function normalizeRadian(angle) { + angle %= PI2$3; + if (angle < 0) { + angle += PI2$3; + } + return angle; +} + +var PI2$2 = Math.PI * 2; + +/** + * 圆弧描边包含判断 + * @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} + */ +function containStroke$4( + 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$2 < 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$2; + } + + var angle = Math.atan2(y, x); + if (angle < 0) { + angle += PI2$2; + } + return (angle >= startAngle && angle <= endAngle) + || (angle + PI2$2 >= startAngle && angle + PI2$2 <= endAngle); +} + +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; + + // If (x, y) on the line, considered as "contain". + return x_ === x ? Infinity : x_ > x ? dir : 0; +} + +var CMD$1 = PathProxy.CMD; +var PI2$1 = Math.PI * 2; + +var EPSILON$2 = 1e-4; + +function isAroundEqual(a, b) { + return Math.abs(a - b) < EPSILON$2; +} + +// 临时数组 +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 = 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_ = cubicAt(x0, x1, x2, x3, t); + if (x_ < x) { // Quick reject + continue; + } + if (nExtrema < 0) { + nExtrema = cubicExtrema(y0, y1, y2, y3, extrema); + if (extrema[1] < extrema[0] && nExtrema > 1) { + swapExtrema(); + } + y0_ = cubicAt(y0, y1, y2, y3, extrema[0]); + if (nExtrema > 1) { + y1_ = 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 = quadraticRootAt(y0, y1, y2, y, roots); + if (nRoots === 0) { + return 0; + } + else { + var t = quadraticExtremum(y0, y1, y2); + if (t >= 0 && t <= 1) { + var w = 0; + var y_ = 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_ = 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_ = 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$1 < 1e-4) { + // Is a circle + startAngle = 0; + endAngle = PI2$1; + 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$1; + } + + 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$1 + angle; + } + if ( + (angle >= startAngle && angle <= endAngle) + || (angle + PI2$1 >= startAngle && angle + PI2$1 <= 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$1.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$1.M: + // moveTo 命令重新创建一个新的 subpath, 并且更新新的起点 + // 在 closePath 的时候使用 + x0 = data[i++]; + y0 = data[i++]; + xi = x0; + yi = y0; + break; + case CMD$1.L: + if (isStroke) { + if (containStroke$1(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$1.C: + if (isStroke) { + if (containStroke$2(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$1.Q: + if (isStroke) { + if (containStroke$3(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$1.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 (containStroke$4( + 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$1.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$1(x0, y0, x1, y0, lineWidth, x, y) + || containStroke$1(x1, y0, x1, y1, lineWidth, x, y) + || containStroke$1(x1, y1, x0, y1, lineWidth, x, y) + || containStroke$1(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$1.Z: + if (isStroke) { + if (containStroke$1( + 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; +} + +function contain(pathData, x, y) { + return containPath(pathData, 0, false, x, y); +} + +function containStroke(pathData, lineWidth, x, y) { + return containPath(pathData, lineWidth, true, x, y); +} + +var getCanvasPattern = Pattern.prototype.getCanvasPattern; + +var abs = Math.abs; + +var pathProxyForDraw = new PathProxy(true); +/** + * @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 = null; +} + +Path.prototype = { + + constructor: Path, + + type: 'path', + + __dirtyPath: true, + + strokeContainThreshold: 5, + + brush: function (ctx, prevEl) { + var style = this.style; + var path = this.path || pathProxyForDraw; + 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; + // Update gradient because bounding rect may changed + if (hasFillGradient) { + rect = rect || this.getBoundingRect(); + this._fillGradient = style.getGradient(ctx, fill, rect); + } + if (hasStrokeGradient) { + rect = rect || this.getBoundingRect(); + 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.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 + if (this.path) { + 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([]); + } + + // Draw rect text + if (style.text != null) { + // Only restore transform when needs draw text. + this.restoreTransform(ctx); + 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) {}, + + createPathProxy: function () { + this.path = new PathProxy(); + }, + + getBoundingRect: function () { + var rect = this._rect; + var style = this.style; + var needsUpdateRect = !rect; + if (needsUpdateRect) { + var path = this.path; + if (!path) { + // Create path on demand. + path = this.path = new PathProxy(); + } + 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 (containStroke( + pathData, lineWidth / lineScale, x, y + )) { + return true; + } + } + } + if (style.hasFill()) { + return 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 (isObject$1(key)) { + for (var name in key) { + if (key.hasOwnProperty(name)) { + 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$$1) { + var Sub = function (opts) { + Path.call(this, opts); + + if (defaults$$1.style) { + // Extend default style + this.style.extendFrom(defaults$$1.style, false); + } + + // Extend default shape + var defaultShape = defaults$$1.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$$1.init && defaults$$1.init.call(this, opts); + }; + + inherits(Sub, Path); + + // FIXME 不能 extend position, rotation 等引用对象 + for (var name in defaults$$1) { + // Extending prototype values and methods + if (name !== 'style' && name !== 'shape') { + Sub.prototype[name] = defaults$$1[name]; + } + } + + return Sub; +}; + +inherits(Path, Displayable); + +var CMD$2 = PathProxy.CMD; + +var points = [[], [], []]; +var mathSqrt$3 = Math.sqrt; +var mathAtan2 = Math.atan2; + +var transformPath = function (path, m) { + var data = path.data; + var cmd; + var nPoint; + var i; + var j; + var k; + var p; + + var M = CMD$2.M; + var C = CMD$2.C; + var L = CMD$2.L; + var R = CMD$2.R; + var A = CMD$2.A; + var Q = CMD$2.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$3(m[0] * m[0] + m[1] * m[1]); + var sy = mathSqrt$3(m[2] * m[2] + m[3] * m[3]); + var angle = mathAtan2(-m[1] / sy, m[0] / sx); + // cx + data[i] *= sx; + data[i++] += x; + // cy + data[i] *= sy; + 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++]; + applyTransform(p, p, m); + data[j++] = p[0]; + data[j++] = p[1]; + // x1, y1 + p[0] += data[i++]; + p[1] += data[i++]; + applyTransform(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++]; + + applyTransform(p, p, m); + // Write back + data[j++] = p[0]; + data[j++] = p[1]; + } + } +}; + +// 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); + opts = opts || {}; + opts.buildPath = function (path) { + if (path.setData) { + path.setData(pathProxy.data); + // Svg and vml renderer don't have context + var ctx = path.getContext(); + if (ctx) { + path.rebuildPath(ctx); + } + } + else { + var ctx = path; + pathProxy.rebuildPath(ctx); + } + }; + + opts.applyTransform = function (m) { + transformPath(pathProxy, m); + this.dirty(true); + }; + + return opts; +} + +/** + * Create a Path object from path string data + * http://www.w3.org/TR/SVG/paths.html#PathData + * @param {Object} opts Other options + */ +function createFromString(str, opts) { + return new Path(createPathOptions(str, opts)); +} + +/** + * Create a Path class from path string data + * @param {string} str + * @param {Object} opts Other options + */ +function extendFromString(str, opts) { + return Path.extend(createPathOptions(str, opts)); +} + +/** + * Merge multiple paths + */ +// TODO Apply transform +// TODO stroke dash +// TODO Optimize double memory cost problem +function mergePath$1(pathEls, opts) { + var pathList = []; + var len = pathEls.length; + for (var i = 0; i < len; i++) { + var pathEl = pathEls[i]; + if (!pathEl.path) { + pathEl.createPathProxy(); + } + if (pathEl.__dirtyPath) { + pathEl.buildPath(pathEl.path, pathEl.shape, true); + } + pathList.push(pathEl.path); + } + + var pathBundle = new Path(opts); + // Need path proxy. + pathBundle.createPathProxy(); + 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; +} + +/** + * @alias zrender/graphic/Text + * @extends module:zrender/graphic/Displayable + * @constructor + * @param {Object} opts + */ +var Text = function (opts) { // jshint ignore:line + Displayable.call(this, opts); +}; + +Text.prototype = { + + constructor: Text, + + type: 'text', + + brush: function (ctx, prevEl) { + var style = this.style; + + // Optimize, avoid normalize every time. + this.__dirty && normalizeTextStyle(style, true); + + // Use props with prefix 'text'. + style.fill = style.stroke = style.shadowBlur = style.shadowColor = + style.shadowOffsetX = style.shadowOffsetY = null; + + var text = style.text; + // Convert to string + text != null && (text += ''); + + // Always bind style + style.bind(ctx, this, prevEl); + + if (!needDrawText(text, style)) { + return; + } + + this.setTransform(ctx); + + renderText(this, ctx, text, style); + + this.restoreTransform(ctx); + }, + + getBoundingRect: function () { + var style = this.style; + + // Optimize, avoid normalize every time. + this.__dirty && normalizeTextStyle(style, true); + + if (!this._rect) { + var text = style.text; + text != null ? (text += '') : (text = ''); + + var rect = getBoundingRect( + style.text + '', + style.font, + style.textAlign, + style.textVerticalAlign, + style.textPadding, + style.rich + ); + + rect.x += style.x || 0; + rect.y += style.y || 0; + + if (getStroke(style.textStroke, style.textStrokeWidth)) { + var w = style.textStrokeWidth; + rect.x -= w / 2; + rect.y -= w / 2; + rect.width += w; + rect.height += w; + } + + this._rect = rect; + } + + return this._rect; + } +}; + +inherits(Text, Displayable); + +/** + * 圆形 + * @module zrender/shape/Circle + */ + +var Circle = Path.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); + } + // else { + // if (ctx.allocate && !ctx.data.length) { + // ctx.allocate(ctx.CMD_MEM_SIZE.A); + // } + // } + // 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); + } +}); + +// Fix weird bug in some version of IE11 (like 11.0.9600.178**), +// where exception "unexpected call to method or property access" +// might be thrown when calling ctx.fill or ctx.stroke after a path +// whose area size is zero is drawn and ctx.clip() is called and +// shadowBlur is set. See #4572, #3112, #5777. +// (e.g., +// ctx.moveTo(10, 10); +// ctx.lineTo(20, 10); +// ctx.closePath(); +// ctx.clip(); +// ctx.shadowBlur = 10; +// ... +// ctx.fill(); +// ) + +var shadowTemp = [ + ['shadowBlur', 0], + ['shadowColor', '#000'], + ['shadowOffsetX', 0], + ['shadowOffsetY', 0] +]; + +var fixClipWithShadow = function (orignalBrush) { + + // version string can be: '11.0' + return (env$1.browser.ie && env$1.browser.version >= 11) + + ? function () { + var clipPaths = this.__clipPaths; + var style = this.style; + var modified; + + if (clipPaths) { + for (var i = 0; i < clipPaths.length; i++) { + var clipPath = clipPaths[i]; + var shape = clipPath && clipPath.shape; + var type = clipPath && clipPath.type; + + if (shape && ( + (type === 'sector' && shape.startAngle === shape.endAngle) + || (type === 'rect' && (!shape.width || !shape.height)) + )) { + for (var j = 0; j < shadowTemp.length; j++) { + // It is save to put shadowTemp static, because shadowTemp + // will be all modified each item brush called. + shadowTemp[j][2] = style[shadowTemp[j][0]]; + style[shadowTemp[j][0]] = shadowTemp[j][1]; + } + modified = true; + break; + } + } + } + + orignalBrush.apply(this, arguments); + + if (modified) { + for (var j = 0; j < shadowTemp.length; j++) { + style[shadowTemp[j][0]] = shadowTemp[j][2]; + } + } + } + + : orignalBrush; +}; + +/** + * 扇形 + * @module zrender/graphic/shape/Sector + */ + +var Sector = Path.extend({ + + type: 'sector', + + shape: { + + cx: 0, + + cy: 0, + + r0: 0, + + r: 0, + + startAngle: 0, + + endAngle: Math.PI * 2, + + clockwise: true + }, + + brush: fixClipWithShadow(Path.prototype.brush), + + 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(); + } +}); + +/** + * 圆环 + * @module zrender/graphic/shape/Ring + */ + +var Ring = Path.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); + } +}); + +/** + * 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) + */ + +/** + * @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} + */ +var smoothSpline = function (points, isLoop) { + var len$$1 = points.length; + var ret = []; + + var distance$$1 = 0; + for (var i = 1; i < len$$1; i++) { + distance$$1 += distance(points[i - 1], points[i]); + } + + var segs = distance$$1 / 2; + segs = segs < len$$1 ? len$$1 : segs; + for (var i = 0; i < segs; i++) { + var pos = i / (segs - 1) * (isLoop ? len$$1 : len$$1 - 1); + var idx = Math.floor(pos); + + var w = pos - idx; + + var p0; + var p1 = points[idx % len$$1]; + var p2; + var p3; + if (!isLoop) { + p0 = points[idx === 0 ? idx : idx - 1]; + p2 = points[idx > len$$1 - 2 ? len$$1 - 1 : idx + 1]; + p3 = points[idx > len$$1 - 3 ? len$$1 - 1 : idx + 2]; + } + else { + p0 = points[(idx - 1 + len$$1) % len$$1]; + p2 = points[(idx + 1) % len$$1]; + p3 = points[(idx + 2) % len$$1]; + } + + 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; +}; + +/** + * 贝塞尔平滑曲线 + * @module zrender/shape/util/smoothBezier + * @author pissang (https://www.github.com/pissang) + * Kener (@Kener-林峰, kener.linfeng@gmail.com) + * errorrik (errorrik@gmail.com) + */ + +/** + * 贝塞尔平滑曲线 + * @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} 计算出来的控制点数组 + */ +var smoothBezier = function (points, smooth, isLoop, constraint) { + var cps = []; + + var v = []; + var v1 = []; + var v2 = []; + var prevPoint; + var nextPoint; + + var min$$1, max$$1; + if (constraint) { + min$$1 = [Infinity, Infinity]; + max$$1 = [-Infinity, -Infinity]; + for (var i = 0, len$$1 = points.length; i < len$$1; i++) { + min(min$$1, min$$1, points[i]); + max(max$$1, max$$1, points[i]); + } + // 与指定的包围盒做并集 + min(min$$1, min$$1, constraint[0]); + max(max$$1, max$$1, constraint[1]); + } + + for (var i = 0, len$$1 = points.length; i < len$$1; i++) { + var point = points[i]; + + if (isLoop) { + prevPoint = points[i ? i - 1 : len$$1 - 1]; + nextPoint = points[(i + 1) % len$$1]; + } + else { + if (i === 0 || i === len$$1 - 1) { + cps.push(clone$1(points[i])); + continue; + } + else { + prevPoint = points[i - 1]; + nextPoint = points[i + 1]; + } + } + + sub(v, nextPoint, prevPoint); + + // use degree to scale the handle length + scale(v, v, smooth); + + var d0 = distance(point, prevPoint); + var d1 = distance(point, nextPoint); + var sum = d0 + d1; + if (sum !== 0) { + d0 /= sum; + d1 /= sum; + } + + scale(v1, v, -d0); + scale(v2, v, d1); + var cp0 = add([], point, v1); + var cp1 = add([], point, v2); + if (constraint) { + max(cp0, cp0, min$$1); + min(cp0, cp0, max$$1); + max(cp1, cp1, min$$1); + min(cp1, cp1, max$$1); + } + cps.push(cp0); + cps.push(cp1); + } + + if (isLoop) { + cps.push(cps.shift()); + } + + return cps; +}; + +function buildPath$1(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(); + } +} + +/** + * 多边形 + * @module zrender/shape/Polygon + */ + +var Polygon = Path.extend({ + + type: 'polygon', + + shape: { + points: null, + + smooth: false, + + smoothConstraint: null + }, + + buildPath: function (ctx, shape) { + buildPath$1(ctx, shape, true); + } +}); + +/** + * @module zrender/graphic/shape/Polyline + */ + +var Polyline = Path.extend({ + + type: 'polyline', + + shape: { + points: null, + + smooth: false, + + smoothConstraint: null + }, + + style: { + stroke: '#000', + + fill: null + }, + + buildPath: function (ctx, shape) { + buildPath$1(ctx, shape, false); + } +}); + +/** + * 矩形 + * @module zrender/graphic/shape/Rect + */ + +var Rect = Path.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 { + buildPath(ctx, shape); + } + ctx.closePath(); + return; + } +}); + +/** + * 直线 + * @module zrender/graphic/shape/Line + */ + +var Line = Path.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 + ]; + } +}); + +/** + * 贝塞尔曲线 + * @module zrender/shape/BezierCurve + */ + +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) + ]; + } +} + +var BezierCurve = Path.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 normalize(p, p); + } +}); + +/** + * 圆弧 + * @module zrender/graphic/shape/Arc + */ + +var Arc = Path.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); + } +}); + +// CompoundPath to improve performance + +var CompoundPath = 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++) { + if (!paths[i].path) { + paths[i].createPathProxy(); + } + 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); + } +}); + +/** + * @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 + }); + } + +}; + +/** + * 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) { + // Should do nothing more in this constructor. Because gradient can be + // declard by `color: {type: 'linear', colorStops: ...}`, where + // this constructor will not be called. + + 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 +}; + +inherits(LinearGradient, Gradient); + +/** + * 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) { + // Should do nothing more in this constructor. Because gradient can be + // declard by `color: {type: 'radial', colorStops: ...}`, where + // this constructor will not be called. + + 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 +}; + +inherits(RadialGradient, Gradient); + +/** + * Displayable for incremental rendering. It will be rendered in a separate layer + * IncrementalDisplay have too main methods. `clearDisplayables` and `addDisplayables` + * addDisplayables will render the added displayables incremetally. + * + * It use a not clearFlag to tell the painter don't clear the layer if it's the first element. + */ +// TODO Style override ? +function IncrementalDisplayble(opts) { + + Displayable.call(this, opts); + + this._displayables = []; + + this._temporaryDisplayables = []; + + this._cursor = 0; + + this.notClear = true; +} + +IncrementalDisplayble.prototype.incremental = true; + +IncrementalDisplayble.prototype.clearDisplaybles = function () { + this._displayables = []; + this._temporaryDisplayables = []; + this._cursor = 0; + this.dirty(); + + this.notClear = false; +}; + +IncrementalDisplayble.prototype.addDisplayable = function (displayable, notPersistent) { + if (notPersistent) { + this._temporaryDisplayables.push(displayable); + } + else { + this._displayables.push(displayable); + } + this.dirty(); +}; + +IncrementalDisplayble.prototype.addDisplayables = function (displayables, notPersistent) { + notPersistent = notPersistent || false; + for (var i = 0; i < displayables.length; i++) { + this.addDisplayable(displayables[i], notPersistent); + } +}; + +IncrementalDisplayble.prototype.eachPendingDisplayable = function (cb) { + for (var i = this._cursor; i < this._displayables.length; i++) { + cb && cb(this._displayables[i]); + } + for (var i = 0; i < this._temporaryDisplayables.length; i++) { + cb && cb(this._temporaryDisplayables[i]); + } +}; + +IncrementalDisplayble.prototype.update = function () { + this.updateTransform(); + for (var i = this._cursor; i < this._displayables.length; i++) { + var displayable = this._displayables[i]; + // PENDING + displayable.parent = this; + displayable.update(); + displayable.parent = null; + } + for (var i = 0; i < this._temporaryDisplayables.length; i++) { + var displayable = this._temporaryDisplayables[i]; + // PENDING + displayable.parent = this; + displayable.update(); + displayable.parent = null; + } +}; + +IncrementalDisplayble.prototype.brush = function (ctx, prevEl) { + // Render persistant displayables. + for (var i = this._cursor; i < this._displayables.length; i++) { + var displayable = this._displayables[i]; + displayable.beforeBrush && displayable.beforeBrush(ctx); + displayable.brush(ctx, i === this._cursor ? null : this._displayables[i - 1]); + displayable.afterBrush && displayable.afterBrush(ctx); + } + this._cursor = i; + // Render temporary displayables. + for (var i = 0; i < this._temporaryDisplayables.length; i++) { + var displayable = this._temporaryDisplayables[i]; + displayable.beforeBrush && displayable.beforeBrush(ctx); + displayable.brush(ctx, i === 0 ? null : this._temporaryDisplayables[i - 1]); + displayable.afterBrush && displayable.afterBrush(ctx); + } + + this._temporaryDisplayables = []; + + this.notClear = true; +}; + +var m = []; +IncrementalDisplayble.prototype.getBoundingRect = function () { + if (!this._rect) { + var rect = new BoundingRect(Infinity, Infinity, -Infinity, -Infinity); + for (var i = 0; i < this._displayables.length; i++) { + var displayable = this._displayables[i]; + var childRect = displayable.getBoundingRect().clone(); + if (displayable.needLocalTransform()) { + childRect.applyTransform(displayable.getLocalTransform(m)); + } + rect.union(childRect); + } + this._rect = rect; + } + return this._rect; +}; + +IncrementalDisplayble.prototype.contain = function (x, y) { + var localPos = this.transformCoordToLocal(x, y); + var rect = this.getBoundingRect(); + + if (rect.contain(localPos[0], localPos[1])) { + for (var i = 0; i < this._displayables.length; i++) { + var displayable = this._displayables[i]; + if (displayable.contain(x, y)) { + return true; + } + } + } + return false; +}; + +inherits(IncrementalDisplayble, Displayable); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var round = Math.round; +var mathMax$1 = Math.max; +var mathMin$1 = Math.min; + +var EMPTY_OBJ = {}; + +/** + * Extend shape with parameters + */ +function extendShape(opts) { + return Path.extend(opts); +} + +/** + * Extend path + */ +function extendPath(pathData, opts) { + return 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' + */ +function makePath(pathData, opts, rect, layout) { + var path = createFromString(pathData, opts); + var boundingRect = path.getBoundingRect(); + if (rect) { + if (layout === 'center') { + rect = centerGraphic(rect, boundingRect); + } + + resizePath(path, rect); + } + return path; +} + +/** + * Create a image element from image url + * @param {string} imageUrl image url + * @param {Object} opts options + * @param {module:zrender/core/BoundingRect} rect constrain rect + * @param {string} [layout=cover] 'center' or 'cover' + */ +function makeImage(imageUrl, rect, layout) { + var path = new ZImage({ + style: { + image: imageUrl, + x: rect.x, + y: rect.y, + width: rect.width, + height: rect.height + }, + onload: function (img) { + if (layout === 'center') { + var boundingRect = { + width: img.width, + height: img.height + }; + path.setStyle(centerGraphic(rect, boundingRect)); + } + } + }); + return path; +} + +/** + * Get position of centered element in bounding box. + * + * @param {Object} rect element local bounding box + * @param {Object} boundingRect constraint bounding box + * @return {Object} element position containing x, y, width, and height + */ +function centerGraphic(rect, boundingRect) { + // Set rect to center, keep width / height ratio. + var aspect = boundingRect.width / boundingRect.height; + 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; + + return { + x: cx - width / 2, + y: cy - height / 2, + width: width, + height: height + }; +} + +var mergePath = mergePath$1; + +/** + * Resize a path to fit the rect + * @param {module:zrender/graphic/Path} path + * @param {Object} rect + */ +function resizePath(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 + */ +function subPixelOptimizeLine(param) { + 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 + */ +function subPixelOptimizeRect(param) { + 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. + */ +function subPixelOptimize(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' ? 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) { + // See comment in `doSingleEnterHover`. + if (hoverStyle[name] != null) { + 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 { + var style = el.style; + var insideRollbackOpt = style.insideRollbackOpt; + + // Consider case: only `position: 'top'` is set on emphasis, then text + // color should be returned to `autoColor`, rather than remain '#fff'. + // So we should rollback then apply again after style merging. + insideRollbackOpt && rollbackInsideStyle(style); + + // styles can be: + // { + // label: { + // show: false, + // position: 'outside', + // fontSize: 18 + // }, + // emphasis: { + // label: { + // show: true + // } + // } + // }, + // where properties of `emphasis` may not appear in `normal`. We previously use + // module:echarts/util/model#defaultEmphasis to merge `normal` to `emphasis`. + // But consider rich text and setOption in merge mode, it is impossible to cover + // all properties in merge. So we use merge mode when setting style here, where + // only properties that is not `null/undefined` can be set. The disadventage: + // null/undefined can not be used to remove style any more in `emphasis`. + style.extendFrom(el.__hoverStl); + + // Do not save `insideRollback`. + if (insideRollbackOpt) { + applyInsideStyle(style, style.insideOriginalTextPosition, insideRollbackOpt); + + // textFill may be rollbacked to null. + if (style.textFill == null) { + style.textFill = insideRollbackOpt.autoColor; + } + } + + el.dirty(false); + 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 { + // Consider null/undefined value, should use + // `setStyle` but not `extendFrom(stl, true)`. + 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(e) { + if (this.__hoverSilentOnTouch && e.zrByTouch) { + return; + } + + // Only if element is not in emphasis status + !this.__isEmphasis && doEnterHover(this); +} + +/** + * @inner + */ +function onElementMouseOut(e) { + if (this.__hoverSilentOnTouch && e.zrByTouch) { + return; + } + + // 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. + * This method can be called repeatly without side-effects. + * @param {module:zrender/Element} el + * @param {Object} [hoverStyle] + * @param {Object} [opt] + * @param {boolean} [opt.hoverSilentOnTouch=false] + * In touch device, mouseover event will be trigger on touchstart event + * (see module:zrender/dom/HandlerProxy). By this mechanism, we can + * conviniently use hoverStyle when tap on touch screen without additional + * code for compatibility. + * But if the chart/component has select feature, which usually also use + * hoverStyle, there might be conflict between 'select-highlight' and + * 'hover-highlight' especially when roam is enabled (see geo for example). + * In this case, hoverSilentOnTouch should be used to disable hover-highlight + * on touch device. + */ +function setHoverStyle(el, hoverStyle, opt) { + el.__hoverSilentOnTouch = opt && opt.hoverSilentOnTouch; + + el.type === 'group' + ? el.traverse(function (child) { + if (child.type !== 'group') { + setElementHoverStl(child, hoverStyle); + } + }) + : setElementHoverStl(el, hoverStyle); + + // Duplicated function will be auto-ignored, see Eventful.js. + el.on('mouseover', onElementMouseOver) + .on('mouseout', onElementMouseOut); + + // Emphasis, normal can be triggered manually + el.on('emphasis', enterEmphasis) + .on('normal', leaveEmphasis); +} + +/** + * @param {Object|module:zrender/graphic/Style} normalStyle + * @param {Object} emphasisStyle + * @param {module:echarts/model/Model} normalModel + * @param {module:echarts/model/Model} emphasisModel + * @param {Object} opt Check `opt` of `setTextStyleCommon` to find other props. + * @param {string|Function} [opt.defaultText] + * @param {module:echarts/model/Model} [opt.labelFetcher] Fetch text by + * `opt.labelFetcher.getFormattedLabel(opt.labelDataIndex, 'normal'/'emphasis', null, opt.labelDimIndex)` + * @param {module:echarts/model/Model} [opt.labelDataIndex] Fetch text by + * `opt.textFetcher.getFormattedLabel(opt.labelDataIndex, 'normal'/'emphasis', null, opt.labelDimIndex)` + * @param {module:echarts/model/Model} [opt.labelDimIndex] Fetch text by + * `opt.textFetcher.getFormattedLabel(opt.labelDataIndex, 'normal'/'emphasis', null, opt.labelDimIndex)` + * @param {Object} [normalSpecified] + * @param {Object} [emphasisSpecified] + */ +function setLabelStyle( + normalStyle, emphasisStyle, + normalModel, emphasisModel, + opt, + normalSpecified, emphasisSpecified +) { + opt = opt || EMPTY_OBJ; + var labelFetcher = opt.labelFetcher; + var labelDataIndex = opt.labelDataIndex; + var labelDimIndex = opt.labelDimIndex; + + // This scenario, `label.normal.show = true; label.emphasis.show = false`, + // is not supported util someone requests. + + var showNormal = normalModel.getShallow('show'); + var showEmphasis = emphasisModel.getShallow('show'); + + // Consider performance, only fetch label when necessary. + // If `normal.show` is `false` and `emphasis.show` is `true` and `emphasis.formatter` is not set, + // label should be displayed, where text is fetched by `normal.formatter` or `opt.defaultText`. + var baseText; + if (showNormal || showEmphasis) { + if (labelFetcher) { + baseText = labelFetcher.getFormattedLabel(labelDataIndex, 'normal', null, labelDimIndex); + } + if (baseText == null) { + baseText = isFunction$1(opt.defaultText) ? opt.defaultText(labelDataIndex, opt) : opt.defaultText; + } + } + var normalStyleText = showNormal ? baseText : null; + var emphasisStyleText = showEmphasis + ? retrieve2( + labelFetcher + ? labelFetcher.getFormattedLabel(labelDataIndex, 'emphasis', null, labelDimIndex) + : null, + baseText + ) + : null; + + // Optimize: If style.text is null, text will not be drawn. + if (normalStyleText != null || emphasisStyleText != null) { + // Always set `textStyle` even if `normalStyle.text` is null, because default + // values have to be set on `normalStyle`. + // If we set default values on `emphasisStyle`, consider case: + // Firstly, `setOption(... label: {normal: {text: null}, emphasis: {show: true}} ...);` + // Secondly, `setOption(... label: {noraml: {show: true, text: 'abc', color: 'red'} ...);` + // Then the 'red' will not work on emphasis. + setTextStyle(normalStyle, normalModel, normalSpecified, opt); + setTextStyle(emphasisStyle, emphasisModel, emphasisSpecified, opt, true); + } + + normalStyle.text = normalStyleText; + emphasisStyle.text = emphasisStyleText; +} + +/** + * Set basic textStyle properties. + * @param {Object|module:zrender/graphic/Style} textStyle + * @param {module:echarts/model/Model} model + * @param {Object} [specifiedTextStyle] Can be overrided by settings in model. + * @param {Object} [opt] See `opt` of `setTextStyleCommon`. + * @param {boolean} [isEmphasis] + */ +function setTextStyle( + textStyle, textStyleModel, specifiedTextStyle, opt, isEmphasis +) { + setTextStyleCommon(textStyle, textStyleModel, opt, isEmphasis); + specifiedTextStyle && extend(textStyle, specifiedTextStyle); + textStyle.host && textStyle.host.dirty && textStyle.host.dirty(false); + + return textStyle; +} + +/** + * Set text option in the style. + * @deprecated + * @param {Object} textStyle + * @param {module:echarts/model/Model} labelModel + * @param {string|boolean} defaultColor Default text color. + * If set as false, it will be processed as a emphasis style. + */ +function setText(textStyle, labelModel, defaultColor) { + var opt = {isRectText: true}; + var isEmphasis; + + if (defaultColor === false) { + isEmphasis = true; + } + else { + // Support setting color as 'auto' to get visual color. + opt.autoColor = defaultColor; + } + setTextStyleCommon(textStyle, labelModel, opt, isEmphasis); + textStyle.host && textStyle.host.dirty && textStyle.host.dirty(false); +} + +/** + * { + * disableBox: boolean, Whether diable drawing box of block (outer most). + * isRectText: boolean, + * autoColor: string, specify a color when color is 'auto', + * for textFill, textStroke, textBackgroundColor, and textBorderColor. + * If autoColor specified, it is used as default textFill. + * useInsideStyle: + * `true`: Use inside style (textFill, textStroke, textStrokeWidth) + * if `textFill` is not specified. + * `false`: Do not use inside style. + * `null/undefined`: use inside style if `isRectText` is true and + * `textFill` is not specified and textPosition contains `'inside'`. + * forceRich: boolean + * } + */ +function setTextStyleCommon(textStyle, textStyleModel, opt, isEmphasis) { + // Consider there will be abnormal when merge hover style to normal style if given default value. + opt = opt || EMPTY_OBJ; + + if (opt.isRectText) { + var textPosition = textStyleModel.getShallow('position') + || (isEmphasis ? null : 'inside'); + // 'outside' is not a valid zr textPostion value, but used + // in bar series, and magric type should be considered. + textPosition === 'outside' && (textPosition = 'top'); + textStyle.textPosition = textPosition; + textStyle.textOffset = textStyleModel.getShallow('offset'); + var labelRotate = textStyleModel.getShallow('rotate'); + labelRotate != null && (labelRotate *= Math.PI / 180); + textStyle.textRotation = labelRotate; + textStyle.textDistance = retrieve2( + textStyleModel.getShallow('distance'), isEmphasis ? null : 5 + ); + } + + var ecModel = textStyleModel.ecModel; + var globalTextStyle = ecModel && ecModel.option.textStyle; + + // Consider case: + // { + // data: [{ + // value: 12, + // label: { + // rich: { + // // no 'a' here but using parent 'a'. + // } + // } + // }], + // rich: { + // a: { ... } + // } + // } + var richItemNames = getRichItemNames(textStyleModel); + var richResult; + if (richItemNames) { + richResult = {}; + for (var name in richItemNames) { + if (richItemNames.hasOwnProperty(name)) { + // Cascade is supported in rich. + var richTextStyle = textStyleModel.getModel(['rich', name]); + // In rich, never `disableBox`. + setTokenTextStyle(richResult[name] = {}, richTextStyle, globalTextStyle, opt, isEmphasis); + } + } + } + textStyle.rich = richResult; + + setTokenTextStyle(textStyle, textStyleModel, globalTextStyle, opt, isEmphasis, true); + + if (opt.forceRich && !opt.textStyle) { + opt.textStyle = {}; + } + + return textStyle; +} + +// Consider case: +// { +// data: [{ +// value: 12, +// label: { +// rich: { +// // no 'a' here but using parent 'a'. +// } +// } +// }], +// rich: { +// a: { ... } +// } +// } +function getRichItemNames(textStyleModel) { + // Use object to remove duplicated names. + var richItemNameMap; + while (textStyleModel && textStyleModel !== textStyleModel.ecModel) { + var rich = (textStyleModel.option || EMPTY_OBJ).rich; + if (rich) { + richItemNameMap = richItemNameMap || {}; + for (var name in rich) { + if (rich.hasOwnProperty(name)) { + richItemNameMap[name] = 1; + } + } + } + textStyleModel = textStyleModel.parentModel; + } + return richItemNameMap; +} + +function setTokenTextStyle(textStyle, textStyleModel, globalTextStyle, opt, isEmphasis, isBlock) { + // In merge mode, default value should not be given. + globalTextStyle = !isEmphasis && globalTextStyle || EMPTY_OBJ; + + textStyle.textFill = getAutoColor(textStyleModel.getShallow('color'), opt) + || globalTextStyle.color; + textStyle.textStroke = getAutoColor(textStyleModel.getShallow('textBorderColor'), opt) + || globalTextStyle.textBorderColor; + textStyle.textStrokeWidth = retrieve2( + textStyleModel.getShallow('textBorderWidth'), + globalTextStyle.textBorderWidth + ); + + if (!isEmphasis) { + if (isBlock) { + // Always set `insideRollback`, for clearing previous. + var originalTextPosition = textStyle.textPosition; + textStyle.insideRollback = applyInsideStyle(textStyle, originalTextPosition, opt); + // Save original textPosition, because style.textPosition will be repalced by + // real location (like [10, 30]) in zrender. + textStyle.insideOriginalTextPosition = originalTextPosition; + textStyle.insideRollbackOpt = opt; + } + + // Set default finally. + if (textStyle.textFill == null) { + textStyle.textFill = opt.autoColor; + } + } + + // Do not use `getFont` here, because merge should be supported, where + // part of these properties may be changed in emphasis style, and the + // others should remain their original value got from normal style. + textStyle.fontStyle = textStyleModel.getShallow('fontStyle') || globalTextStyle.fontStyle; + textStyle.fontWeight = textStyleModel.getShallow('fontWeight') || globalTextStyle.fontWeight; + textStyle.fontSize = textStyleModel.getShallow('fontSize') || globalTextStyle.fontSize; + textStyle.fontFamily = textStyleModel.getShallow('fontFamily') || globalTextStyle.fontFamily; + + textStyle.textAlign = textStyleModel.getShallow('align'); + textStyle.textVerticalAlign = textStyleModel.getShallow('verticalAlign') + || textStyleModel.getShallow('baseline'); + + textStyle.textLineHeight = textStyleModel.getShallow('lineHeight'); + textStyle.textWidth = textStyleModel.getShallow('width'); + textStyle.textHeight = textStyleModel.getShallow('height'); + textStyle.textTag = textStyleModel.getShallow('tag'); + + if (!isBlock || !opt.disableBox) { + textStyle.textBackgroundColor = getAutoColor(textStyleModel.getShallow('backgroundColor'), opt); + textStyle.textPadding = textStyleModel.getShallow('padding'); + textStyle.textBorderColor = getAutoColor(textStyleModel.getShallow('borderColor'), opt); + textStyle.textBorderWidth = textStyleModel.getShallow('borderWidth'); + textStyle.textBorderRadius = textStyleModel.getShallow('borderRadius'); + + textStyle.textBoxShadowColor = textStyleModel.getShallow('shadowColor'); + textStyle.textBoxShadowBlur = textStyleModel.getShallow('shadowBlur'); + textStyle.textBoxShadowOffsetX = textStyleModel.getShallow('shadowOffsetX'); + textStyle.textBoxShadowOffsetY = textStyleModel.getShallow('shadowOffsetY'); + } + + textStyle.textShadowColor = textStyleModel.getShallow('textShadowColor') + || globalTextStyle.textShadowColor; + textStyle.textShadowBlur = textStyleModel.getShallow('textShadowBlur') + || globalTextStyle.textShadowBlur; + textStyle.textShadowOffsetX = textStyleModel.getShallow('textShadowOffsetX') + || globalTextStyle.textShadowOffsetX; + textStyle.textShadowOffsetY = textStyleModel.getShallow('textShadowOffsetY') + || globalTextStyle.textShadowOffsetY; +} + +function getAutoColor(color, opt) { + return color !== 'auto' ? color : (opt && opt.autoColor) ? opt.autoColor : null; +} + +function applyInsideStyle(textStyle, textPosition, opt) { + var useInsideStyle = opt.useInsideStyle; + var insideRollback; + + if (textStyle.textFill == null + && useInsideStyle !== false + && (useInsideStyle === true + || (opt.isRectText + && textPosition + // textPosition can be [10, 30] + && typeof textPosition === 'string' + && textPosition.indexOf('inside') >= 0 + ) + ) + ) { + insideRollback = { + textFill: null, + textStroke: textStyle.textStroke, + textStrokeWidth: textStyle.textStrokeWidth + }; + textStyle.textFill = '#fff'; + // Consider text with #fff overflow its container. + if (textStyle.textStroke == null) { + textStyle.textStroke = opt.autoColor; + textStyle.textStrokeWidth == null && (textStyle.textStrokeWidth = 2); + } + } + + return insideRollback; +} + +function rollbackInsideStyle(style) { + var insideRollback = style.insideRollback; + if (insideRollback) { + style.textFill = insideRollback.textFill; + style.textStroke = insideRollback.textStroke; + style.textStrokeWidth = insideRollback.textStrokeWidth; + } +} + +function getFont(opt, ecModel) { + // ecModel or default text style model. + var gTextStyleModel = ecModel || ecModel.getModel('textStyle'); + return trim([ + // FIXME in node-canvas fontWeight is before fontStyle + opt.fontStyle || gTextStyleModel && gTextStyleModel.getShallow('fontStyle') || '', + opt.fontWeight || gTextStyleModel && gTextStyleModel.getShallow('fontWeight') || '', + (opt.fontSize || gTextStyleModel && gTextStyleModel.getShallow('fontSize') || 12) + 'px', + opt.fontFamily || gTextStyleModel && gTextStyleModel.getShallow('fontFamily') || 'sans-serif' + ].join(' ')); +} + +function animateOrSetProps(isUpdate, el, props, animatableModel, dataIndex, cb) { + if (typeof dataIndex === 'function') { + cb = dataIndex; + dataIndex = null; + } + // Do not check 'animation' property directly here. Consider this case: + // animation model is an `itemModel`, whose does not have `isAnimationEnabled` + // but its parent model (`seriesModel`) does. + var animationEnabled = animatableModel && animatableModel.isAnimationEnabled(); + + if (animationEnabled) { + var postfix = isUpdate ? 'Update' : ''; + var duration = animatableModel.getShallow('animationDuration' + postfix); + var animationEasing = animatableModel.getShallow('animationEasing' + postfix); + var animationDelay = animatableModel.getShallow('animationDelay' + postfix); + if (typeof animationDelay === 'function') { + animationDelay = animationDelay( + dataIndex, + animatableModel.getAnimationDelayParams + ? animatableModel.getAnimationDelayParams(el, dataIndex) + : null + ); + } + if (typeof duration === 'function') { + duration = duration(dataIndex); + } + + duration > 0 + ? el.animateTo(props, duration, animationDelay || 0, animationEasing, cb, !!cb) + : (el.stopAnimation(), el.attr(props), cb && cb()); + } + else { + el.stopAnimation(); + el.attr(props); + cb && cb(); + } +} + +/** + * Update graphic element properties with or without animation according to the + * configuration in series. + * + * Caution: this method will stop previous animation. + * So if do not use this method to one element twice before + * animation starts, unless you know what you are doing. + * + * @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!'); }); + */ +function updateProps(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. + * + * Caution: this method will stop previous animation. + * So if do not use this method to one element twice before + * animation starts, unless you know what you are doing. + * + * @param {module:zrender/Element} el + * @param {Object} props + * @param {module:echarts/model/Model} [animatableModel] + * @param {number} [dataIndex] + * @param {Function} cb + */ +function initProps(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] + */ +function getTransform(target, ancestor) { + var mat = identity([]); + + while (target && target !== ancestor) { + mul$1(mat, target.getLocalTransform(), mat); + target = target.parent; + } + + return mat; +} + +/** + * Apply transform to an vertex. + * @param {Array.} target [x, y] + * @param {Array.|TypedArray.|Object} transform Can be: + * + Transform matrix: like [1, 0, 0, 1, 0, 0] + * + {position, rotation, scale}, the same as `zrender/Transformable`. + * @param {boolean=} invert Whether use invert matrix. + * @return {Array.} [x, y] + */ +function applyTransform$1(target, transform, invert$$1) { + if (transform && !isArrayLike(transform)) { + transform = Transformable.getLocalTransform(transform); + } + + if (invert$$1) { + transform = invert([], transform); + } + return applyTransform([], target, 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' + */ +function transformDirection(direction, transform, invert$$1) { + + // 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 = applyTransform$1(vertex, transform, invert$$1); + + 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. + * If no animatableModel, no animation. + */ +function groupTransition(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: clone$1(el.position), + rotation: el.rotation + }; + if (el.shape) { + obj.shape = 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)); + updateProps(el, newProp, animatableModel, el.dataIndex); + } + // else { + // if (el.previousProps) { + // graphic.updateProps + // } + // } + } + }); +} + +/** + * @param {Array.>} points Like: [[23, 44], [53, 66], ...] + * @param {Object} rect {x, y, width, height} + * @return {Array.>} A new clipped points. + */ +function clipPointsByRect(points, rect) { + return map(points, function (point) { + var x = point[0]; + x = mathMax$1(x, rect.x); + x = mathMin$1(x, rect.x + rect.width); + var y = point[1]; + y = mathMax$1(y, rect.y); + y = mathMin$1(y, rect.y + rect.height); + return [x, y]; + }); +} + +/** + * @param {Object} targetRect {x, y, width, height} + * @param {Object} rect {x, y, width, height} + * @return {Object} A new clipped rect. If rect size are negative, return undefined. + */ +function clipRectByRect(targetRect, rect) { + var x = mathMax$1(targetRect.x, rect.x); + var x2 = mathMin$1(targetRect.x + targetRect.width, rect.x + rect.width); + var y = mathMax$1(targetRect.y, rect.y); + var y2 = mathMin$1(targetRect.y + targetRect.height, rect.y + rect.height); + + if (x2 >= x && y2 >= y) { + return { + x: x, + y: y, + width: x2 - x, + height: y2 - y + }; + } +} + +/** + * @param {string} iconStr Support 'image://' or 'path://' or direct svg path. + * @param {Object} [opt] Properties of `module:zrender/Element`, except `style`. + * @param {Object} [rect] {x, y, width, height} + * @return {module:zrender/Element} Icon path or image element. + */ +function createIcon(iconStr, opt, rect) { + opt = extend({rectHover: true}, opt); + var style = opt.style = {strokeNoScale: true}; + rect = rect || {x: -1, y: -1, width: 2, height: 2}; + + if (iconStr) { + return iconStr.indexOf('image://') === 0 + ? ( + style.image = iconStr.slice(8), + defaults(style, rect), + new ZImage(opt) + ) + : ( + makePath( + iconStr.replace('path://', ''), + opt, + rect, + 'center' + ) + ); + } +} + + + + +var graphic = (Object.freeze || Object)({ + extendShape: extendShape, + extendPath: extendPath, + makePath: makePath, + makeImage: makeImage, + mergePath: mergePath, + resizePath: resizePath, + subPixelOptimizeLine: subPixelOptimizeLine, + subPixelOptimizeRect: subPixelOptimizeRect, + subPixelOptimize: subPixelOptimize, + setHoverStyle: setHoverStyle, + setLabelStyle: setLabelStyle, + setTextStyle: setTextStyle, + setText: setText, + getFont: getFont, + updateProps: updateProps, + initProps: initProps, + getTransform: getTransform, + applyTransform: applyTransform$1, + transformDirection: transformDirection, + groupTransition: groupTransition, + clipPointsByRect: clipPointsByRect, + clipRectByRect: clipRectByRect, + createIcon: createIcon, + Group: Group, + Image: ZImage, + Text: Text, + Circle: Circle, + Sector: Sector, + Ring: Ring, + Polygon: Polygon, + Polyline: Polyline, + Rect: Rect, + Line: Line, + BezierCurve: BezierCurve, + Arc: Arc, + IncrementalDisplayable: IncrementalDisplayble, + CompoundPath: CompoundPath, + LinearGradient: LinearGradient, + RadialGradient: RadialGradient, + BoundingRect: BoundingRect +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var PATH_COLOR = ['textStyle', 'color']; + +var textStyleMixin = { + /** + * Get color property or get color from option.textStyle.color + * @param {boolean} [isEmphasis] + * @return {string} + */ + getTextColor: function (isEmphasis) { + var ecModel = this.ecModel; + return this.getShallow('color') + || ( + (!isEmphasis && ecModel) ? ecModel.get(PATH_COLOR) : null + ); + }, + + /** + * Create font string from fontStyle, fontWeight, fontSize, fontFamily + * @return {string} + */ + getFont: function () { + return getFont({ + fontStyle: this.getShallow('fontStyle'), + fontWeight: this.getShallow('fontWeight'), + fontSize: this.getShallow('fontSize'), + fontFamily: this.getShallow('fontFamily') + }, this.ecModel); + }, + + getTextRect: function (text) { + return getBoundingRect( + text, + this.getFont(), + this.getShallow('align'), + this.getShallow('verticalAlign') || this.getShallow('baseline'), + this.getShallow('padding'), + this.getShallow('rich'), + this.getShallow('truncateText') + ); + } +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var getItemStyle = makeStyleMapper( + [ + ['fill', 'color'], + ['stroke', 'borderColor'], + ['lineWidth', 'borderWidth'], + ['opacity'], + ['shadowBlur'], + ['shadowOffsetX'], + ['shadowOffsetY'], + ['shadowColor'], + ['textPosition'], + ['textAlign'] + ] +); + +var itemStyleMixin = { + getItemStyle: function (excludes, includes) { + var style = getItemStyle(this, excludes, includes); + 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]); + } +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * @module echarts/model/Model + */ + +var mixin$1 = mixin; +var inner = makeInner(); + +/** + * @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) { + merge(this.option, option, true); + }, + + /** + * @param {string|Array.} path + * @param {boolean} [ignoreParent=false] + * @return {*} + */ + get: function (path, ignoreParent) { + if (path == null) { + return this.option; + } + + return doGet( + this.option, + this.parsePath(path), + !ignoreParent && getParent(this, path) + ); + }, + + /** + * @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 = !ignoreParent && getParent(this, key); + if (val == null && parentModel) { + val = parentModel.getShallow(key); + } + return val; + }, + + /** + * @param {string|Array.} [path] + * @param {module:echarts/model/Model} [parentModel] + * @return {module:echarts/model/Model} + */ + getModel: function (path, parentModel) { + var obj = path == null + ? this.option + : doGet(this.option, path = this.parsePath(path)); + + var thisParentModel; + parentModel = parentModel || ( + (thisParentModel = getParent(this, path)) + && thisParentModel.getModel(path) + ); + + return new Model(obj, parentModel, this.ecModel); + }, + + /** + * If model has option + */ + isEmpty: function () { + return this.option == null; + }, + + restoreData: function () {}, + + // Pending + clone: function () { + var Ctor = this.constructor; + return new Ctor(clone(this.option)); + }, + + setReadOnly: function (properties) { + // clazzUtil.setReadOnly(this, properties); + }, + + // If path is null/undefined, return null/undefined. + parsePath: function(path) { + if (typeof path === 'string') { + path = path.split('.'); + } + return path; + }, + + /** + * @param {Function} getParentMethod + * param {Array.|string} path + * return {module:echarts/model/Model} + */ + customizeGetParent: function (getParentMethod) { + inner(this).getParent = getParentMethod; + }, + + isAnimationEnabled: function () { + if (!env$1.node) { + if (this.option.animation != null) { + return !!this.option.animation; + } + else if (this.parentModel) { + return this.parentModel.isAnimationEnabled(); + } + } + } + +}; + +function doGet(obj, pathArr, parentModel) { + for (var i = 0; i < pathArr.length; i++) { + // Ignore empty + if (!pathArr[i]) { + continue; + } + // obj could be number/string/... (like 0) + obj = (obj && typeof obj === 'object') ? obj[pathArr[i]] : null; + if (obj == null) { + break; + } + } + if (obj == null && parentModel) { + obj = parentModel.get(pathArr); + } + return obj; +} + +// `path` can be null/undefined +function getParent(model, path) { + var getParentMethod = inner(model).getParent; + return getParentMethod ? getParentMethod.call(model, path) : model.parentModel; +} + +// Enable Model.extend. +enableClassExtend(Model); +enableClassCheck(Model); + +mixin$1(Model, lineStyleMixin); +mixin$1(Model, areaStyleMixin); +mixin$1(Model, textStyleMixin); +mixin$1(Model, itemStyleMixin); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var base = 0; + +/** + * @public + * @param {string} type + * @return {string} + */ +function getUID(type) { + // Considering the case of crossing js context, + // use Math.random to make id as unique as possible. + return [(type || ''), base++, Math.random().toFixed(5)].join('_'); +} + +/** + * @inner + */ +function enableSubTypeDefaulter(entity) { + + var subTypeDefaulters = {}; + + entity.registerSubTypeDefaulter = function (componentType, defaulter) { + componentType = parseClassType$1(componentType); + subTypeDefaulters[componentType.main] = defaulter; + }; + + entity.determineSubType = function (componentType, option) { + var type = option.type; + if (!type) { + var componentTypeMain = parseClassType$1(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. + * + */ +function enableTopologicalTravel(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 = {}; + each$1(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]; + } + each$1( + currVertex.successor, + isInTargetNameSet ? removeEdgeAndAdd : removeEdge + ); + } + + each$1(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 = []; + + each$1(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); + } + + each$1(availableDeps, function (dependentName) { + if (indexOf(thisItem.predecessor, dependentName) < 0) { + thisItem.predecessor.push(dependentName); + } + var thatItem = createDependencyGraphItem(graph, dependentName); + if (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 = []; + each$1(originalDeps, function (dep) { + indexOf(fullNameList, dep) >= 0 && availableDeps.push(dep); + }); + return availableDeps; + } +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +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.} + */ +function linearMap(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} + */ +function parsePercent$1(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; +} + +/** + * (1) Fix rounding error of float numbers. + * (2) Support return string to avoid scientific notation like '3.5e-7'. + * + * @param {number} x + * @param {number} [precision] + * @param {boolean} [returnStr] + * @return {number|string} + */ +function round$1(x, precision, returnStr) { + if (precision == null) { + precision = 10; + } + // Avoid range error + precision = Math.min(Math.max(0, precision), 20); + x = (+x).toFixed(precision); + return returnStr ? x : +x; +} + +function asc(arr) { + arr.sort(function (a, b) { + return a - b; + }); + return arr; +} + +/** + * Get precision + * @param {number} val + */ +function getPrecision(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; +} + +/** + * @param {string|number} val + * @return {number} + */ +function getPrecisionSafe(val) { + var str = val.toString(); + + // Consider scientific notation: '3.4e-12' '3.4e+12' + var eIndex = str.indexOf('e'); + if (eIndex > 0) { + var precision = +str.slice(eIndex + 1); + return precision < 0 ? -precision : 0; + } + else { + var dotIndex = str.indexOf('.'); + return dotIndex < 0 ? 0 : str.length - 1 - dotIndex; + } +} + +/** + * Minimal dicernible data precisioin according to a single pixel. + * + * @param {Array.} dataExtent + * @param {Array.} pixelExtent + * @return {number} precision + */ +function getPixelPrecision(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); + // toFixed() digits argument must be between 0 and 20. + var precision = Math.min(Math.max(-dataQuantity + sizeQuantity, 0), 20); + return !isFinite(precision) ? 20 : precision; +} + +/** + * Get a data of given precision, assuring the sum of percentages + * in valueList is 1. + * The largest remainer method is used. + * https://en.wikipedia.org/wiki/Largest_remainder_method + * + * @param {Array.} valueList a list of all data + * @param {number} idx index of the data to be processed in valueList + * @param {number} precision integer number showing digits of precision + * @return {number} percent ranging from 0 to 100 + */ +function getPercentWithPrecision(valueList, idx, precision) { + if (!valueList[idx]) { + return 0; + } + + var sum = reduce(valueList, function (acc, val) { + return acc + (isNaN(val) ? 0 : val); + }, 0); + if (sum === 0) { + return 0; + } + + var digits = Math.pow(10, precision); + var votesPerQuota = map(valueList, function (val) { + return (isNaN(val) ? 0 : val) / sum * digits * 100; + }); + var targetSeats = digits * 100; + + var seats = map(votesPerQuota, function (votes) { + // Assign automatic seats. + return Math.floor(votes); + }); + var currentSum = reduce(seats, function (acc, val) { + return acc + val; + }, 0); + + var remainder = map(votesPerQuota, function (votes, idx) { + return votes - seats[idx]; + }); + + // Has remainding votes. + while (currentSum < targetSeats) { + // Find next largest remainder. + var max = Number.NEGATIVE_INFINITY; + var maxId = null; + for (var i = 0, len = remainder.length; i < len; ++i) { + if (remainder[i] > max) { + max = remainder[i]; + maxId = i; + } + } + + // Add a vote to max remainder. + ++seats[maxId]; + remainder[maxId] = 0; + ++currentSum; + } + + return seats[idx] / digits; +} + +// Number.MAX_SAFE_INTEGER, ie do not support. +var MAX_SAFE_INTEGER = 9007199254740991; + +/** + * To 0 - 2 * PI, considering negative radian. + * @param {number} radian + * @return {number} + */ +function remRadian(radian) { + var pi2 = Math.PI * 2; + return (radian % pi2 + pi2) % pi2; +} + +/** + * @param {type} radian + * @return {boolean} + */ +function isRadianAroundZero(val) { + return val > -RADIAN_EPSILON && val < RADIAN_EPSILON; +} + +var TIME_REG = /^(?:(\d{4})(?:[-\/](\d{1,2})(?:[-\/](\d{1,2})(?:[T ](\d{1,2})(?::(\d\d)(?::(\d\d)(?:[.,](\d+))?)?)?(Z|[\+\-]\d\d:?\d\d)?)?)?)?)?$/; // jshint ignore:line + +/** + * @param {string|Date|number} value These values can be accepted: + * + An instance of Date, represent a time in its own time zone. + * + Or string in a subset of ISO 8601, only including: + * + only year, month, date: '2012-03', '2012-03-01', '2012-03-01 05', '2012-03-01 05:06', + * + separated with T or space: '2012-03-01T12:22:33.123', '2012-03-01 12:22:33.123', + * + time zone: '2012-03-01T12:22:33Z', '2012-03-01T12:22:33+8000', '2012-03-01T12:22:33-05:00', + * all of which will be treated as local time if time zone is not specified + * (see ). + * + Or other string format, including (all of which will be treated as loacal time): + * '2012', '2012-3-1', '2012/3/1', '2012/03/01', + * '2009/6/12 2:00', '2009/6/12 2:05:08', '2009/6/12 2:05:08.123' + * + a timestamp, which represent a time in UTC. + * @return {Date} date + */ +function parseDate(value) { + if (value instanceof Date) { + return value; + } + else if (typeof value === 'string') { + // Different browsers parse date in different way, so we parse it manually. + // Some other issues: + // new Date('1970-01-01') is UTC, + // new Date('1970/01/01') and new Date('1970-1-01') is local. + // See issue #3623 + var match = TIME_REG.exec(value); + + if (!match) { + // return Invalid Date. + return new Date(NaN); + } + + // Use local time when no timezone offset specifed. + if (!match[8]) { + // match[n] can only be string or undefined. + // But take care of '12' + 1 => '121'. + return new Date( + +match[1], + +(match[2] || 1) - 1, + +match[3] || 1, + +match[4] || 0, + +(match[5] || 0), + +match[6] || 0, + +match[7] || 0 + ); + } + // Timezoneoffset of Javascript Date has considered DST (Daylight Saving Time, + // https://tc39.github.io/ecma262/#sec-daylight-saving-time-adjustment). + // For example, system timezone is set as "Time Zone: America/Toronto", + // then these code will get different result: + // `new Date(1478411999999).getTimezoneOffset(); // get 240` + // `new Date(1478412000000).getTimezoneOffset(); // get 300` + // So we should not use `new Date`, but use `Date.UTC`. + else { + var hour = +match[4] || 0; + if (match[8].toUpperCase() !== 'Z') { + hour -= match[8].slice(0, 3); + } + return new Date(Date.UTC( + +match[1], + +(match[2] || 1) - 1, + +match[3] || 1, + hour, + +(match[5] || 0), + +match[6] || 0, + +match[7] || 0 + )); + } + } + else if (value == null) { + return new Date(NaN); + } + + return new Date(Math.round(value)); +} + +/** + * Quantity of a number. e.g. 0.1, 1, 10, 100 + * + * @param {number} val + * @return {number} + */ +function quantity(val) { + return Math.pow(10, quantityExponent(val)); +} + +function quantityExponent(val) { + return Math.floor(Math.log(val) / Math.LN10); +} + +/** + * 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. + * + * See "Nice Numbers for Graph Labels" of Graphic Gems. + * + * @param {number} val Non-negative value. + * @param {boolean} round + * @return {number} + */ +function nice(val, round) { + var exponent = quantityExponent(val); + var exp10 = Math.pow(10, exponent); + var f = val / exp10; // 1 <= f < 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; } + } + val = nf * exp10; + + // Fix 3 * 0.1 === 0.30000000000000004 issue (see IEEE 754). + // 20 is the uppper bound of toFixed. + return exponent >= -20 ? +val.toFixed(exponent < 0 ? -exponent : 0) : val; +} + +/** + * BSD 3-Clause + * + * Copyright (c) 2010-2015, Michael Bostock + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * The name Michael Bostock may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL MICHAEL BOSTOCK BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @see + * @see + * @param {Array.} ascArr + */ +function quantile(ascArr, p) { + var H = (ascArr.length - 1) * p + 1; + var h = Math.floor(H); + var v = +ascArr[h - 1]; + var e = H - h; + return e ? v + e * (ascArr[h] - v) : v; +} + +/** + * Order intervals asc, and split them when overlap. + * expect(numberUtil.reformIntervals([ + * {interval: [18, 62], close: [1, 1]}, + * {interval: [-Infinity, -70], close: [0, 0]}, + * {interval: [-70, -26], close: [1, 1]}, + * {interval: [-26, 18], close: [1, 1]}, + * {interval: [62, 150], close: [1, 1]}, + * {interval: [106, 150], close: [1, 1]}, + * {interval: [150, Infinity], close: [0, 0]} + * ])).toEqual([ + * {interval: [-Infinity, -70], close: [0, 0]}, + * {interval: [-70, -26], close: [1, 1]}, + * {interval: [-26, 18], close: [0, 1]}, + * {interval: [18, 62], close: [0, 1]}, + * {interval: [62, 150], close: [0, 1]}, + * {interval: [150, Infinity], close: [0, 0]} + * ]); + * @param {Array.} list, where `close` mean open or close + * of the interval, and Infinity can be used. + * @return {Array.} The origin list, which has been reformed. + */ +function reformIntervals(list) { + list.sort(function (a, b) { + return littleThan(a, b, 0) ? -1 : 1; + }); + + var curr = -Infinity; + var currClose = 1; + for (var i = 0; i < list.length;) { + var interval = list[i].interval; + var close = list[i].close; + + for (var lg = 0; lg < 2; lg++) { + if (interval[lg] <= curr) { + interval[lg] = curr; + close[lg] = !lg ? 1 - currClose : 1; + } + curr = interval[lg]; + currClose = close[lg]; + } + + if (interval[0] === interval[1] && close[0] * close[1] !== 1) { + list.splice(i, 1); + } + else { + i++; + } + } + + return list; + + function littleThan(a, b, lg) { + return a.interval[lg] < b.interval[lg] + || ( + a.interval[lg] === b.interval[lg] + && ( + (a.close[lg] - b.close[lg] === (!lg ? 1 : -1)) + || (!lg && littleThan(a, b, 1)) + ) + ); + } +} + +/** + * parseFloat NaNs numeric-cast false positives (null|true|false|"") + * ...but misinterprets leading-number strings, particularly hex literals ("0x...") + * subtraction forces infinities to NaN + * + * @param {*} v + * @return {boolean} + */ +function isNumeric(v) { + return v - parseFloat(v) >= 0; +} + + +var number = (Object.freeze || Object)({ + linearMap: linearMap, + parsePercent: parsePercent$1, + round: round$1, + asc: asc, + getPrecision: getPrecision, + getPrecisionSafe: getPrecisionSafe, + getPixelPrecision: getPixelPrecision, + getPercentWithPrecision: getPercentWithPrecision, + MAX_SAFE_INTEGER: MAX_SAFE_INTEGER, + remRadian: remRadian, + isRadianAroundZero: isRadianAroundZero, + parseDate: parseDate, + quantity: quantity, + nice: nice, + quantile: quantile, + reformIntervals: reformIntervals, + isNumeric: isNumeric +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * 每三位默认加,格式化 + * @param {string|number} x + * @return {string} + */ +function addCommas(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 + * @param {boolean} [upperCaseFirst=false] + * @return {string} str + */ +function toCamelCase(str, upperCaseFirst) { + str = (str || '').toLowerCase().replace(/-(.)/g, function(match, group1) { + return group1.toUpperCase(); + }); + + if (upperCaseFirst && str) { + str = str.charAt(0).toUpperCase() + str.slice(1); + } + + return str; +} + +var normalizeCssArray$1 = normalizeCssArray; + + +var replaceReg = /([&<>"'])/g; +var replaceMap = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + '\'': ''' +}; + +function encodeHTML(source) { + return source == null + ? '' + : (source + '').replace(replaceReg, function (str, c) { + return replaceMap[c]; + }); +} + +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 + * @param {boolean} [encode=false] + * @return {string} + */ +function formatTpl(tpl, paramsList, encode) { + if (!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++) { + var val = paramsList[seriesIdx][$vars[k]]; + tpl = tpl.replace( + wrapVar(TPL_VAR_ALIAS[k], seriesIdx), + encode ? encodeHTML(val) : val + ); + } + } + + return tpl; +} + +/** + * simple Template formatter + * + * @param {string} tpl + * @param {Object} param + * @param {boolean} [encode=false] + * @return {string} + */ +function formatTplSimple(tpl, param, encode) { + each$1(param, function (value, key) { + tpl = tpl.replace( + '{' + key + '}', + encode ? encodeHTML(value) : value + ); + }); + return tpl; +} + +/** + * @param {Object|string} [opt] If string, means color. + * @param {string} [opt.color] + * @param {string} [opt.extraCssText] + * @param {string} [opt.type='item'] 'item' or 'subItem' + * @return {string} + */ +function getTooltipMarker(opt, extraCssText) { + opt = isString(opt) ? {color: opt, extraCssText: extraCssText} : (opt || {}); + var color = opt.color; + var type = opt.type; + var extraCssText = opt.extraCssText; + + if (!color) { + return ''; + } + + return type === 'subItem' + ? '' + : ''; +} + +function pad(str, len) { + str += ''; + return '0000'.substr(0, len - str.length) + str; +} + + +/** + * ISO Date format + * @param {string} tpl + * @param {number} value + * @param {boolean} [isUTC=false] Default in local time. + * see `module:echarts/scale/Time` + * and `module:echarts/util/number#parseDate`. + * @inner + */ +function formatTime(tpl, value, isUTC) { + if (tpl === 'week' + || tpl === 'month' + || tpl === 'quarter' + || tpl === 'half-year' + || tpl === 'year' + ) { + tpl = 'MM-dd\nyyyy'; + } + + var date = parseDate(value); + var utc = isUTC ? 'UTC' : ''; + var y = date['get' + utc + 'FullYear'](); + var M = date['get' + utc + 'Month']() + 1; + var d = date['get' + utc + 'Date'](); + var h = date['get' + utc + 'Hours'](); + var m = date['get' + utc + 'Minutes'](); + var s = date['get' + utc + 'Seconds'](); + var S = date['get' + utc + 'Milliseconds'](); + + tpl = tpl.replace('MM', pad(M, 2)) + .replace('M', M) + .replace('yyyy', y) + .replace('yy', y % 100) + .replace('dd', pad(d, 2)) + .replace('d', d) + .replace('hh', pad(h, 2)) + .replace('h', h) + .replace('mm', pad(m, 2)) + .replace('m', m) + .replace('ss', pad(s, 2)) + .replace('s', s) + .replace('SSS', pad(S, 3)); + + return tpl; +} + +/** + * Capital first + * @param {string} str + * @return {string} + */ +function capitalFirst(str) { + return str ? str.charAt(0).toUpperCase() + str.substr(1) : str; +} + +var truncateText$1 = truncateText; + +var getTextRect = getBoundingRect; + + +var format = (Object.freeze || Object)({ + addCommas: addCommas, + toCamelCase: toCamelCase, + normalizeCssArray: normalizeCssArray$1, + encodeHTML: encodeHTML, + formatTpl: formatTpl, + formatTplSimple: formatTplSimple, + getTooltipMarker: getTooltipMarker, + formatTime: formatTime, + capitalFirst: capitalFirst, + truncateText: truncateText$1, + getTextRect: getTextRect +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +// Layout helpers for each component positioning + +var each$3 = each$1; + +/** + * @public + */ +var LOCATION_PARAMS = [ + 'left', 'right', 'top', 'bottom', 'width', 'height' +]; + +/** + * @public + */ +var HV_NAMES = [ + ['width', 'left', 'right'], + ['height', 'top', 'bottom'] +]; + +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 + // FIXME compare before adding gap? + if (nextX > maxWidth || child.newline) { + x = 0; + nextX = moveX; + y += currentLineMaxSize + gap; + currentLineMaxSize = rect.height; + } + else { + // FIXME: consider rect.y is not `0`? + 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] + */ +var box = boxLayout; + +/** + * VBox layouting + * @param {module:zrender/container/Group} group + * @param {number} gap + * @param {number} [width=Infinity] + * @param {number} [height=Infinity] + */ +var vbox = curry(boxLayout, 'vertical'); + +/** + * HBox layouting + * @param {module:zrender/container/Group} group + * @param {number} gap + * @param {number} [width=Infinity] + * @param {number} [height=Infinity] + */ +var hbox = 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 {width, height} + * @param {string|number} margin + * @return {Object} {width, height} + */ +function getAvailableSize(positionInfo, containerRect, margin) { + var containerWidth = containerRect.width; + var containerHeight = containerRect.height; + + var x = parsePercent$1(positionInfo.x, containerWidth); + var y = parsePercent$1(positionInfo.y, containerHeight); + var x2 = parsePercent$1(positionInfo.x2, containerWidth); + var y2 = parsePercent$1(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 = normalizeCssArray$1(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} + */ +function getLayoutRect( + positionInfo, containerRect, margin +) { + margin = normalizeCssArray$1(margin || 0); + + var containerWidth = containerRect.width; + var containerHeight = containerRect.height; + + var left = parsePercent$1(positionInfo.left, containerWidth); + var top = parsePercent$1(positionInfo.top, containerHeight); + var right = parsePercent$1(positionInfo.right, containerWidth); + var bottom = parsePercent$1(positionInfo.bottom, containerHeight); + var width = parsePercent$1(positionInfo.width, containerWidth); + var height = parsePercent$1(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 (aspect != null) { + // 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 + // FIXME + // Margin is not considered, because there is no case that both + // using margin and aspect so far. + if (isNaN(width) && isNaN(height)) { + if (aspect > containerWidth / containerHeight) { + width = containerWidth * 0.8; + } + else { + height = containerHeight * 0.8; + } + } + + // 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 - horizontalMargin - left - (right || 0); + } + if (isNaN(height)) { + // Height may be NaN if only one value is given except height + height = containerHeight - verticalMargin - top - (bottom || 0); + } + + var rect = new BoundingRect(left + margin[3], top + margin[0], width, height); + rect.margin = margin; + return rect; +} + + +/** + * Position a zr element in viewport + * Group position is specified by either + * {left, top}, {right, bottom} + * If all properties exists, right and bottom will be igonred. + * + * Logic: + * 1. Scale (against origin point in parent coord) + * 2. Rotate (against origin point in parent coord) + * 3. Traslate (with el.position by this method) + * So this method only fixes the last step 'Traslate', which does not affect + * scaling and rotating. + * + * If be called repeatly with the same input el, the same result will be gotten. + * + * @param {module:zrender/Element} el Should have `getBoundingRect` method. + * @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] Only for opt.boundingModel: 'raw' + * @param {number|string} [positionInfo.height] Only for opt.boundingModel: 'raw' + * @param {Object} containerRect + * @param {string|number} margin + * @param {Object} [opt] + * @param {Array.} [opt.hv=[1,1]] Only horizontal or only vertical. + * @param {Array.} [opt.boundingMode='all'] + * Specify how to calculate boundingRect when locating. + * 'all': Position the boundingRect that is transformed and uioned + * both itself and its descendants. + * This mode simplies confine the elements in the bounding + * of their container (e.g., using 'right: 0'). + * 'raw': Position the boundingRect that is not transformed and only itself. + * This mode is useful when you want a element can overflow its + * container. (Consider a rotated circle needs to be located in a corner.) + * In this mode positionInfo.width/height can only be number. + */ +function positionElement(el, positionInfo, containerRect, margin, opt) { + var h = !opt || !opt.hv || opt.hv[0]; + var v = !opt || !opt.hv || opt.hv[1]; + var boundingMode = opt && opt.boundingMode || 'all'; + + if (!h && !v) { + return; + } + + var rect; + if (boundingMode === 'raw') { + rect = el.type === 'group' + ? new BoundingRect(0, 0, +positionInfo.width || 0, +positionInfo.height || 0) + : el.getBoundingRect(); + } + else { + rect = el.getBoundingRect(); + if (el.needLocalTransform()) { + var transform = el.getLocalTransform(); + // Notice: raw rect may be inner object of el, + // which should not be modified. + rect = rect.clone(); + rect.applyTransform(transform); + } + } + + // The real width and height can not be specified but calculated by the given el. + positionInfo = getLayoutRect( + defaults( + {width: rect.width, height: rect.height}, + positionInfo + ), + containerRect, + margin + ); + + // Because 'tranlate' is the last step in transform + // (see zrender/core/Transformable#getLocalTransform), + // we can just only modify el.position to get final result. + var elPos = el.position; + var dx = h ? positionInfo.x - rect.x : 0; + var dy = v ? positionInfo.y - rect.y : 0; + + el.attr('position', boundingMode === 'raw' ? [dx, dy] : [elPos[0] + dx, elPos[1] + dy]); +} + +/** + * @param {Object} option Contains some of the properties in HV_NAMES. + * @param {number} hvIdx 0: horizontal; 1: vertical. + */ +function sizeCalculable(option, hvIdx) { + return option[HV_NAMES[hvIdx][0]] != null + || (option[HV_NAMES[hvIdx][1]] != null && option[HV_NAMES[hvIdx][2]] != null); +} + +/** + * 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|Array.} [opt.ignoreSize=false] Used for the components + * that width (or height) should not be calculated by left and right (or top and bottom). + */ +function mergeLayoutParam(targetOption, newOption, opt) { + !isObject$1(opt) && (opt = {}); + + var ignoreSize = opt.ignoreSize; + !isArray(ignoreSize) && (ignoreSize = [ignoreSize, ignoreSize]); + + var hResult = merge$$1(HV_NAMES[0], 0); + var vResult = merge$$1(HV_NAMES[1], 1); + + copy(HV_NAMES[0], targetOption, hResult); + copy(HV_NAMES[1], targetOption, vResult); + + function merge$$1(names, hvIdx) { + var newParams = {}; + var newValueCount = 0; + var merged = {}; + var mergedValueCount = 0; + var enoughParamNumber = 2; + + each$3(names, function (name) { + merged[name] = targetOption[name]; + }); + each$3(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++; + }); + + if (ignoreSize[hvIdx]) { + // Only one of left/right is premitted to exist. + if (hasValue(newOption, names[1])) { + merged[names[2]] = null; + } + else if (hasValue(newOption, names[2])) { + merged[names[1]] = null; + } + return merged; + } + + // 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. + 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$3(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. + */ +function getLayoutParams(source) { + return copyLayoutParams({}, source); +} + +/** + * Retrieve 'left', 'right', 'top', 'bottom', 'width', 'height' from object. + * @param {Object} source + * @return {Object} Result contains those props. + */ +function copyLayoutParams(target, source) { + source && target && each$3(LOCATION_PARAMS, function (name) { + source.hasOwnProperty(name) && (target[name] = source[name]); + }); + return target; +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + + +var boxLayoutMixin = { + 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') + }; + } +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * Component model + * + * @module echarts/model/Component + */ + +var inner$1 = makeInner(); + +/** + * @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: '', + + /** + * Because simplified concept is probably better, series.name (or component.name) + * has been having too many resposibilities: + * (1) Generating id (which requires name in option should not be modified). + * (2) As an index to mapping series when merging option or calling API (a name + * can refer to more then one components, which is convinient is some case). + * (3) Display. + * @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); + + this.uid = getUID('ec_cpt_model'); + }, + + init: function (option, parentModel, ecModel, extraOpt) { + this.mergeDefaultAndTheme(option, ecModel); + }, + + mergeDefaultAndTheme: function (option, ecModel) { + var layoutMode = this.layoutMode; + var inputPositionParams = layoutMode + ? getLayoutParams(option) : {}; + + var themeModel = ecModel.getTheme(); + merge(option, themeModel.get(this.mainType)); + merge(option, this.getDefaultOption()); + + if (layoutMode) { + mergeLayoutParam(option, inputPositionParams, layoutMode); + } + }, + + mergeOption: function (option, extraOpt) { + merge(this.option, option, true); + + var layoutMode = this.layoutMode; + if (layoutMode) { + mergeLayoutParam(this.option, option, layoutMode); + } + }, + + // Hooker after init or mergeOption + optionUpdated: function (newCptOption, isInit) {}, + + getDefaultOption: function () { + var fields = inner$1(this); + if (!fields.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 = merge(defaultOption, optList[i], true); + } + fields.defaultOption = defaultOption; + } + return fields.defaultOption; + }, + + getReferringComponents: function (mainType) { + return this.ecModel.queryComponents({ + mainType: mainType, + index: this.get(mainType + 'Index', true), + id: this.get(mainType + 'Id', true) + }); + } + +}); + +// 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. +enableClassManagement( + ComponentModel, {registerWhenExtend: true} +); +enableSubTypeDefaulter(ComponentModel); + +// Add capability of ComponentModel.topologicalTravel. +enableTopologicalTravel(ComponentModel, getDependencies); + +function getDependencies(componentType) { + var deps = []; + each$1(ComponentModel.getClassesByMainType(componentType), function (Clazz) { + deps = deps.concat(Clazz.prototype.dependencies || []); + }); + + // Ensure main type. + deps = map(deps, function (type) { + return parseClassType$1(type).main; + }); + + // Hack dataset for convenience. + if (componentType !== 'dataset' && indexOf(deps, 'dataset') <= 0) { + deps.unshift('dataset'); + } + + return deps; +} + +mixin(ComponentModel, boxLayoutMixin); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + + +var platform = ''; +// Navigator not exists in node +if (typeof navigator !== 'undefined') { + platform = navigator.platform || ''; +} + +var globalDefault = { + // backgroundColor: 'rgba(0,0,0,0)', + + // https://dribbble.com/shots/1065960-Infographic-Pie-chart-visualization + // color: ['#5793f3', '#d14a61', '#fd9c35', '#675bba', '#fec42c', '#dd4444', '#d4df5a', '#cd4870'], + // Light colors: + // color: ['#bcd3bb', '#e88f70', '#edc1a5', '#9dc5c8', '#e1e8c8', '#7b7c68', '#e5b5b5', '#f0b489', '#928ea8', '#bda29a'], + // color: ['#cc5664', '#9bd6ec', '#ea946e', '#8acaaa', '#f1ec64', '#ee8686', '#a48dc1', '#5da6bc', '#b9dcae'], + // Dark colors: + color: ['#c23531','#2f4554', '#61a0a8', '#d48265', '#91c7ae','#749f83', '#ca8622', '#bda29a','#6e7074', '#546570', '#c4ccd3'], + + gradientColor: ['#f6efa6', '#d88273', '#bf444c'], + + // If xAxis and yAxis declared, grid is created by default. + // 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: 'auto', + 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. + // It is recommended that `hoverLayerThreshold` is equivalent to or less than + // `progressiveThreshold`, otherwise hover will cause restart of progressive, + // which is unexpected. + // see example . + hoverLayerThreshold: 3000, + + // See: module:echarts/scale/Time + useUTC: false +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var inner$2 = makeInner(); + +function getNearestColorPalette(colors, requestColorNum) { + var paletteNum = colors.length; + // TODO colors must be in order + for (var i = 0; i < paletteNum; i++) { + if (colors[i].length > requestColorNum) { + return colors[i]; + } + } + return colors[paletteNum - 1]; +} + +var colorPaletteMixin = { + clearColorPalette: function () { + inner$2(this).colorIdx = 0; + inner$2(this).colorNameMap = {}; + }, + + /** + * @param {string} name MUST NOT be null/undefined. Otherwise call this function + * twise with the same parameters will get different result. + * @param {Object} [scope=this] + * @param {Object} [requestColorNum] + * @return {string} color string. + */ + getColorFromPalette: function (name, scope, requestColorNum) { + scope = scope || this; + var scopeFields = inner$2(scope); + var colorIdx = scopeFields.colorIdx || 0; + var colorNameMap = scopeFields.colorNameMap = scopeFields.colorNameMap || {}; + // Use `hasOwnProperty` to avoid conflict with Object.prototype. + if (colorNameMap.hasOwnProperty(name)) { + return colorNameMap[name]; + } + var defaultColorPalette = normalizeToArray(this.get('color', true)); + var layeredColorPalette = this.get('colorLayer', true); + var colorPalette = ((requestColorNum == null || !layeredColorPalette) + ? defaultColorPalette : getNearestColorPalette(layeredColorPalette, requestColorNum)); + + // In case can't find in layered color palette. + colorPalette = colorPalette || defaultColorPalette; + + if (!colorPalette || !colorPalette.length) { + return; + } + + var color = colorPalette[colorIdx]; + if (name) { + colorNameMap[name] = color; + } + scopeFields.colorIdx = (colorIdx + 1) % colorPalette.length; + + return color; + } +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * Helper for model references. + * There are many manners to refer axis/coordSys. + */ + +// TODO +// merge relevant logic to this file? +// check: "modelHelper" of tooltip and "BrushTargetManager". + +/** + * @return {Object} For example: + * { + * coordSysName: 'cartesian2d', + * coordSysDims: ['x', 'y', ...], + * axisMap: HashMap({ + * x: xAxisModel, + * y: yAxisModel + * }), + * categoryAxisMap: HashMap({ + * x: xAxisModel, + * y: undefined + * }), + * // It also indicate that whether there is category axis. + * firstCategoryDimIndex: 1, + * // To replace user specified encode. + * } + */ +function getCoordSysDefineBySeries(seriesModel) { + var coordSysName = seriesModel.get('coordinateSystem'); + var result = { + coordSysName: coordSysName, + coordSysDims: [], + axisMap: createHashMap(), + categoryAxisMap: createHashMap() + }; + var fetch = fetchers[coordSysName]; + if (fetch) { + fetch(seriesModel, result, result.axisMap, result.categoryAxisMap); + return result; + } +} + +var fetchers = { + + cartesian2d: function (seriesModel, result, axisMap, categoryAxisMap) { + var xAxisModel = seriesModel.getReferringComponents('xAxis')[0]; + var yAxisModel = seriesModel.getReferringComponents('yAxis')[0]; + + if (__DEV__) { + if (!xAxisModel) { + throw new Error('xAxis "' + retrieve( + seriesModel.get('xAxisIndex'), + seriesModel.get('xAxisId'), + 0 + ) + '" not found'); + } + if (!yAxisModel) { + throw new Error('yAxis "' + retrieve( + seriesModel.get('xAxisIndex'), + seriesModel.get('yAxisId'), + 0 + ) + '" not found'); + } + } + + result.coordSysDims = ['x', 'y']; + axisMap.set('x', xAxisModel); + axisMap.set('y', yAxisModel); + + if (isCategory(xAxisModel)) { + categoryAxisMap.set('x', xAxisModel); + result.firstCategoryDimIndex = 0; + } + if (isCategory(yAxisModel)) { + categoryAxisMap.set('y', yAxisModel); + result.firstCategoryDimIndex = 1; + } + }, + + singleAxis: function (seriesModel, result, axisMap, categoryAxisMap) { + var singleAxisModel = seriesModel.getReferringComponents('singleAxis')[0]; + + if (__DEV__) { + if (!singleAxisModel) { + throw new Error('singleAxis should be specified.'); + } + } + + result.coordSysDims = ['single']; + axisMap.set('single', singleAxisModel); + + if (isCategory(singleAxisModel)) { + categoryAxisMap.set('single', singleAxisModel); + result.firstCategoryDimIndex = 0; + } + }, + + polar: function (seriesModel, result, axisMap, categoryAxisMap) { + var polarModel = seriesModel.getReferringComponents('polar')[0]; + var radiusAxisModel = polarModel.findAxisModel('radiusAxis'); + var angleAxisModel = polarModel.findAxisModel('angleAxis'); + + if (__DEV__) { + if (!angleAxisModel) { + throw new Error('angleAxis option not found'); + } + if (!radiusAxisModel) { + throw new Error('radiusAxis option not found'); + } + } + + result.coordSysDims = ['radius', 'angle']; + axisMap.set('radius', radiusAxisModel); + axisMap.set('angle', angleAxisModel); + + if (isCategory(radiusAxisModel)) { + categoryAxisMap.set('radius', radiusAxisModel); + result.firstCategoryDimIndex = 0; + } + if (isCategory(angleAxisModel)) { + categoryAxisMap.set('angle', angleAxisModel); + result.firstCategoryDimIndex = 1; + } + }, + + geo: function (seriesModel, result, axisMap, categoryAxisMap) { + result.coordSysDims = ['lng', 'lat']; + }, + + parallel: function (seriesModel, result, axisMap, categoryAxisMap) { + var ecModel = seriesModel.ecModel; + var parallelModel = ecModel.getComponent( + 'parallel', seriesModel.get('parallelIndex') + ); + var coordSysDims = result.coordSysDims = parallelModel.dimensions.slice(); + + each$1(parallelModel.parallelAxisIndex, function (axisIndex, index) { + var axisModel = ecModel.getComponent('parallelAxis', axisIndex); + var axisDim = coordSysDims[index]; + axisMap.set(axisDim, axisModel); + + if (isCategory(axisModel) && result.firstCategoryDimIndex == null) { + categoryAxisMap.set(axisDim, axisModel); + result.firstCategoryDimIndex = index; + } + }); + } +}; + +function isCategory(axisModel) { + return axisModel.get('type') === 'category'; +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +// Avoid typo. +var SOURCE_FORMAT_ORIGINAL = 'original'; +var SOURCE_FORMAT_ARRAY_ROWS = 'arrayRows'; +var SOURCE_FORMAT_OBJECT_ROWS = 'objectRows'; +var SOURCE_FORMAT_KEYED_COLUMNS = 'keyedColumns'; +var SOURCE_FORMAT_UNKNOWN = 'unknown'; +// ??? CHANGE A NAME +var SOURCE_FORMAT_TYPED_ARRAY = 'typedArray'; + +var SERIES_LAYOUT_BY_COLUMN = 'column'; +var SERIES_LAYOUT_BY_ROW = 'row'; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * [sourceFormat] + * + * + "original": + * This format is only used in series.data, where + * itemStyle can be specified in data item. + * + * + "arrayRows": + * [ + * ['product', 'score', 'amount'], + * ['Matcha Latte', 89.3, 95.8], + * ['Milk Tea', 92.1, 89.4], + * ['Cheese Cocoa', 94.4, 91.2], + * ['Walnut Brownie', 85.4, 76.9] + * ] + * + * + "objectRows": + * [ + * {product: 'Matcha Latte', score: 89.3, amount: 95.8}, + * {product: 'Milk Tea', score: 92.1, amount: 89.4}, + * {product: 'Cheese Cocoa', score: 94.4, amount: 91.2}, + * {product: 'Walnut Brownie', score: 85.4, amount: 76.9} + * ] + * + * + "keyedColumns": + * { + * 'product': ['Matcha Latte', 'Milk Tea', 'Cheese Cocoa', 'Walnut Brownie'], + * 'count': [823, 235, 1042, 988], + * 'score': [95.8, 81.4, 91.2, 76.9] + * } + * + * + "typedArray" + * + * + "unknown" + */ + +/** + * @constructor + * @param {Object} fields + * @param {string} fields.sourceFormat + * @param {Array|Object} fields.fromDataset + * @param {Array|Object} [fields.data] + * @param {string} [seriesLayoutBy='column'] + * @param {Array.} [dimensionsDefine] + * @param {Objet|HashMap} [encodeDefine] + * @param {number} [startIndex=0] + * @param {number} [dimensionsDetectCount] + */ +function Source(fields) { + + /** + * @type {boolean} + */ + this.fromDataset = fields.fromDataset; + + /** + * Not null/undefined. + * @type {Array|Object} + */ + this.data = fields.data || ( + fields.sourceFormat === SOURCE_FORMAT_KEYED_COLUMNS ? {} : [] + ); + + /** + * See also "detectSourceFormat". + * Not null/undefined. + * @type {string} + */ + this.sourceFormat = fields.sourceFormat || SOURCE_FORMAT_UNKNOWN; + + /** + * 'row' or 'column' + * Not null/undefined. + * @type {string} seriesLayoutBy + */ + this.seriesLayoutBy = fields.seriesLayoutBy || SERIES_LAYOUT_BY_COLUMN; + + /** + * dimensions definition in option. + * can be null/undefined. + * @type {Array.} + */ + this.dimensionsDefine = fields.dimensionsDefine; + + /** + * encode definition in option. + * can be null/undefined. + * @type {Objet|HashMap} + */ + this.encodeDefine = fields.encodeDefine && createHashMap(fields.encodeDefine); + + /** + * Not null/undefined, uint. + * @type {number} + */ + this.startIndex = fields.startIndex || 0; + + /** + * Can be null/undefined (when unknown), uint. + * @type {number} + */ + this.dimensionsDetectCount = fields.dimensionsDetectCount; +} + +/** + * Wrap original series data for some compatibility cases. + */ +Source.seriesDataToSource = function (data) { + return new Source({ + data: data, + sourceFormat: isTypedArray(data) + ? SOURCE_FORMAT_TYPED_ARRAY + : SOURCE_FORMAT_ORIGINAL, + fromDataset: false + }); +}; + +enableClassCheck(Source); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var inner$3 = makeInner(); + +/** + * @see {module:echarts/data/Source} + * @param {module:echarts/component/dataset/DatasetModel} datasetModel + * @return {string} sourceFormat + */ +function detectSourceFormat(datasetModel) { + var data = datasetModel.option.source; + var sourceFormat = SOURCE_FORMAT_UNKNOWN; + + if (isTypedArray(data)) { + sourceFormat = SOURCE_FORMAT_TYPED_ARRAY; + } + else if (isArray(data)) { + // FIXME Whether tolerate null in top level array? + for (var i = 0, len = data.length; i < len; i++) { + var item = data[i]; + + if (item == null) { + continue; + } + else if (isArray(item)) { + sourceFormat = SOURCE_FORMAT_ARRAY_ROWS; + break; + } + else if (isObject$1(item)) { + sourceFormat = SOURCE_FORMAT_OBJECT_ROWS; + break; + } + } + } + else if (isObject$1(data)) { + for (var key in data) { + if (data.hasOwnProperty(key) && isArrayLike(data[key])) { + sourceFormat = SOURCE_FORMAT_KEYED_COLUMNS; + break; + } + } + } + else if (data != null) { + throw new Error('Invalid data'); + } + + inner$3(datasetModel).sourceFormat = sourceFormat; +} + +/** + * [Scenarios]: + * (1) Provide source data directly: + * series: { + * encode: {...}, + * dimensions: [...] + * seriesLayoutBy: 'row', + * data: [[...]] + * } + * (2) Refer to datasetModel. + * series: [{ + * encode: {...} + * // Ignore datasetIndex means `datasetIndex: 0` + * // and the dimensions defination in dataset is used + * }, { + * encode: {...}, + * seriesLayoutBy: 'column', + * datasetIndex: 1 + * }] + * + * Get data from series itself or datset. + * @return {module:echarts/data/Source} source + */ +function getSource(seriesModel) { + return inner$3(seriesModel).source; +} + +/** + * MUST be called before mergeOption of all series. + * @param {module:echarts/model/Global} ecModel + */ +function resetSourceDefaulter(ecModel) { + // `datasetMap` is used to make default encode. + inner$3(ecModel).datasetMap = createHashMap(); +} + +/** + * [Caution]: + * MUST be called after series option merged and + * before "series.getInitailData()" called. + * + * [The rule of making default encode]: + * Category axis (if exists) alway map to the first dimension. + * Each other axis occupies a subsequent dimension. + * + * [Why make default encode]: + * Simplify the typing of encode in option, avoiding the case like that: + * series: [{encode: {x: 0, y: 1}}, {encode: {x: 0, y: 2}}, {encode: {x: 0, y: 3}}], + * where the "y" have to be manually typed as "1, 2, 3, ...". + * + * @param {module:echarts/model/Series} seriesModel + */ +function prepareSource(seriesModel) { + var seriesOption = seriesModel.option; + + var data = seriesOption.data; + var sourceFormat = isTypedArray(data) + ? SOURCE_FORMAT_TYPED_ARRAY : SOURCE_FORMAT_ORIGINAL; + var fromDataset = false; + + var seriesLayoutBy = seriesOption.seriesLayoutBy; + var sourceHeader = seriesOption.sourceHeader; + var dimensionsDefine = seriesOption.dimensions; + + var datasetModel = getDatasetModel(seriesModel); + if (datasetModel) { + var datasetOption = datasetModel.option; + + data = datasetOption.source; + sourceFormat = inner$3(datasetModel).sourceFormat; + fromDataset = true; + + // These settings from series has higher priority. + seriesLayoutBy = seriesLayoutBy || datasetOption.seriesLayoutBy; + sourceHeader == null && (sourceHeader = datasetOption.sourceHeader); + dimensionsDefine = dimensionsDefine || datasetOption.dimensions; + } + + var completeResult = completeBySourceData( + data, sourceFormat, seriesLayoutBy, sourceHeader, dimensionsDefine + ); + + // Note: dataset option does not have `encode`. + var encodeDefine = seriesOption.encode; + if (!encodeDefine && datasetModel) { + encodeDefine = makeDefaultEncode( + seriesModel, datasetModel, data, sourceFormat, seriesLayoutBy, completeResult + ); + } + + inner$3(seriesModel).source = new Source({ + data: data, + fromDataset: fromDataset, + seriesLayoutBy: seriesLayoutBy, + sourceFormat: sourceFormat, + dimensionsDefine: completeResult.dimensionsDefine, + startIndex: completeResult.startIndex, + dimensionsDetectCount: completeResult.dimensionsDetectCount, + encodeDefine: encodeDefine + }); +} + +// return {startIndex, dimensionsDefine, dimensionsCount} +function completeBySourceData(data, sourceFormat, seriesLayoutBy, sourceHeader, dimensionsDefine) { + if (!data) { + return {dimensionsDefine: normalizeDimensionsDefine(dimensionsDefine)}; + } + + var dimensionsDetectCount; + var startIndex; + var findPotentialName; + + if (sourceFormat === SOURCE_FORMAT_ARRAY_ROWS) { + // Rule: Most of the first line are string: it is header. + // Caution: consider a line with 5 string and 1 number, + // it still can not be sure it is a head, because the + // 5 string may be 5 values of category columns. + if (sourceHeader === 'auto' || sourceHeader == null) { + arrayRowsTravelFirst(function (val) { + // '-' is regarded as null/undefined. + if (val != null && val !== '-') { + if (isString(val)) { + startIndex == null && (startIndex = 1); + } + else { + startIndex = 0; + } + } + // 10 is an experience number, avoid long loop. + }, seriesLayoutBy, data, 10); + } + else { + startIndex = sourceHeader ? 1 : 0; + } + + if (!dimensionsDefine && startIndex === 1) { + dimensionsDefine = []; + arrayRowsTravelFirst(function (val, index) { + dimensionsDefine[index] = val != null ? val : ''; + }, seriesLayoutBy, data); + } + + dimensionsDetectCount = dimensionsDefine + ? dimensionsDefine.length + : seriesLayoutBy === SERIES_LAYOUT_BY_ROW + ? data.length + : data[0] + ? data[0].length + : null; + } + else if (sourceFormat === SOURCE_FORMAT_OBJECT_ROWS) { + if (!dimensionsDefine) { + dimensionsDefine = objectRowsCollectDimensions(data); + findPotentialName = true; + } + } + else if (sourceFormat === SOURCE_FORMAT_KEYED_COLUMNS) { + if (!dimensionsDefine) { + dimensionsDefine = []; + findPotentialName = true; + each$1(data, function (colArr, key) { + dimensionsDefine.push(key); + }); + } + } + else if (sourceFormat === SOURCE_FORMAT_ORIGINAL) { + var value0 = getDataItemValue(data[0]); + dimensionsDetectCount = isArray(value0) && value0.length || 1; + } + else if (sourceFormat === SOURCE_FORMAT_TYPED_ARRAY) { + if (__DEV__) { + assert$1(!!dimensionsDefine, 'dimensions must be given if data is TypedArray.'); + } + } + + var potentialNameDimIndex; + if (findPotentialName) { + each$1(dimensionsDefine, function (dim, idx) { + if ((isObject$1(dim) ? dim.name : dim) === 'name') { + potentialNameDimIndex = idx; + } + }); + } + + return { + startIndex: startIndex, + dimensionsDefine: normalizeDimensionsDefine(dimensionsDefine), + dimensionsDetectCount: dimensionsDetectCount, + potentialNameDimIndex: potentialNameDimIndex + // TODO: potentialIdDimIdx + }; +} + +// Consider dimensions defined like ['A', 'price', 'B', 'price', 'C', 'price'], +// which is reasonable. But dimension name is duplicated. +// Returns undefined or an array contains only object without null/undefiend or string. +function normalizeDimensionsDefine(dimensionsDefine) { + if (!dimensionsDefine) { + // The meaning of null/undefined is different from empty array. + return; + } + var nameMap = createHashMap(); + return map(dimensionsDefine, function (item, index) { + item = extend({}, isObject$1(item) ? item : {name: item}); + + // User can set null in dimensions. + // We dont auto specify name, othewise a given name may + // cause it be refered unexpectedly. + if (item.name == null) { + return item; + } + + // Also consider number form like 2012. + item.name += ''; + // User may also specify displayName. + // displayName will always exists except user not + // specified or dim name is not specified or detected. + // (A auto generated dim name will not be used as + // displayName). + if (item.displayName == null) { + item.displayName = item.name; + } + + var exist = nameMap.get(item.name); + if (!exist) { + nameMap.set(item.name, {count: 1}); + } + else { + item.name += '-' + exist.count++; + } + + return item; + }); +} + +function arrayRowsTravelFirst(cb, seriesLayoutBy, data, maxLoop) { + maxLoop == null && (maxLoop = Infinity); + if (seriesLayoutBy === SERIES_LAYOUT_BY_ROW) { + for (var i = 0; i < data.length && i < maxLoop; i++) { + cb(data[i] ? data[i][0] : null, i); + } + } + else { + var value0 = data[0] || []; + for (var i = 0; i < value0.length && i < maxLoop; i++) { + cb(value0[i], i); + } + } +} + +function objectRowsCollectDimensions(data) { + var firstIndex = 0; + var obj; + while (firstIndex < data.length && !(obj = data[firstIndex++])) {} // jshint ignore: line + if (obj) { + var dimensions = []; + each$1(obj, function (value, key) { + dimensions.push(key); + }); + return dimensions; + } +} + +// ??? TODO merge to completedimensions, where also has +// default encode making logic. And the default rule +// should depends on series? consider 'map'. +function makeDefaultEncode( + seriesModel, datasetModel, data, sourceFormat, seriesLayoutBy, completeResult +) { + var coordSysDefine = getCoordSysDefineBySeries(seriesModel); + var encode = {}; + // var encodeTooltip = []; + // var encodeLabel = []; + var encodeItemName = []; + var encodeSeriesName = []; + var seriesType = seriesModel.subType; + + // ??? TODO refactor: provide by series itself. + // Consider the case: 'map' series is based on geo coordSys, + // 'graph', 'heatmap' can be based on cartesian. But can not + // give default rule simply here. + var nSeriesMap = createHashMap(['pie', 'map', 'funnel']); + var cSeriesMap = createHashMap([ + 'line', 'bar', 'pictorialBar', 'scatter', 'effectScatter', 'candlestick', 'boxplot' + ]); + + // Usually in this case series will use the first data + // dimension as the "value" dimension, or other default + // processes respectively. + if (coordSysDefine && cSeriesMap.get(seriesType) != null) { + var ecModel = seriesModel.ecModel; + var datasetMap = inner$3(ecModel).datasetMap; + var key = datasetModel.uid + '_' + seriesLayoutBy; + var datasetRecord = datasetMap.get(key) + || datasetMap.set(key, {categoryWayDim: 1, valueWayDim: 0}); + + // TODO + // Auto detect first time axis and do arrangement. + each$1(coordSysDefine.coordSysDims, function (coordDim) { + // In value way. + if (coordSysDefine.firstCategoryDimIndex == null) { + var dataDim = datasetRecord.valueWayDim++; + encode[coordDim] = dataDim; + + // ??? TODO give a better default series name rule? + // especially when encode x y specified. + // consider: when mutiple series share one dimension + // category axis, series name should better use + // the other dimsion name. On the other hand, use + // both dimensions name. + + encodeSeriesName.push(dataDim); + // encodeTooltip.push(dataDim); + // encodeLabel.push(dataDim); + } + // In category way, category axis. + else if (coordSysDefine.categoryAxisMap.get(coordDim)) { + encode[coordDim] = 0; + encodeItemName.push(0); + } + // In category way, non-category axis. + else { + var dataDim = datasetRecord.categoryWayDim++; + encode[coordDim] = dataDim; + // encodeTooltip.push(dataDim); + // encodeLabel.push(dataDim); + encodeSeriesName.push(dataDim); + } + }); + } + // Do not make a complex rule! Hard to code maintain and not necessary. + // ??? TODO refactor: provide by series itself. + // [{name: ..., value: ...}, ...] like: + else if (nSeriesMap.get(seriesType) != null) { + // Find the first not ordinal. (5 is an experience value) + var firstNotOrdinal; + for (var i = 0; i < 5 && firstNotOrdinal == null; i++) { + if (!doGuessOrdinal( + data, sourceFormat, seriesLayoutBy, + completeResult.dimensionsDefine, completeResult.startIndex, i + )) { + firstNotOrdinal = i; + } + } + if (firstNotOrdinal != null) { + encode.value = firstNotOrdinal; + var nameDimIndex = completeResult.potentialNameDimIndex + || Math.max(firstNotOrdinal - 1, 0); + // By default, label use itemName in charts. + // So we dont set encodeLabel here. + encodeSeriesName.push(nameDimIndex); + encodeItemName.push(nameDimIndex); + // encodeTooltip.push(firstNotOrdinal); + } + } + + // encodeTooltip.length && (encode.tooltip = encodeTooltip); + // encodeLabel.length && (encode.label = encodeLabel); + encodeItemName.length && (encode.itemName = encodeItemName); + encodeSeriesName.length && (encode.seriesName = encodeSeriesName); + + return encode; +} + +/** + * If return null/undefined, indicate that should not use datasetModel. + */ +function getDatasetModel(seriesModel) { + var option = seriesModel.option; + // Caution: consider the scenario: + // A dataset is declared and a series is not expected to use the dataset, + // and at the beginning `setOption({series: { noData })` (just prepare other + // option but no data), then `setOption({series: {data: [...]}); In this case, + // the user should set an empty array to avoid that dataset is used by default. + var thisData = option.data; + if (!thisData) { + return seriesModel.ecModel.getComponent('dataset', option.datasetIndex || 0); + } +} + +/** + * The rule should not be complex, otherwise user might not + * be able to known where the data is wrong. + * The code is ugly, but how to make it neat? + * + * @param {module:echars/data/Source} source + * @param {number} dimIndex + * @return {boolean} Whether ordinal. + */ +function guessOrdinal(source, dimIndex) { + return doGuessOrdinal( + source.data, + source.sourceFormat, + source.seriesLayoutBy, + source.dimensionsDefine, + source.startIndex, + dimIndex + ); +} + +// dimIndex may be overflow source data. +function doGuessOrdinal( + data, sourceFormat, seriesLayoutBy, dimensionsDefine, startIndex, dimIndex +) { + var result; + // Experience value. + var maxLoop = 5; + + if (isTypedArray(data)) { + return false; + } + + // When sourceType is 'objectRows' or 'keyedColumns', dimensionsDefine + // always exists in source. + var dimName; + if (dimensionsDefine) { + dimName = dimensionsDefine[dimIndex]; + dimName = isObject$1(dimName) ? dimName.name : dimName; + } + + if (sourceFormat === SOURCE_FORMAT_ARRAY_ROWS) { + if (seriesLayoutBy === SERIES_LAYOUT_BY_ROW) { + var sample = data[dimIndex]; + for (var i = 0; i < (sample || []).length && i < maxLoop; i++) { + if ((result = detectValue(sample[startIndex + i])) != null) { + return result; + } + } + } + else { + for (var i = 0; i < data.length && i < maxLoop; i++) { + var row = data[startIndex + i]; + if (row && (result = detectValue(row[dimIndex])) != null) { + return result; + } + } + } + } + else if (sourceFormat === SOURCE_FORMAT_OBJECT_ROWS) { + if (!dimName) { + return; + } + for (var i = 0; i < data.length && i < maxLoop; i++) { + var item = data[i]; + if (item && (result = detectValue(item[dimName])) != null) { + return result; + } + } + } + else if (sourceFormat === SOURCE_FORMAT_KEYED_COLUMNS) { + if (!dimName) { + return; + } + var sample = data[dimName]; + if (!sample || isTypedArray(sample)) { + return false; + } + for (var i = 0; i < sample.length && i < maxLoop; i++) { + if ((result = detectValue(sample[i])) != null) { + return result; + } + } + } + else if (sourceFormat === SOURCE_FORMAT_ORIGINAL) { + for (var i = 0; i < data.length && i < maxLoop; i++) { + var item = data[i]; + var val = getDataItemValue(item); + if (!isArray(val)) { + return false; + } + if ((result = detectValue(val[dimIndex])) != null) { + return result; + } + } + } + + function detectValue(val) { + // Consider usage convenience, '1', '2' will be treated as "number". + // `isFinit('')` get `true`. + if (val != null && isFinite(val) && val !== '') { + return false; + } + else if (isString(val) && val !== '-') { + return true; + } + } + + return false; +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * ECharts global model + * + * @module {echarts/model/Global} + */ + + +/** + * Caution: If the mechanism should be changed some day, these cases + * should be considered: + * + * (1) In `merge option` mode, if using the same option to call `setOption` + * many times, the result should be the same (try our best to ensure that). + * (2) In `merge option` mode, if a component has no id/name specified, it + * will be merged by index, and the result sequence of the components is + * consistent to the original sequence. + * (3) `reset` feature (in toolbox). Find detailed info in comments about + * `mergeOption` in module:echarts/model/OptionManager. + */ + +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({ + + 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) { + assert$1( + !(OPTION_INNER_KEY in option), + 'please use chart.getOption()' + ); + + this._optionManager.setOption(option, optionPreprocessorFuncs); + + this.resetOption(null); + }, + + /** + * @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$1(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 = []; + + resetSourceDefaulter(this); + + // If no component class, merge directly. + // For example: color, animaiton options, etc. + each$1(newOption, function (componentOption, mainType) { + if (componentOption == null) { + return; + } + + if (!ComponentModel.hasClass(mainType)) { + // globalSettingTask.dirty(); + option[mainType] = option[mainType] == null + ? clone(componentOption) + : merge(option[mainType], componentOption, true); + } + else if (mainType) { + newCptTypes.push(mainType); + } + }); + + ComponentModel.topologicalTravel( + newCptTypes, ComponentModel.getAllClassMainTypes(), visitComponent, this + ); + + function visitComponent(mainType, dependencies) { + + var newCptOptionList = normalizeToArray(newOption[mainType]); + + var mapResult = mappingToExists( + componentsMap.get(mainType), newCptOptionList + ); + + makeIdAndName(mapResult); + + // Set mainType and complete subType. + each$1(mapResult, function (item, index) { + var opt = item.option; + if (isObject$1(opt)) { + item.keyInfo.mainType = mainType; + item.keyInfo.subType = determineSubType(mainType, opt, item.exist); + } + }); + + var dependentModels = getComponentsByTypes( + componentsMap, dependencies + ); + + option[mainType] = []; + componentsMap.set(mainType, []); + + each$1(mapResult, function (resultItem, index) { + var componentModel = resultItem.exist; + var newCptOption = resultItem.option; + + assert$1( + isObject$1(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.name = resultItem.keyInfo.name; + // componentModel.settingTask && componentModel.settingTask.dirty(); + componentModel.mergeOption(newCptOption, this); + componentModel.optionUpdated(newCptOption, false); + } + else { + // PENDING Global as parent ? + var extraOpt = extend( + { + dependentModels: dependentModels, + componentIndex: index + }, + resultItem.keyInfo + ); + componentModel = new ComponentModelClass( + newCptOption, this, this, extraOpt + ); + extend(componentModel, 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.get(mainType)[index] = componentModel; + option[mainType][index] = componentModel.option; + }, this); + + // Backup series for filtering. + if (mainType === 'series') { + createSeriesIndices(this, componentsMap.get('series')); + } + } + + this._seriesIndicesMap = createHashMap( + this._seriesIndices = this._seriesIndices || [] + ); + }, + + /** + * Get option for output (cloned option and inner info removed) + * @public + * @return {Object} + */ + getOption: function () { + var option = clone(this.option); + + each$1(option, function (opts, mainType) { + if (ComponentModel.hasClass(mainType)) { + var opts = normalizeToArray(opts); + for (var i = opts.length - 1; i >= 0; i--) { + // Remove options with inner id. + if (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.get(mainType); + if (list) { + return list[idx || 0]; + } + }, + + /** + * If none of index and id and name used, return all components with mainType. + * @param {Object} condition + * @param {string} condition.mainType + * @param {string} [condition.subType] If ignore, only query by mainType + * @param {number|Array.} [condition.index] Either input index or id or name. + * @param {string|Array.} [condition.id] Either input index or id or name. + * @param {string|Array.} [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.get(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.slice(); + } + + 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.get(mainType); + + return doFilter(filterBySubType(result, condition)); + + function getQueryCond(q) { + var indexAttr = mainType + 'Index'; + var idAttr = mainType + 'Id'; + var nameAttr = mainType + 'Name'; + return q && ( + q[indexAttr] != null + || q[idAttr] != null + || q[nameAttr] != null + ) + ? { + 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; + componentsMap.each(function (components, componentType) { + each$1(components, function (component, index) { + cb.call(context, componentType, component, index); + }); + }); + } + else if (isString(mainType)) { + each$1(componentsMap.get(mainType), cb, context); + } + else if (isObject$1(mainType)) { + var queryResult = this.findComponents(mainType); + each$1(queryResult, cb, context); + } + }, + + /** + * @param {string} name + * @return {Array.} + */ + getSeriesByName: function (name) { + var series = this._componentsMap.get('series'); + return filter(series, function (oneSeries) { + return oneSeries.name === name; + }); + }, + + /** + * @param {number} seriesIndex + * @return {module:echarts/model/Series} + */ + getSeriesByIndex: function (seriesIndex) { + return this._componentsMap.get('series')[seriesIndex]; + }, + + /** + * Get series list before filtered by type. + * FIXME: rename to getRawSeriesByType? + * + * @param {string} subType + * @return {Array.} + */ + getSeriesByType: function (subType) { + var series = this._componentsMap.get('series'); + return filter(series, function (oneSeries) { + return oneSeries.subType === subType; + }); + }, + + /** + * @return {Array.} + */ + getSeries: function () { + return this._componentsMap.get('series').slice(); + }, + + /** + * @return {number} + */ + getSeriesCount: function () { + return this._componentsMap.get('series').length; + }, + + /** + * After filtering, series may be different + * frome raw series. + * + * @param {Function} cb + * @param {*} context + */ + eachSeries: function (cb, context) { + assertSeriesInitialized(this); + each$1(this._seriesIndices, function (rawSeriesIndex) { + var series = this._componentsMap.get('series')[rawSeriesIndex]; + cb.call(context, series, rawSeriesIndex); + }, this); + }, + + /** + * Iterate raw series before filtered. + * + * @param {Function} cb + * @param {*} context + */ + eachRawSeries: function (cb, context) { + each$1(this._componentsMap.get('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$1(this._seriesIndices, function (rawSeriesIndex) { + var series = this._componentsMap.get('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$1(this.getSeriesByType(subType), cb, context); + }, + + /** + * @param {module:echarts/model/Series} seriesModel + */ + isSeriesFiltered: function (seriesModel) { + assertSeriesInitialized(this); + return this._seriesIndicesMap.get(seriesModel.componentIndex) == null; + }, + + /** + * @return {Array.} + */ + getCurrentSeriesIndices: function () { + return (this._seriesIndices || []).slice(); + }, + + /** + * @param {Function} cb + * @param {*} context + */ + filterSeries: function (cb, context) { + assertSeriesInitialized(this); + var filteredSeries = filter( + this._componentsMap.get('series'), cb, context + ); + createSeriesIndices(this, filteredSeries); + }, + + restoreData: function (payload) { + var componentsMap = this._componentsMap; + + createSeriesIndices(this, componentsMap.get('series')); + + var componentTypes = []; + componentsMap.each(function (components, componentType) { + componentTypes.push(componentType); + }); + + ComponentModel.topologicalTravel( + componentTypes, + ComponentModel.getAllClassMainTypes(), + function (componentType, dependencies) { + each$1(componentsMap.get(componentType), function (component) { + (componentType !== 'series' || !isNotTargetSeries(component, payload)) + && component.restoreData(); + }); + } + ); + } + +}); + +function isNotTargetSeries(seriesModel, payload) { + if (payload) { + var index = payload.seiresIndex; + var id = payload.seriesId; + var name = payload.seriesName; + return (index != null && seriesModel.componentIndex !== index) + || (id != null && seriesModel.id !== id) + || (name != null && seriesModel.name !== name); + } +} + +/** + * @inner + */ +function mergeTheme(option, theme) { + // PENDING + // NOT use `colorLayer` in theme if option has `color` + var notMergeColorLayer = option.color && !option.colorLayer; + + each$1(theme, function (themeItem, name) { + if (name === 'colorLayer' && notMergeColorLayer) { + return; + } + // 如果有 component model 则把具体的 merge 逻辑交给该 model 处理 + if (!ComponentModel.hasClass(name)) { + if (typeof themeItem === 'object') { + option[name] = !option[name] + ? clone(themeItem) + : merge(option[name], themeItem, false); + } + else { + if (option[name] == null) { + option[name] = themeItem; + } + } + } + }); +} + +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; + + /** + * Init with series: [], in case of calling findSeries method + * before series initialized. + * @type {Object.>} + * @private + */ + this._componentsMap = createHashMap({series: []}); + + /** + * Mapping between filtered series list and raw series list. + * key: filtered series indices, value: raw series indices. + * @type {Array.} + * @private + */ + this._seriesIndices; + + this._seriesIndicesMap; + + mergeTheme(baseOption, this._theme.option); + + // TODO Needs clone when merging to the unexisted property + 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 (!isArray(types)) { + types = types ? [types] : []; + } + + var ret = {}; + each$1(types, function (type) { + ret[type] = (componentsMap.get(type) || []).slice(); + }); + + return ret; +} + +/** + * @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(ecModel, seriesModels) { + ecModel._seriesIndicesMap = createHashMap( + ecModel._seriesIndices = 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 (__DEV__) { + if (!ecModel._seriesIndices) { + throw new Error('Option should contains series.'); + } + } +} + +mixin(GlobalModel, colorPaletteMixin); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var echartsAPIList = [ + 'getDom', 'getZr', 'getWidth', 'getHeight', 'getDevicePixelRatio', 'dispatchAction', 'isDisposed', + 'on', 'off', 'getDataURL', 'getConnectedDataURL', 'getModel', 'getOption', + 'getViewOfComponentModel', 'getViewOfSeriesModel' +]; +// And `getCoordinateSystems` and `getComponentByElement` will be injected in echarts.js + +function ExtensionAPI(chartInstance) { + each$1(echartsAPIList, function (name) { + this[name] = bind(chartInstance[name], chartInstance); + }, this); +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var coordinateSystemCreators = {}; + +function CoordinateSystemManager() { + + this._coordinateSystems = []; +} + +CoordinateSystemManager.prototype = { + + constructor: CoordinateSystemManager, + + create: function (ecModel, api) { + var coordinateSystems = []; + each$1(coordinateSystemCreators, function (creater, type) { + var list = creater.create(ecModel, api); + coordinateSystems = coordinateSystems.concat(list || []); + }); + + this._coordinateSystems = coordinateSystems; + }, + + update: function (ecModel, api) { + each$1(this._coordinateSystems, function (coordSys) { + coordSys.update && coordSys.update(ecModel, api); + }); + }, + + getCoordinateSystems: function () { + return this._coordinateSystems.slice(); + } +}; + +CoordinateSystemManager.register = function (type, coordinateSystemCreator) { + coordinateSystemCreators[type] = coordinateSystemCreator; +}; + +CoordinateSystemManager.get = function (type) { + return coordinateSystemCreators[type]; +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * ECharts option manager + * + * @module {echarts/model/OptionManager} + */ + + +var each$4 = each$1; +var clone$3 = clone; +var map$1 = map; +var merge$1 = 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) { + if (rawOption) { + // That set dat primitive is dangerous if user reuse the data when setOption again. + each$1(normalizeToArray(rawOption.series), function (series) { + series && series.data && isTypedArray(series.data) && setAsPrimitive(series.data); + }); + } + + // Caution: some series modify option data, if do not clone, + // it should ensure that the repeat modify correctly + // (create a new object when modify itself). + rawOption = clone$3(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$1(optionBackup.timelineOptions, clone$3); + this._mediaList = map$1(optionBackup.mediaList, clone$3); + this._mediaDefault = clone$3(optionBackup.mediaDefault); + this._currentMediaIndices = []; + + return clone$3(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 + // performed 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$3( + 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$1(indices, function (index) { + return clone$3( + 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$4(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$4([baseOption].concat(timelineOptions) + .concat(map(mediaList, function (media) { + return media.option; + })), + function (option) { + each$4(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; + + each$1(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$4(newOption, function (newCptOpt, mainType) { + if (newCptOpt == null) { + return; + } + + var oldCptOpt = oldOption[mainType]; + + if (!ComponentModel.hasClass(mainType)) { + oldOption[mainType] = merge$1(oldCptOpt, newCptOpt, true); + } + else { + newCptOpt = normalizeToArray(newCptOpt); + oldCptOpt = normalizeToArray(oldCptOpt); + + var mapResult = mappingToExists(oldCptOpt, newCptOpt); + + oldOption[mainType] = map$1(mapResult, function (item) { + return (item.option && item.exist) + ? merge$1(item.exist, item.option, true) + : (item.exist || item.option); + }); + } + }); +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var each$5 = each$1; +var isObject$3 = isObject$1; + +var POSSIBLE_STYLES = [ + 'areaStyle', 'lineStyle', 'nodeStyle', 'linkStyle', + 'chordStyle', 'label', 'labelLine' +]; + +function compatEC2ItemStyle(opt) { + var itemStyleOpt = opt && opt.itemStyle; + if (!itemStyleOpt) { + return; + } + for (var i = 0, len = POSSIBLE_STYLES.length; i < len; i++) { + var styleName = POSSIBLE_STYLES[i]; + 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 { + 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 { + merge(opt[styleName].emphasis, emphasisItemStyleOpt[styleName]); + } + emphasisItemStyleOpt[styleName] = null; + } + } +} + +function convertNormalEmphasis(opt, optType, useExtend) { + if (opt && opt[optType] && (opt[optType].normal || opt[optType].emphasis)) { + var normalOpt = opt[optType].normal; + var emphasisOpt = opt[optType].emphasis; + + if (normalOpt) { + // Timeline controlStyle has other properties besides normal and emphasis + if (useExtend) { + opt[optType].normal = opt[optType].emphasis = null; + defaults(opt[optType], normalOpt); + } + else { + opt[optType] = normalOpt; + } + } + if (emphasisOpt) { + opt.emphasis = opt.emphasis || {}; + opt.emphasis[optType] = emphasisOpt; + } + } +} +function removeEC3NormalStatus(opt) { + convertNormalEmphasis(opt, 'itemStyle'); + convertNormalEmphasis(opt, 'lineStyle'); + convertNormalEmphasis(opt, 'areaStyle'); + convertNormalEmphasis(opt, 'label'); + convertNormalEmphasis(opt, 'labelLine'); + // treemap + convertNormalEmphasis(opt, 'upperLabel'); + // graph + convertNormalEmphasis(opt, 'edgeLabel'); +} + +function compatTextStyle(opt, propName) { + // Check whether is not object (string\null\undefined ...) + var labelOptSingle = isObject$3(opt) && opt[propName]; + var textStyle = isObject$3(labelOptSingle) && labelOptSingle.textStyle; + if (textStyle) { + for (var i = 0, len = TEXT_STYLE_OPTIONS.length; i < len; i++) { + var propName = TEXT_STYLE_OPTIONS[i]; + if (textStyle.hasOwnProperty(propName)) { + labelOptSingle[propName] = textStyle[propName]; + } + } + } +} + +function compatEC3CommonStyles(opt) { + if (opt) { + removeEC3NormalStatus(opt); + compatTextStyle(opt, 'label'); + opt.emphasis && compatTextStyle(opt.emphasis, 'label'); + } +} + +function processSeries(seriesOpt) { + if (!isObject$3(seriesOpt)) { + return; + } + + compatEC2ItemStyle(seriesOpt); + removeEC3NormalStatus(seriesOpt); + + compatTextStyle(seriesOpt, 'label'); + // treemap + compatTextStyle(seriesOpt, 'upperLabel'); + // graph + compatTextStyle(seriesOpt, 'edgeLabel'); + if (seriesOpt.emphasis) { + compatTextStyle(seriesOpt.emphasis, 'label'); + // treemap + compatTextStyle(seriesOpt.emphasis, 'upperLabel'); + // graph + compatTextStyle(seriesOpt.emphasis, 'edgeLabel'); + } + + var markPoint = seriesOpt.markPoint; + if (markPoint) { + compatEC2ItemStyle(markPoint); + compatEC3CommonStyles(markPoint); + } + + var markLine = seriesOpt.markLine; + if (markLine) { + compatEC2ItemStyle(markLine); + compatEC3CommonStyles(markLine); + } + + var markArea = seriesOpt.markArea; + if (markArea) { + compatEC3CommonStyles(markArea); + } + + var data = seriesOpt.data; + + // Break with ec3: if `setOption` again, there may be no `type` in option, + // then the backward compat based on option type will not be performed. + + if (seriesOpt.type === 'graph') { + data = data || seriesOpt.nodes; + var edgeData = seriesOpt.links || seriesOpt.edges; + if (edgeData && !isTypedArray(edgeData)) { + for (var i = 0; i < edgeData.length; i++) { + compatEC3CommonStyles(edgeData[i]); + } + } + each$1(seriesOpt.categories, function (opt) { + removeEC3NormalStatus(opt); + }); + } + + if (data && !isTypedArray(data)) { + for (var i = 0; i < data.length; i++) { + compatEC3CommonStyles(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++) { + compatEC3CommonStyles(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 (isArray(mlData[i])) { + compatEC3CommonStyles(mlData[i][0]); + compatEC3CommonStyles(mlData[i][1]); + } + else { + compatEC3CommonStyles(mlData[i]); + } + } + } + + // Series + if (seriesOpt.type === 'gauge') { + compatTextStyle(seriesOpt, 'axisLabel'); + compatTextStyle(seriesOpt, 'title'); + compatTextStyle(seriesOpt, 'detail'); + } + else if (seriesOpt.type === 'treemap') { + convertNormalEmphasis(seriesOpt.breadcrumb, 'itemStyle'); + each$1(seriesOpt.levels, function (opt) { + removeEC3NormalStatus(opt); + }); + } + else if (seriesOpt.type === 'tree') { + removeEC3NormalStatus(seriesOpt.leaves); + } + // sunburst starts from ec4, so it does not need to compat levels. +} + +function toArr(o) { + return isArray(o) ? o : o ? [o] : []; +} + +function toObj(o) { + return (isArray(o) ? o[0] : o) || {}; +} + +var compatStyle = function (option, isTheme) { + each$5(toArr(option.series), function (seriesOpt) { + isObject$3(seriesOpt) && processSeries(seriesOpt); + }); + + var axes = ['xAxis', 'yAxis', 'radiusAxis', 'angleAxis', 'singleAxis', 'parallelAxis', 'radar']; + isTheme && axes.push('valueAxis', 'categoryAxis', 'logAxis', 'timeAxis'); + + each$5( + axes, + function (axisName) { + each$5(toArr(option[axisName]), function (axisOpt) { + if (axisOpt) { + compatTextStyle(axisOpt, 'axisLabel'); + compatTextStyle(axisOpt.axisPointer, 'label'); + } + }); + } + ); + + each$5(toArr(option.parallel), function (parallelOpt) { + var parallelAxisDefault = parallelOpt && parallelOpt.parallelAxisDefault; + compatTextStyle(parallelAxisDefault, 'axisLabel'); + compatTextStyle(parallelAxisDefault && parallelAxisDefault.axisPointer, 'label'); + }); + + each$5(toArr(option.calendar), function (calendarOpt) { + convertNormalEmphasis(calendarOpt, 'itemStyle'); + compatTextStyle(calendarOpt, 'dayLabel'); + compatTextStyle(calendarOpt, 'monthLabel'); + compatTextStyle(calendarOpt, 'yearLabel'); + }); + + // radar.name.textStyle + each$5(toArr(option.radar), function (radarOpt) { + compatTextStyle(radarOpt, 'name'); + }); + + each$5(toArr(option.geo), function (geoOpt) { + if (isObject$3(geoOpt)) { + compatEC3CommonStyles(geoOpt); + each$5(toArr(geoOpt.regions), function (regionObj) { + compatEC3CommonStyles(regionObj); + }); + } + }); + + each$5(toArr(option.timeline), function (timelineOpt) { + compatEC3CommonStyles(timelineOpt); + convertNormalEmphasis(timelineOpt, 'label'); + convertNormalEmphasis(timelineOpt, 'itemStyle'); + convertNormalEmphasis(timelineOpt, 'controlStyle', true); + + var data = timelineOpt.data; + isArray(data) && each$1(data, function (item) { + if (isObject$1(item)) { + convertNormalEmphasis(item, 'label'); + convertNormalEmphasis(item, 'itemStyle'); + } + }); + }); + + each$5(toArr(option.toolbox), function (toolboxOpt) { + convertNormalEmphasis(toolboxOpt, 'iconStyle'); + each$5(toolboxOpt.feature, function (featureOpt) { + convertNormalEmphasis(featureOpt, 'iconStyle'); + }); + }); + + compatTextStyle(toObj(option.axisPointer), 'label'); + compatTextStyle(toObj(option.tooltip).axisPointer, 'label'); +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +// Compatitable with 2.0 + +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$1(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$1(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 backwardCompat = function (option, isTheme) { + compatStyle(option, isTheme); + + // Make sure series array for model initialization. + option.series = normalizeToArray(option.series); + + each$1(option.series, function (seriesOpt) { + if (!isObject$1(seriesOpt)) { + return; + } + + var seriesType = seriesOpt.type; + + 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$1(seriesOpt, 'itemStyle.normal.color', pointerColor); + } + + compatLayoutProperties(seriesOpt); + }); + + // dataRange has changed to visualMap + if (option.dataRange) { + option.visualMap = option.dataRange; + } + + each$1(COMPATITABLE_COMPONENTS, function (componentName) { + var options = option[componentName]; + if (options) { + if (!isArray(options)) { + options = [options]; + } + each$1(options, function (option) { + compatLayoutProperties(option); + }); + } + }); +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +// (1) [Caution]: the logic is correct based on the premises: +// data processing stage is blocked in stream. +// See +// (2) Only register once when import repeatly. +// Should be executed before after series filtered and before stack calculation. +var dataStack = function (ecModel) { + var stackInfoMap = createHashMap(); + ecModel.eachSeries(function (seriesModel) { + var stack = seriesModel.get('stack'); + // Compatibal: when `stack` is set as '', do not stack. + if (stack) { + var stackInfoList = stackInfoMap.get(stack) || stackInfoMap.set(stack, []); + var data = seriesModel.getData(); + + var stackInfo = { + // Used for calculate axis extent automatically. + stackResultDimension: data.getCalculationInfo('stackResultDimension'), + stackedOverDimension: data.getCalculationInfo('stackedOverDimension'), + stackedDimension: data.getCalculationInfo('stackedDimension'), + stackedByDimension: data.getCalculationInfo('stackedByDimension'), + isStackedByIndex: data.getCalculationInfo('isStackedByIndex'), + data: data, + seriesModel: seriesModel + }; + + // If stacked on axis that do not support data stack. + if (!stackInfo.stackedDimension + || !(stackInfo.isStackedByIndex || stackInfo.stackedByDimension) + ) { + return; + } + + stackInfoList.length && data.setCalculationInfo( + 'stackedOnSeries', stackInfoList[stackInfoList.length - 1].seriesModel + ); + + stackInfoList.push(stackInfo); + } + }); + + stackInfoMap.each(calculateStack); +}; + +function calculateStack(stackInfoList) { + each$1(stackInfoList, function (targetStackInfo, idxInStack) { + var resultVal = []; + var resultNaN = [NaN, NaN]; + var dims = [targetStackInfo.stackResultDimension, targetStackInfo.stackedOverDimension]; + var targetData = targetStackInfo.data; + var isStackedByIndex = targetStackInfo.isStackedByIndex; + + // Should not write on raw data, because stack series model list changes + // depending on legend selection. + var newData = targetData.map(dims, function (v0, v1, dataIndex) { + var sum = targetData.get(targetStackInfo.stackedDimension, dataIndex); + + // Consider `connectNulls` of line area, if value is NaN, stackedOver + // should also be NaN, to draw a appropriate belt area. + if (isNaN(sum)) { + return resultNaN; + } + + var byValue; + var stackedDataRawIndex; + + if (isStackedByIndex) { + stackedDataRawIndex = targetData.getRawIndex(dataIndex); + } + else { + byValue = targetData.get(targetStackInfo.stackedByDimension, dataIndex); + } + + // If stackOver is NaN, chart view will render point on value start. + var stackedOver = NaN; + + for (var j = idxInStack - 1; j >= 0; j--) { + var stackInfo = stackInfoList[j]; + + // Has been optimized by inverted indices on `stackedByDimension`. + if (!isStackedByIndex) { + stackedDataRawIndex = stackInfo.data.rawIndexOf(stackInfo.stackedByDimension, byValue); + } + + if (stackedDataRawIndex >= 0) { + var val = stackInfo.data.getByRawIndex(stackInfo.stackResultDimension, stackedDataRawIndex); + + // Considering positive stack, negative stack and empty data + if ((sum >= 0 && val > 0) // Positive stack + || (sum <= 0 && val < 0) // Negative stack + ) { + sum += val; + stackedOver = val; + break; + } + } + } + + resultVal[0] = sum; + resultVal[1] = stackedOver; + + return resultVal; + }); + + targetData.hostModel.setData(newData); + // Update for consequent calculation + targetStackInfo.data = newData; + }); +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +// TODO +// ??? refactor? check the outer usage of data provider. +// merge with defaultDimValueGetter? + +/** + * If normal array used, mutable chunk size is supported. + * If typed array used, chunk size must be fixed. + */ +function DefaultDataProvider(source, dimSize) { + if (!Source.isInstance(source)) { + source = Source.seriesDataToSource(source); + } + this._source = source; + + var data = this._data = source.data; + var sourceFormat = source.sourceFormat; + + // Typed array. TODO IE10+? + if (sourceFormat === SOURCE_FORMAT_TYPED_ARRAY) { + if (__DEV__) { + if (dimSize == null) { + throw new Error('Typed array data must specify dimension size'); + } + } + this._offset = 0; + this._dimSize = dimSize; + this._data = data; + } + + var methods = providerMethods[ + sourceFormat === SOURCE_FORMAT_ARRAY_ROWS + ? sourceFormat + '_' + source.seriesLayoutBy + : sourceFormat + ]; + + if (__DEV__) { + assert$1(methods, 'Invalide sourceFormat: ' + sourceFormat); + } + + extend(this, methods); +} + +var providerProto = DefaultDataProvider.prototype; +// If data is pure without style configuration +providerProto.pure = false; +// If data is persistent and will not be released after use. +providerProto.persistent = true; + +// ???! FIXME legacy data provider do not has method getSource +providerProto.getSource = function () { + return this._source; +}; + +var providerMethods = { + + 'arrayRows_column': { + pure: true, + count: function () { + return Math.max(0, this._data.length - this._source.startIndex); + }, + getItem: function (idx) { + return this._data[idx + this._source.startIndex]; + }, + appendData: appendDataSimply + }, + + 'arrayRows_row': { + pure: true, + count: function () { + var row = this._data[0]; + return row ? Math.max(0, row.length - this._source.startIndex) : 0; + }, + getItem: function (idx) { + idx += this._source.startIndex; + var item = []; + var data = this._data; + for (var i = 0; i < data.length; i++) { + var row = data[i]; + item.push(row ? row[idx] : null); + } + return item; + }, + appendData: function () { + throw new Error('Do not support appendData when set seriesLayoutBy: "row".'); + } + }, + + 'objectRows': { + pure: true, + count: countSimply, + getItem: getItemSimply, + appendData: appendDataSimply + }, + + 'keyedColumns': { + pure: true, + count: function () { + var dimName = this._source.dimensionsDefine[0].name; + var col = this._data[dimName]; + return col ? col.length : 0; + }, + getItem: function (idx) { + var item = []; + var dims = this._source.dimensionsDefine; + for (var i = 0; i < dims.length; i++) { + var col = this._data[dims[i].name]; + item.push(col ? col[idx] : null); + } + return item; + }, + appendData: function (newData) { + var data = this._data; + each$1(newData, function (newCol, key) { + var oldCol = data[key] || (data[key] = []); + for (var i = 0; i < (newCol || []).length; i++) { + oldCol.push(newCol[i]); + } + }); + } + }, + + 'original': { + count: countSimply, + getItem: getItemSimply, + appendData: appendDataSimply + }, + + 'typedArray': { + persistent: false, + pure: true, + count: function () { + return this._data ? (this._data.length / this._dimSize) : 0; + }, + getItem: function (idx, out) { + idx = idx - this._offset; + out = out || []; + var offset = this._dimSize * idx; + for (var i = 0; i < this._dimSize; i++) { + out[i] = this._data[offset + i]; + } + return out; + }, + appendData: function (newData) { + if (__DEV__) { + assert$1( + isTypedArray(newData), + 'Added data must be TypedArray if data in initialization is TypedArray' + ); + } + + this._data = newData; + }, + + // Clean self if data is already used. + clean: function () { + // PENDING + this._offset += this.count(); + this._data = null; + } + } +}; + +function countSimply() { + return this._data.length; +} +function getItemSimply(idx) { + return this._data[idx]; +} +function appendDataSimply(newData) { + for (var i = 0; i < newData.length; i++) { + this._data.push(newData[i]); + } +} + + + +var rawValueGetters = { + + arrayRows: getRawValueSimply, + + objectRows: function (dataItem, dataIndex, dimIndex, dimName) { + return dimIndex != null ? dataItem[dimName] : dataItem; + }, + + keyedColumns: getRawValueSimply, + + original: function (dataItem, dataIndex, dimIndex, dimName) { + // FIXME + // In some case (markpoint in geo (geo-map.html)), dataItem + // is {coord: [...]} + var value = getDataItemValue(dataItem); + return (dimIndex == null || !(value instanceof Array)) + ? value + : value[dimIndex]; + }, + + typedArray: getRawValueSimply +}; + +function getRawValueSimply(dataItem, dataIndex, dimIndex, dimName) { + return dimIndex != null ? dataItem[dimIndex] : dataItem; +} + + +var defaultDimValueGetters = { + + arrayRows: getDimValueSimply, + + objectRows: function (dataItem, dimName, dataIndex, dimIndex) { + return converDataValue(dataItem[dimName], this._dimensionInfos[dimName]); + }, + + keyedColumns: getDimValueSimply, + + original: function (dataItem, dimName, dataIndex, dimIndex) { + // Performance sensitive, do not use modelUtil.getDataItemValue. + // If dataItem is an plain object with no value field, the var `value` + // will be assigned with the object, but it will be tread correctly + // in the `convertDataValue`. + var value = dataItem && (dataItem.value == null ? dataItem : dataItem.value); + + // If any dataItem is like { value: 10 } + if (!this._rawData.pure && isDataItemOption(dataItem)) { + this.hasItemOption = true; + } + return converDataValue( + (value instanceof Array) + ? value[dimIndex] + // If value is a single number or something else not array. + : value, + this._dimensionInfos[dimName] + ); + }, + + typedArray: function (dataItem, dimName, dataIndex, dimIndex) { + return dataItem[dimIndex]; + } + +}; + +function getDimValueSimply(dataItem, dimName, dataIndex, dimIndex) { + return converDataValue(dataItem[dimIndex], this._dimensionInfos[dimName]); +} + +/** + * This helper method convert value in data. + * @param {string|number|Date} value + * @param {Object|string} [dimInfo] If string (like 'x'), dimType defaults 'number'. + * If "dimInfo.ordinalParseAndSave", ordinal value can be parsed. + */ +function converDataValue(value, dimInfo) { + // Performance sensitive. + var dimType = dimInfo && dimInfo.type; + if (dimType === 'ordinal') { + // If given value is a category string + var ordinalMeta = dimInfo && dimInfo.ordinalMeta; + return ordinalMeta + ? ordinalMeta.parseAndCollect(value) + : value; + } + + if (dimType === 'time' + // spead up when using timestamp + && typeof value !== 'number' + && value != null + && value !== '-' + ) { + value = +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 + // If string (like '-'), using '+' parse to NaN + // If object, also parse to NaN + : +value; +} + +// ??? FIXME can these logic be more neat: getRawValue, getRawDataItem, +// Consider persistent. +// Caution: why use raw value to display on label or tooltip? +// A reason is to avoid format. For example time value we do not know +// how to format is expected. More over, if stack is used, calculated +// value may be 0.91000000001, which have brings trouble to display. +// TODO: consider how to treat null/undefined/NaN when display? +/** + * @param {module:echarts/data/List} data + * @param {number} dataIndex + * @param {string|number} [dim] dimName or dimIndex + * @return {Array.|string|number} can be null/undefined. + */ +function retrieveRawValue(data, dataIndex, dim) { + if (!data) { + return; + } + + // Consider data may be not persistent. + var dataItem = data.getRawDataItem(dataIndex); + + if (dataItem == null) { + return; + } + + var sourceFormat = data.getProvider().getSource().sourceFormat; + var dimName; + var dimIndex; + + var dimInfo = data.getDimensionInfo(dim); + if (dimInfo) { + dimName = dimInfo.name; + dimIndex = dimInfo.index; + } + + return rawValueGetters[sourceFormat](dataItem, dataIndex, dimIndex, dimName); +} + +/** + * Compatible with some cases (in pie, map) like: + * data: [{name: 'xx', value: 5, selected: true}, ...] + * where only sourceFormat is 'original' and 'objectRows' supported. + * + * ??? TODO + * Supported detail options in data item when using 'arrayRows'. + * + * @param {module:echarts/data/List} data + * @param {number} dataIndex + * @param {string} attr like 'selected' + */ +function retrieveRawAttr(data, dataIndex, attr) { + if (!data) { + return; + } + + var sourceFormat = data.getProvider().getSource().sourceFormat; + + if (sourceFormat !== SOURCE_FORMAT_ORIGINAL + && sourceFormat !== SOURCE_FORMAT_OBJECT_ROWS + ) { + return; + } + + var dataItem = data.getRawDataItem(dataIndex); + if (sourceFormat === SOURCE_FORMAT_ORIGINAL && !isObject$1(dataItem)) { + dataItem = null; + } + if (dataItem) { + return dataItem[attr]; + } +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var DIMENSION_LABEL_REG = /\{@(.+?)\}/g; + +// PENDING A little ugly +var dataFormatMixin = { + /** + * Get params for formatter + * @param {number} dataIndex + * @param {string} [dataType] + * @return {Object} + */ + getDataParams: function (dataIndex, dataType) { + var data = this.getData(dataType); + var rawValue = this.getRawValue(dataIndex, dataType); + var rawDataIndex = data.getRawIndex(dataIndex); + var name = data.getName(dataIndex); + var itemOpt = data.getRawDataItem(dataIndex); + var color = data.getItemVisual(dataIndex, 'color'); + + return { + componentType: this.mainType, + componentSubType: this.subType, + seriesType: this.mainType === 'series' ? this.subType : null, + seriesIndex: this.seriesIndex, + seriesId: this.id, + seriesName: this.name, + name: name, + dataIndex: rawDataIndex, + data: itemOpt, + dataType: dataType, + value: rawValue, + color: color, + marker: getTooltipMarker(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] + * @param {string} [labelProp='label'] + * @return {string} If not formatter, return null/undefined + */ + getFormattedLabel: function (dataIndex, status, dataType, dimIndex, labelProp) { + 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( + status === 'normal' + ? [labelProp || 'label', 'formatter'] + : [status, labelProp || 'label', 'formatter'] + ); + + if (typeof formatter === 'function') { + params.status = status; + return formatter(params); + } + else if (typeof formatter === 'string') { + var str = formatTpl(formatter, params); + + // Support 'aaa{@[3]}bbb{@product}ccc'. + // Do not support '}' in dim name util have to. + return str.replace(DIMENSION_LABEL_REG, function (origin, dim) { + var len = dim.length; + if (dim.charAt(0) === '[' && dim.charAt(len - 1) === ']') { + dim = +dim.slice(1, len - 1); // Also: '[]' => 0 + } + return retrieveRawValue(data, dataIndex, dim); + }); + } + }, + + /** + * Get raw value in option + * @param {number} idx + * @param {string} [dataType] + * @return {Array|number|string} + */ + getRawValue: function (idx, dataType) { + return retrieveRawValue(this.getData(dataType), idx); + }, + + /** + * Should be implemented. + * @param {number} dataIndex + * @param {boolean} [multipleSeries=false] + * @param {number} [dataType] + * @return {string} tooltip string + */ + formatTooltip: function () { + // Empty function + } +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * @param {Object} define + * @return See the return of `createTask`. + */ +function createTask(define) { + return new Task(define); +} + +/** + * @constructor + * @param {Object} define + * @param {Function} define.reset Custom reset + * @param {Function} [define.plan] Returns 'reset' indicate reset immediately. + * @param {Function} [define.count] count is used to determin data task. + * @param {Function} [define.onDirty] count is used to determin data task. + */ +function Task(define) { + define = define || {}; + + this._reset = define.reset; + this._plan = define.plan; + this._count = define.count; + this._onDirty = define.onDirty; + + this._dirty = true; + + // Context must be specified implicitly, to + // avoid miss update context when model changed. + this.context; +} + +var taskProto = Task.prototype; + +/** + * @param {Object} performArgs + * @param {number} [performArgs.step] Specified step. + * @param {number} [performArgs.skip] Skip customer perform call. + * @param {number} [performArgs.modBy] Sampling window size. + * @param {number} [performArgs.modDataCount] Sampling count. + */ +taskProto.perform = function (performArgs) { + var upTask = this._upstream; + var skip = performArgs && performArgs.skip; + + // TODO some refactor. + // Pull data. Must pull data each time, because context.data + // may be updated by Series.setData. + if (this._dirty && upTask) { + var context = this.context; + context.data = context.outputData = upTask.context.outputData; + } + + if (this.__pipeline) { + this.__pipeline.currentTask = this; + } + + var planResult; + if (this._plan && !skip) { + planResult = this._plan(this.context); + } + + // Support sharding by mod, which changes the render sequence and makes the rendered graphic + // elements uniformed distributed when progress, especially when moving or zooming. + var lastModBy = normalizeModBy(this._modBy); + var lastModDataCount = this._modDataCount || 0; + var modBy = normalizeModBy(performArgs && performArgs.modBy); + var modDataCount = performArgs && performArgs.modDataCount || 0; + if (lastModBy !== modBy || lastModDataCount !== modDataCount) { + planResult = 'reset'; + } + + function normalizeModBy(val) { + !(val >= 1) && (val = 1); // jshint ignore:line + return val; + } + + var forceFirstProgress; + if (this._dirty || planResult === 'reset') { + this._dirty = false; + forceFirstProgress = reset(this, skip); + } + + this._modBy = modBy; + this._modDataCount = modDataCount; + + var step = performArgs && performArgs.step; + + if (upTask) { + + if (__DEV__) { + assert$1(upTask._outputDueEnd != null); + } + this._dueEnd = upTask._outputDueEnd; + } + // DataTask or overallTask + else { + if (__DEV__) { + assert$1(!this._progress || this._count); + } + this._dueEnd = this._count ? this._count(this.context) : Infinity; + } + + // Note: Stubs, that its host overall task let it has progress, has progress. + // If no progress, pass index from upstream to downstream each time plan called. + if (this._progress) { + var start = this._dueIndex; + var end = Math.min( + step != null ? this._dueIndex + step : Infinity, + this._dueEnd + ); + + if (!skip && (forceFirstProgress || start < end)) { + var progress = this._progress; + if (isArray(progress)) { + for (var i = 0; i < progress.length; i++) { + doProgress(this, progress[i], start, end, modBy, modDataCount); + } + } + else { + doProgress(this, progress, start, end, modBy, modDataCount); + } + } + + this._dueIndex = end; + // If no `outputDueEnd`, assume that output data and + // input data is the same, so use `dueIndex` as `outputDueEnd`. + var outputDueEnd = this._settedOutputEnd != null + ? this._settedOutputEnd : end; + + if (__DEV__) { + // ??? Can not rollback. + assert$1(outputDueEnd >= this._outputDueEnd); + } + + this._outputDueEnd = outputDueEnd; + } + else { + // (1) Some overall task has no progress. + // (2) Stubs, that its host overall task do not let it has progress, has no progress. + // This should always be performed so it can be passed to downstream. + this._dueIndex = this._outputDueEnd = this._settedOutputEnd != null + ? this._settedOutputEnd : this._dueEnd; + } + + return this.unfinished(); +}; + +var iterator = (function () { + + var end; + var current; + var modBy; + var modDataCount; + var winCount; + + var it = { + reset: function (s, e, sStep, sCount) { + current = s; + end = e; + + modBy = sStep; + modDataCount = sCount; + winCount = Math.ceil(modDataCount / modBy); + + it.next = (modBy > 1 && modDataCount > 0) ? modNext : sequentialNext; + } + }; + + return it; + + function sequentialNext() { + return current < end ? current++ : null; + } + + function modNext() { + var dataIndex = (current % winCount) * modBy + Math.ceil(current / winCount); + var result = current >= end + ? null + : dataIndex < modDataCount + ? dataIndex + // If modDataCount is smaller than data.count() (consider `appendData` case), + // Use normal linear rendering mode. + : current; + current++; + return result; + } +})(); + +taskProto.dirty = function () { + this._dirty = true; + this._onDirty && this._onDirty(this.context); +}; + +function doProgress(taskIns, progress, start, end, modBy, modDataCount) { + iterator.reset(start, end, modBy, modDataCount); + taskIns._callingProgress = progress; + taskIns._callingProgress({ + start: start, end: end, count: end - start, next: iterator.next + }, taskIns.context); +} + +function reset(taskIns, skip) { + taskIns._dueIndex = taskIns._outputDueEnd = taskIns._dueEnd = 0; + taskIns._settedOutputEnd = null; + + var progress; + var forceFirstProgress; + + if (!skip && taskIns._reset) { + progress = taskIns._reset(taskIns.context); + if (progress && progress.progress) { + forceFirstProgress = progress.forceFirstProgress; + progress = progress.progress; + } + // To simplify no progress checking, array must has item. + if (isArray(progress) && !progress.length) { + progress = null; + } + } + + taskIns._progress = progress; + taskIns._modBy = taskIns._modDataCount = null; + + var downstream = taskIns._downstream; + downstream && downstream.dirty(); + + return forceFirstProgress; +} + +/** + * @return {boolean} + */ +taskProto.unfinished = function () { + return this._progress && this._dueIndex < this._dueEnd; +}; + +/** + * @param {Object} downTask The downstream task. + * @return {Object} The downstream task. + */ +taskProto.pipe = function (downTask) { + if (__DEV__) { + assert$1(downTask && !downTask._disposed && downTask !== this); + } + + // If already downstream, do not dirty downTask. + if (this._downstream !== downTask || this._dirty) { + this._downstream = downTask; + downTask._upstream = this; + downTask.dirty(); + } +}; + +taskProto.dispose = function () { + if (this._disposed) { + return; + } + + this._upstream && (this._upstream._downstream = null); + this._downstream && (this._downstream._upstream = null); + + this._dirty = false; + this._disposed = true; +}; + +taskProto.getUpstream = function () { + return this._upstream; +}; + +taskProto.getDownstream = function () { + return this._downstream; +}; + +taskProto.setOutputEnd = function (end) { + // This only happend in dataTask, dataZoom, map, currently. + // where dataZoom do not set end each time, but only set + // when reset. So we should record the setted end, in case + // that the stub of dataZoom perform again and earse the + // setted end by upstream. + this._outputDueEnd = this._settedOutputEnd = end; +}; + + +/////////////////////////////////////////////////////////// +// For stream debug (Should be commented out after used!) +// Usage: printTask(this, 'begin'); +// Usage: printTask(this, null, {someExtraProp}); +// function printTask(task, prefix, extra) { +// window.ecTaskUID == null && (window.ecTaskUID = 0); +// task.uidDebug == null && (task.uidDebug = `task_${window.ecTaskUID++}`); +// task.agent && task.agent.uidDebug == null && (task.agent.uidDebug = `task_${window.ecTaskUID++}`); +// var props = []; +// if (task.__pipeline) { +// var val = `${task.__idxInPipeline}/${task.__pipeline.tail.__idxInPipeline} ${task.agent ? '(stub)' : ''}`; +// props.push({text: 'idx', value: val}); +// } else { +// var stubCount = 0; +// task.agentStubMap.each(() => stubCount++); +// props.push({text: 'idx', value: `overall (stubs: ${stubCount})`}); +// } +// props.push({text: 'uid', value: task.uidDebug}); +// if (task.__pipeline) { +// props.push({text: 'pid', value: task.__pipeline.id}); +// task.agent && props.push( +// {text: 'stubFor', value: task.agent.uidDebug} +// ); +// } +// props.push( +// {text: 'dirty', value: task._dirty}, +// {text: 'dueIndex', value: task._dueIndex}, +// {text: 'dueEnd', value: task._dueEnd}, +// {text: 'outputDueEnd', value: task._outputDueEnd} +// ); +// if (extra) { +// Object.keys(extra).forEach(key => { +// props.push({text: key, value: extra[key]}); +// }); +// } +// var args = ['color: blue']; +// var msg = `%c[${prefix || 'T'}] %c` + props.map(item => ( +// args.push('color: black', 'color: red'), +// `${item.text}: %c${item.value}` +// )).join('%c, '); +// console.log.apply(console, [msg].concat(args)); +// // console.log(this); +// } + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var inner$4 = makeInner(); + +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.color', + + /** + * 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, + + init: function (option, parentModel, ecModel, extraOpt) { + + /** + * @type {number} + * @readOnly + */ + this.seriesIndex = this.componentIndex; + + this.dataTask = createTask({ + count: dataTaskCount, + reset: dataTaskReset + }); + this.dataTask.context = {model: this}; + + this.mergeDefaultAndTheme(option, ecModel); + + prepareSource(this); + + + var data = this.getInitialData(option, ecModel); + wrapData(data, this); + this.dataTask.context.data = data; + + if (__DEV__) { + assert$1(data, 'getInitialData returned invalid data.'); + } + + /** + * @type {module:echarts/data/List|module:echarts/data/Tree|module:echarts/data/Graph} + * @private + */ + inner$4(this).dataBeforeProcessed = data; + + // If we reverse the order (make data firstly, and then make + // dataBeforeProcessed by cloneShallow), cloneShallow will + // cause data.graph.data !== data when using + // module:echarts/data/Graph or module:echarts/data/Tree. + // See module:echarts/data/helper/linkList + + // Theoretically, it is unreasonable to call `seriesModel.getData()` in the model + // init or merge stage, because the data can be restored. So we do not `restoreData` + // and `setData` here, which forbids calling `seriesModel.getData()` in this stage. + // Call `seriesModel.getRawData()` instead. + // this.restoreData(); + + autoSeriesName(this); + }, + + /** + * Util for merge default and theme to option + * @param {Object} option + * @param {module:echarts/model/Global} ecModel + */ + mergeDefaultAndTheme: function (option, ecModel) { + var layoutMode = this.layoutMode; + var inputPositionParams = layoutMode + ? getLayoutParams(option) : {}; + + // Backward compat: using subType on theme. + // But if name duplicate between series subType + // (for example: parallel) add component mainType, + // add suffix 'Series'. + var themeSubType = this.subType; + if (ComponentModel.hasClass(themeSubType)) { + themeSubType += 'Series'; + } + merge( + option, + ecModel.getTheme().get(this.subType) + ); + merge(option, this.getDefaultOption()); + + // Default label emphasis `show` + defaultEmphasis(option, 'label', ['show']); + + this.fillDataTextStyle(option.data); + + if (layoutMode) { + mergeLayoutParam(option, inputPositionParams, layoutMode); + } + }, + + mergeOption: function (newSeriesOption, ecModel) { + // this.settingTask.dirty(); + + newSeriesOption = merge(this.option, newSeriesOption, true); + this.fillDataTextStyle(newSeriesOption.data); + + var layoutMode = this.layoutMode; + if (layoutMode) { + mergeLayoutParam(this.option, newSeriesOption, layoutMode); + } + + prepareSource(this); + + var data = this.getInitialData(newSeriesOption, ecModel); + wrapData(data, this); + this.dataTask.dirty(); + this.dataTask.context.data = data; + + inner$4(this).dataBeforeProcessed = data; + + autoSeriesName(this); + }, + + fillDataTextStyle: function (data) { + // Default data label emphasis `show` + // FIXME Tree structure data ? + // FIXME Performance ? + if (data && !isTypedArray(data)) { + var props = ['show']; + for (var i = 0; i < data.length; i++) { + if (data[i] && data[i].label) { + defaultEmphasis(data[i], 'label', props); + } + } + } + }, + + /** + * Init a data structure from data related option in series + * Must be overwritten + */ + getInitialData: function () {}, + + /** + * Append data to list + * @param {Object} params + * @param {Array|TypedArray} params.data + */ + appendData: function (params) { + // FIXME ??? + // (1) If data from dataset, forbidden append. + // (2) support append data of dataset. + var data = this.getRawData(); + data.appendData(params.data); + }, + + /** + * Consider some method like `filter`, `map` need make new data, + * We should make sure that `seriesModel.getData()` get correct + * data in the stream procedure. So we fetch data from upstream + * each time `task.perform` called. + * @param {string} [dataType] + * @return {module:echarts/data/List} + */ + getData: function (dataType) { + var task = getCurrentTask(this); + if (task) { + var data = task.context.data; + return dataType == null ? data : data.getLinkedData(dataType); + } + else { + // When series is not alive (that may happen when click toolbox + // restore or setOption with not merge mode), series data may + // be still need to judge animation or something when graphic + // elements want to know whether fade out. + return inner$4(this).data; + } + }, + + /** + * @param {module:echarts/data/List} data + */ + setData: function (data) { + var task = getCurrentTask(this); + if (task) { + var context = task.context; + // Consider case: filter, data sample. + if (context.data !== data && task.modifyOutputEnd) { + task.setOutputEnd(data.count()); + } + context.outputData = data; + // Caution: setData should update context.data, + // Because getData may be called multiply in a + // single stage and expect to get the data just + // set. (For example, AxisProxy, x y both call + // getData and setDate sequentially). + // So the context.data should be fetched from + // upstream each time when a stage starts to be + // performed. + if (task !== this.dataTask) { + context.data = data; + } + } + inner$4(this).data = data; + }, + + /** + * @see {module:echarts/data/helper/sourceHelper#getSource} + * @return {module:echarts/data/Source} source + */ + getSource: function () { + return getSource(this); + }, + + /** + * Get data before processed + * @return {module:echarts/data/List} + */ + getRawData: function () { + return inner$4(this).dataBeforeProcessed; + }, + + /** + * 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) { + // ??? TODO refactor these logic. + // check: category-no-encode-has-axis-data in dataset.html + var vertially = reduce(value, function (vertially, val, idx) { + var dimItem = data.getDimensionInfo(idx); + return vertially |= dimItem && dimItem.tooltip !== false && dimItem.displayName != null; + }, 0); + + var result = []; + + tooltipDims.length + ? each$1(tooltipDims, function (dim) { + setEachItem(retrieveRawValue(data, dataIndex, dim), dim); + }) + // By default, all dims is used on tooltip. + : each$1(value, setEachItem); + + function setEachItem(val, dim) { + var dimInfo = data.getDimensionInfo(dim); + // If `dimInfo.tooltip` is not set, show tooltip. + if (!dimInfo || dimInfo.otherDims.tooltip === false) { + return; + } + var dimType = dimInfo.type; + var dimHead = getTooltipMarker({color: color, type: 'subItem'}); + var valStr = (vertially + ? dimHead + encodeHTML(dimInfo.displayName || '-') + ': ' + : '' + ) + // FIXME should not format time for raw data? + + encodeHTML(dimType === 'ordinal' + ? val + '' + : dimType === 'time' + ? (multipleSeries ? '' : formatTime('yyyy/MM/dd hh:mm:ss', val)) + : addCommas(val) + ); + valStr && result.push(valStr); + } + + return (vertially ? '
' : '') + result.join(vertially ? '
' : ', '); + } + + function formatSingleValue(val) { + return encodeHTML(addCommas(val)); + } + + var data = this.getData(); + var tooltipDims = data.mapDimension('defaultedTooltip', true); + var tooltipDimLen = tooltipDims.length; + var value = this.getRawValue(dataIndex); + var isValueArr = isArray(value); + + var color = data.getItemVisual(dataIndex, 'color'); + if (isObject$1(color) && color.colorStops) { + color = (color.colorStops[0] || {}).color; + } + color = color || 'transparent'; + + // Complicated rule for pretty tooltip. + var formattedValue = (tooltipDimLen > 1 || (isValueArr && !tooltipDimLen)) + ? formatArrayValue(value) + : tooltipDimLen + ? formatSingleValue(retrieveRawValue(data, dataIndex, tooltipDims[0])) + : formatSingleValue(isValueArr ? value[0] : value); + + var colorEl = getTooltipMarker(color); + + var name = data.getName(dataIndex); + + var seriesName = this.name; + if (!isNameSpecified(this)) { + seriesName = ''; + } + seriesName = seriesName + ? encodeHTML(seriesName) + (!multipleSeries ? '
' : ': ') + : ''; + + return !multipleSeries + ? seriesName + colorEl + + (name + ? encodeHTML(name) + ': ' + formattedValue + : formattedValue + ) + : colorEl + seriesName + formattedValue; + }, + + /** + * @return {boolean} + */ + isAnimationEnabled: function () { + if (env$1.node) { + return false; + } + + var animationEnabled = this.getShallow('animation'); + if (animationEnabled) { + if (this.getData().count() > this.getShallow('animationThreshold')) { + animationEnabled = false; + } + } + return animationEnabled; + }, + + restoreData: function () { + this.dataTask.dirty(); + }, + + getColorFromPalette: function (name, scope, requestColorNum) { + var ecModel = this.ecModel; + // PENDING + var color = colorPaletteMixin.getColorFromPalette.call(this, name, scope, requestColorNum); + if (!color) { + color = ecModel.getColorFromPalette(name, scope, requestColorNum); + } + return color; + }, + + /** + * Use `data.mapDimension(coordDim, true)` instead. + * @deprecated + */ + coordDimToDataDim: function (coordDim) { + return this.getRawData().mapDimension(coordDim, true); + }, + + /** + * Get progressive rendering count each step + * @return {number} + */ + getProgressive: function () { + return this.get('progressive'); + }, + + /** + * Get progressive rendering count each step + * @return {number} + */ + getProgressiveThreshold: function () { + return this.get('progressiveThreshold'); + }, + + /** + * Get data indices for show tooltip content. See tooltip. + * @abstract + * @param {Array.|string} dim + * @param {Array.} value + * @param {module:echarts/coord/single/SingleAxis} baseAxis + * @return {Object} {dataIndices, nestestValue}. + */ + getAxisTooltipData: null, + + /** + * See tooltip. + * @abstract + * @param {number} dataIndex + * @return {Array.} Point of tooltip. null/undefined can be returned. + */ + getTooltipPosition: null, + + /** + * @see {module:echarts/stream/Scheduler} + */ + pipeTask: null, + + /** + * Convinient for override in extended class. + * @protected + * @type {Function} + */ + preventIncremental: null, + + /** + * @public + * @readOnly + * @type {Object} + */ + pipelineContext: null + +}); + + +mixin(SeriesModel, dataFormatMixin); +mixin(SeriesModel, colorPaletteMixin); + +/** + * MUST be called after `prepareSource` called + * Here we need to make auto series, especially for auto legend. But we + * do not modify series.name in option to avoid side effects. + */ +function autoSeriesName(seriesModel) { + // User specified name has higher priority, otherwise it may cause + // series can not be queried unexpectedly. + var name = seriesModel.name; + if (!isNameSpecified(seriesModel)) { + seriesModel.name = getSeriesAutoName(seriesModel) || name; + } +} + +function getSeriesAutoName(seriesModel) { + var data = seriesModel.getRawData(); + var dataDims = data.mapDimension('seriesName', true); + var nameArr = []; + each$1(dataDims, function (dataDim) { + var dimInfo = data.getDimensionInfo(dataDim); + dimInfo.displayName && nameArr.push(dimInfo.displayName); + }); + return nameArr.join(' '); +} + +function dataTaskCount(context) { + return context.model.getRawData().count(); +} + +function dataTaskReset(context) { + var seriesModel = context.model; + seriesModel.setData(seriesModel.getRawData().cloneShallow()); + return dataTaskProgress; +} + +function dataTaskProgress(param, context) { + // Avoid repead cloneShallow when data just created in reset. + if (param.end > context.outputData.count()) { + context.model.getRawData().cloneShallow(context.outputData); + } +} + +// TODO refactor +function wrapData(data, seriesModel) { + each$1(data.CHANGABLE_METHODS, function (methodName) { + data.wrapMethod(methodName, curry(onDataSelfChange, seriesModel)); + }); +} + +function onDataSelfChange(seriesModel) { + var task = getCurrentTask(seriesModel); + if (task) { + // Consider case: filter, selectRange + task.setOutputEnd(this.count()); + } +} + +function getCurrentTask(seriesModel) { + var scheduler = (seriesModel.ecModel || {}).scheduler; + var pipeline = scheduler && scheduler.getPipeline(seriesModel.uid); + + if (pipeline) { + // When pipline finished, the currrentTask keep the last + // task (renderTask). + var task = pipeline.currentTask; + if (task) { + var agentStubMap = task.agentStubMap; + if (agentStubMap) { + task = agentStubMap.get(seriesModel.uid); + } + } + return task; + } +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var Component = function () { + /** + * @type {module:zrender/container/Group} + * @readOnly + */ + this.group = new Group(); + + /** + * @type {string} + * @readOnly + */ + this.uid = 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. +enableClassExtend(Component); + +// Enable capability of registerClass, getClass, hasClass, registerSubTypeDefaulter and so on. +enableClassManagement(Component, {registerWhenExtend: true}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * @return {string} If large mode changed, return string 'reset'; + */ +var createRenderPlanner = function () { + var inner = makeInner(); + + return function (seriesModel) { + var fields = inner(seriesModel); + var pipelineContext = seriesModel.pipelineContext; + + var originalLarge = fields.large; + var originalProgressive = fields.progressiveRender; + + var large = fields.large = pipelineContext.large; + var progressive = fields.progressiveRender = pipelineContext.progressiveRender; + + return !!((originalLarge ^ large) || (originalProgressive ^ progressive)) && 'reset'; + }; +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var inner$5 = makeInner(); +var renderPlanner = createRenderPlanner(); + +function Chart() { + + /** + * @type {module:zrender/container/Group} + * @readOnly + */ + this.group = new Group(); + + /** + * @type {string} + * @readOnly + */ + this.uid = getUID('viewChart'); + + this.renderTask = createTask({ + plan: renderTaskPlan, + reset: renderTaskReset + }); + this.renderTask.context = {view: this}; +} + +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 () {}, + + /** + * Rendering preparation in progressive mode. + * @param {module:echarts/model/Series} seriesModel + * @param {module:echarts/model/Global} ecModel + * @param {module:echarts/ExtensionAPI} api + * @param {Object} payload + */ + incrementalPrepareRender: null, + + /** + * Render in progressive mode. + * @param {module:echarts/model/Series} seriesModel + * @param {module:echarts/model/Global} ecModel + * @param {module:echarts/ExtensionAPI} api + * @param {Object} payload + */ + incrementalRender: null, + + /** + * Update transform directly. + * @param {module:echarts/model/Series} seriesModel + * @param {module:echarts/model/Global} ecModel + * @param {module:echarts/ExtensionAPI} api + * @param {Object} payload + * @return {Object} {update: true} + */ + updateTransform: null + + /** + * The view contains the given point. + * @interface + * @param {Array.} point + * @return {boolean} + */ + // containPoint: 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' + */ +function toggleHighlight(data, payload, state) { + var dataIndex = queryDataIndex(data, payload); + + if (dataIndex != null) { + each$1(normalizeToArray(dataIndex), function (dataIdx) { + elSetState(data.getItemGraphicEl(dataIdx), state); + }); + } + else { + data.eachItemGraphicEl(function (el) { + elSetState(el, state); + }); + } +} + +// Enable Chart.extend. +enableClassExtend(Chart, ['dispose']); + +// Add capability of registerClass, getClass, hasClass, registerSubTypeDefaulter and so on. +enableClassManagement(Chart, {registerWhenExtend: true}); + +Chart.markUpdateMethod = function (payload, methodName) { + inner$5(payload).updateMethod = methodName; +}; + +function renderTaskPlan(context) { + return renderPlanner(context.model); +} + +function renderTaskReset(context) { + var seriesModel = context.model; + var ecModel = context.ecModel; + var api = context.api; + var payload = context.payload; + // ???! remove updateView updateVisual + var progressiveRender = seriesModel.pipelineContext.progressiveRender; + var view = context.view; + + var updateMethod = payload && inner$5(payload).updateMethod; + var methodName = progressiveRender + ? 'incrementalPrepareRender' + : (updateMethod && view[updateMethod]) + ? updateMethod + // `appendData` is also supported when data amount + // is less than progressive threshold. + : 'render'; + + if (methodName !== 'render') { + view[methodName](seriesModel, ecModel, api, payload); + } + + return progressMethodMap[methodName]; +} + +var progressMethodMap = { + incrementalPrepareRender: { + progress: function (params, context) { + context.view.incrementalRender( + params, context.model, context.ecModel, context.api, context.payload + ); + } + }, + render: { + // Put view.render in `progress` to support appendData. But in this case + // view.render should not be called in reset, otherwise it will be called + // twise. Use `forceFirstProgress` to make sure that view.render is called + // in any cases. + forceFirstProgress: true, + progress: function (params, context) { + context.view.render( + context.model, context.ecModel, context.api, context.payload + ); + } + } +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + + +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. + */ +function throttle(fn, delay, debounce) { + + var currCall; + var lastCall = 0; + var lastExec = 0; + var timer = null; + var diff; + var scope; + var args; + var debounceNextCall; + + 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; + var thisDelay = debounceNextCall || delay; + var thisDebounce = debounceNextCall || debounce; + debounceNextCall = null; + diff = currCall - (thisDebounce ? lastCall : lastExec) - thisDelay; + + clearTimeout(timer); + + // Here we should make sure that: the `exec` SHOULD NOT be called later + // than a new call of `cb`, that is, preserving the command order. Consider + // calculating "scale rate" when roaming as an example. When a call of `cb` + // happens, either the `exec` is called dierectly, or the call is delayed. + // But the delayed call should never be later than next call of `cb`. Under + // this assurance, we can simply update view state each time `dispatchAction` + // triggered by user roaming, but not need to add extra code to avoid the + // state being "rolled-back". + if (thisDebounce) { + timer = setTimeout(exec, thisDelay); + } + else { + if (diff >= 0) { + exec(); + } + else { + timer = setTimeout(exec, -diff); + } + } + + lastCall = currCall; + }; + + /** + * Clear throttle. + * @public + */ + cb.clear = function () { + if (timer) { + clearTimeout(timer); + timer = null; + } + }; + + /** + * Enable debounce once. + */ + cb.debounceNextCall = function (debounceDelay) { + debounceNextCall = debounceDelay; + }; + + 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. + */ +function createOrUpdate(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] = 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 + */ +function clear(obj, fnAttr) { + var fn = obj[fnAttr]; + if (fn && fn[ORIGIN_METHOD]) { + obj[fnAttr] = fn[ORIGIN_METHOD]; + } +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var seriesColor = { + createOnAllSeries: true, + performRawSeries: true, + reset: function (seriesModel, ecModel) { + var data = seriesModel.getData(); + var colorAccessPath = (seriesModel.visualColorAccessPath || 'itemStyle.color').split('.'); + var color = seriesModel.get(colorAccessPath) // Set in itemStyle + || seriesModel.getColorFromPalette( + // TODO series count changed. + seriesModel.name, null, ecModel.getSeriesCount() + ); // 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 + var dataEach = function (data, idx) { + var itemModel = data.getItemModel(idx); + var color = itemModel.get(colorAccessPath, true); + if (color != null) { + data.setItemVisual(idx, 'color', color); + } + }; + + return { dataEach: data.hasItemOption ? dataEach : null }; + } + } +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + + +var lang = { + toolbox: { + brush: { + title: { + rect: '矩形选择', + polygon: '圈选', + lineX: '横向选择', + lineY: '纵向选择', + keep: '保持选择', + clear: '清除选择' + } + }, + dataView: { + title: '数据视图', + lang: ['数据视图', '关闭', '刷新'] + }, + dataZoom: { + title: { + zoom: '区域缩放', + back: '区域缩放还原' + } + }, + magicType: { + title: { + line: '切换为折线图', + bar: '切换为柱状图', + stack: '切换为堆叠', + tiled: '切换为平铺' + } + }, + restore: { + title: '还原' + }, + saveAsImage: { + title: '保存为图片', + lang: ['右键另存为图片'] + } + }, + series: { + typeNames: { + pie: '饼图', + bar: '柱状图', + line: '折线图', + scatter: '散点图', + effectScatter: '涟漪散点图', + radar: '雷达图', + tree: '树图', + treemap: '矩形树图', + boxplot: '箱型图', + candlestick: 'K线图', + k: 'K线图', + heatmap: '热力图', + map: '地图', + parallel: '平行坐标图', + lines: '线图', + graph: '关系图', + sankey: '桑基图', + funnel: '漏斗图', + gauge: '仪表盘图', + pictorialBar: '象形柱图', + themeRiver: '主题河流图', + sunburst: '旭日图' + } + }, + aria: { + general: { + withTitle: '这是一个关于“{title}”的图表。', + withoutTitle: '这是一个图表,' + }, + series: { + single: { + prefix: '', + withName: '图表类型是{seriesType},表示{seriesName}。', + withoutName: '图表类型是{seriesType}。' + }, + multiple: { + prefix: '它由{seriesCount}个图表系列组成。', + withName: '第{seriesId}个系列是一个表示{seriesName}的{seriesType},', + withoutName: '第{seriesId}个系列是一个{seriesType},', + separator: { + middle: ';', + end: '。' + } + } + }, + data: { + allData: '其数据是——', + partialData: '其中,前{displayCnt}项是——', + withName: '{name}的数据是{value}', + withoutName: '{value}', + separator: { + middle: ',', + end: '' + } + } + } +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var aria = function (dom, ecModel) { + var ariaModel = ecModel.getModel('aria'); + if (!ariaModel.get('show')) { + return; + } + else if (ariaModel.get('description')) { + dom.setAttribute('aria-label', ariaModel.get('description')); + return; + } + + var seriesCnt = 0; + ecModel.eachSeries(function (seriesModel, idx) { + ++seriesCnt; + }, this); + + var maxDataCnt = ariaModel.get('data.maxCount') || 10; + var maxSeriesCnt = ariaModel.get('series.maxCount') || 10; + var displaySeriesCnt = Math.min(seriesCnt, maxSeriesCnt); + + var ariaLabel; + if (seriesCnt < 1) { + // No series, no aria label + return; + } + else { + var title = getTitle(); + if (title) { + ariaLabel = replace(getConfig('general.withTitle'), { + title: title + }); + } + else { + ariaLabel = getConfig('general.withoutTitle'); + } + + var seriesLabels = []; + var prefix = seriesCnt > 1 + ? 'series.multiple.prefix' + : 'series.single.prefix'; + ariaLabel += replace(getConfig(prefix), { seriesCount: seriesCnt }); + + ecModel.eachSeries(function (seriesModel, idx) { + if (idx < displaySeriesCnt) { + var seriesLabel; + + var seriesName = seriesModel.get('name'); + var seriesTpl = 'series.' + + (seriesCnt > 1 ? 'multiple' : 'single') + '.'; + seriesLabel = getConfig(seriesName + ? seriesTpl + 'withName' + : seriesTpl + 'withoutName'); + + seriesLabel = replace(seriesLabel, { + seriesId: seriesModel.seriesIndex, + seriesName: seriesModel.get('name'), + seriesType: getSeriesTypeName(seriesModel.subType) + }); + + var data = seriesModel.getData(); + window.data = data; + if (data.count() > maxDataCnt) { + // Show part of data + seriesLabel += replace(getConfig('data.partialData'), { + displayCnt: maxDataCnt + }); + } + else { + seriesLabel += getConfig('data.allData'); + } + + var dataLabels = []; + for (var i = 0; i < data.count(); i++) { + if (i < maxDataCnt) { + var name = data.getName(i); + var value = retrieveRawValue(data, i); + dataLabels.push( + replace( + name + ? getConfig('data.withName') + : getConfig('data.withoutName'), + { + name: name, + value: value + } + ) + ); + } + } + seriesLabel += dataLabels + .join(getConfig('data.separator.middle')) + + getConfig('data.separator.end'); + + seriesLabels.push(seriesLabel); + } + }); + + ariaLabel += seriesLabels + .join(getConfig('series.multiple.separator.middle')) + + getConfig('series.multiple.separator.end'); + + dom.setAttribute('aria-label', ariaLabel); + } + + function replace(str, keyValues) { + if (typeof str !== 'string') { + return str; + } + + var result = str; + each$1(keyValues, function (value, key) { + result = result.replace( + new RegExp('\\{\\s*' + key + '\\s*\\}', 'g'), + value + ); + }); + return result; + } + + function getConfig(path) { + var userConfig = ariaModel.get(path); + if (userConfig == null) { + var pathArr = path.split('.'); + var result = lang.aria; + for (var i = 0; i < pathArr.length; ++i) { + result = result[pathArr[i]]; + } + return result; + } + else { + return userConfig; + } + } + + function getTitle() { + var title = ecModel.getModel('title').option; + if (title && title.length) { + title = title[0]; + } + return title && title.text; + } + + function getSeriesTypeName(type) { + return lang.series.typeNames[type] || '自定义图'; + } +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var PI$1 = 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} + */ +var loadingDefault = function (api, opts) { + opts = opts || {}; + defaults(opts, { + text: 'loading', + color: '#c23531', + textColor: '#000', + maskColor: 'rgba(255, 255, 255, 0.8)', + zlevel: 0 + }); + var mask = new Rect({ + style: { + fill: opts.maskColor + }, + zlevel: opts.zlevel, + z: 10000 + }); + var arc = new Arc({ + shape: { + startAngle: -PI$1 / 2, + endAngle: -PI$1 / 2 + 0.1, + r: 10 + }, + style: { + stroke: opts.color, + lineCap: 'round', + lineWidth: 5 + }, + zlevel: opts.zlevel, + z: 10001 + }); + var labelRect = new 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$1 * 3 / 2 + }) + .start('circularInOut'); + arc.animateShape(true) + .when(1000, { + startAngle: PI$1 * 3 / 2 + }) + .delay(300) + .start('circularInOut'); + + var group = new 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; +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * @module echarts/stream/Scheduler + */ + +/** + * @constructor + */ +function Scheduler(ecInstance, api, dataProcessorHandlers, visualHandlers) { + this.ecInstance = ecInstance; + this.api = api; + this.unfinished; + + // Fix current processors in case that in some rear cases that + // processors might be registered after echarts instance created. + // Register processors incrementally for a echarts instance is + // not supported by this stream architecture. + var dataProcessorHandlers = this._dataProcessorHandlers = dataProcessorHandlers.slice(); + var visualHandlers = this._visualHandlers = visualHandlers.slice(); + this._allHandlers = dataProcessorHandlers.concat(visualHandlers); + + /** + * @private + * @type { + * [handlerUID: string]: { + * seriesTaskMap?: { + * [seriesUID: string]: Task + * }, + * overallTask?: Task + * } + * } + */ + this._stageTaskMap = createHashMap(); +} + +var proto = Scheduler.prototype; + +/** + * @param {module:echarts/model/Global} ecModel + * @param {Object} payload + */ +proto.restoreData = function (ecModel, payload) { + // TODO: Only restroe needed series and components, but not all components. + // Currently `restoreData` of all of the series and component will be called. + // But some independent components like `title`, `legend`, `graphic`, `toolbox`, + // `tooltip`, `axisPointer`, etc, do not need series refresh when `setOption`, + // and some components like coordinate system, axes, dataZoom, visualMap only + // need their target series refresh. + // (1) If we are implementing this feature some day, we should consider these cases: + // if a data processor depends on a component (e.g., dataZoomProcessor depends + // on the settings of `dataZoom`), it should be re-performed if the component + // is modified by `setOption`. + // (2) If a processor depends on sevral series, speicified by its `getTargetSeries`, + // it should be re-performed when the result array of `getTargetSeries` changed. + // We use `dependencies` to cover these issues. + // (3) How to update target series when coordinate system related components modified. + + // TODO: simply the dirty mechanism? Check whether only the case here can set tasks dirty, + // and this case all of the tasks will be set as dirty. + + ecModel.restoreData(payload); + + // Theoretically an overall task not only depends on each of its target series, but also + // depends on all of the series. + // The overall task is not in pipeline, and `ecModel.restoreData` only set pipeline tasks + // dirty. If `getTargetSeries` of an overall task returns nothing, we should also ensure + // that the overall task is set as dirty and to be performed, otherwise it probably cause + // state chaos. So we have to set dirty of all of the overall tasks manually, otherwise it + // probably cause state chaos (consider `dataZoomProcessor`). + this._stageTaskMap.each(function (taskRecord) { + var overallTask = taskRecord.overallTask; + overallTask && overallTask.dirty(); + }); +}; + +// If seriesModel provided, incremental threshold is check by series data. +proto.getPerformArgs = function (task, isBlock) { + // For overall task + if (!task.__pipeline) { + return; + } + + var pipeline = this._pipelineMap.get(task.__pipeline.id); + var pCtx = pipeline.context; + var incremental = !isBlock + && pipeline.progressiveEnabled + && (!pCtx || pCtx.progressiveRender) + && task.__idxInPipeline > pipeline.blockIndex; + + var step = incremental ? pipeline.step : null; + var modDataCount = pCtx && pCtx.modDataCount; + var modBy = modDataCount != null ? Math.ceil(modDataCount / step): null; + + return {step: step, modBy: modBy, modDataCount: modDataCount}; +}; + +proto.getPipeline = function (pipelineId) { + return this._pipelineMap.get(pipelineId); +}; + +/** + * Current, progressive rendering starts from visual and layout. + * Always detect render mode in the same stage, avoiding that incorrect + * detection caused by data filtering. + * Caution: + * `updateStreamModes` use `seriesModel.getData()`. + */ +proto.updateStreamModes = function (seriesModel, view) { + var pipeline = this._pipelineMap.get(seriesModel.uid); + var data = seriesModel.getData(); + var dataLen = data.count(); + + // `progressiveRender` means that can render progressively in each + // animation frame. Note that some types of series do not provide + // `view.incrementalPrepareRender` but support `chart.appendData`. We + // use the term `incremental` but not `progressive` to describe the + // case that `chart.appendData`. + var progressiveRender = pipeline.progressiveEnabled + && view.incrementalPrepareRender + && dataLen >= pipeline.threshold; + + var large = seriesModel.get('large') && dataLen >= seriesModel.get('largeThreshold'); + + // TODO: modDataCount should not updated if `appendData`, otherwise cause whole repaint. + // see `test/candlestick-large3.html` + var modDataCount = seriesModel.get('progressiveChunkMode') === 'mod' ? dataLen : null; + + seriesModel.pipelineContext = pipeline.context = { + progressiveRender: progressiveRender, + modDataCount: modDataCount, + large: large + }; +}; + +proto.restorePipelines = function (ecModel) { + var scheduler = this; + var pipelineMap = scheduler._pipelineMap = createHashMap(); + + ecModel.eachSeries(function (seriesModel) { + var progressive = seriesModel.getProgressive(); + var pipelineId = seriesModel.uid; + + pipelineMap.set(pipelineId, { + id: pipelineId, + head: null, + tail: null, + threshold: seriesModel.getProgressiveThreshold(), + progressiveEnabled: progressive + && !(seriesModel.preventIncremental && seriesModel.preventIncremental()), + blockIndex: -1, + step: Math.round(progressive || 700), + count: 0 + }); + + pipe(scheduler, seriesModel, seriesModel.dataTask); + }); +}; + +proto.prepareStageTasks = function () { + var stageTaskMap = this._stageTaskMap; + var ecModel = this.ecInstance.getModel(); + var api = this.api; + + each$1(this._allHandlers, function (handler) { + var record = stageTaskMap.get(handler.uid) || stageTaskMap.set(handler.uid, []); + + handler.reset && createSeriesStageTask(this, handler, record, ecModel, api); + handler.overallReset && createOverallStageTask(this, handler, record, ecModel, api); + }, this); +}; + +proto.prepareView = function (view, model, ecModel, api) { + var renderTask = view.renderTask; + var context = renderTask.context; + + context.model = model; + context.ecModel = ecModel; + context.api = api; + + renderTask.__block = !view.incrementalPrepareRender; + + pipe(this, model, renderTask); +}; + + +proto.performDataProcessorTasks = function (ecModel, payload) { + // If we do not use `block` here, it should be considered when to update modes. + performStageTasks(this, this._dataProcessorHandlers, ecModel, payload, {block: true}); +}; + +// opt +// opt.visualType: 'visual' or 'layout' +// opt.setDirty +proto.performVisualTasks = function (ecModel, payload, opt) { + performStageTasks(this, this._visualHandlers, ecModel, payload, opt); +}; + +function performStageTasks(scheduler, stageHandlers, ecModel, payload, opt) { + opt = opt || {}; + var unfinished; + + each$1(stageHandlers, function (stageHandler, idx) { + if (opt.visualType && opt.visualType !== stageHandler.visualType) { + return; + } + + var stageHandlerRecord = scheduler._stageTaskMap.get(stageHandler.uid); + var seriesTaskMap = stageHandlerRecord.seriesTaskMap; + var overallTask = stageHandlerRecord.overallTask; + + if (overallTask) { + var overallNeedDirty; + var agentStubMap = overallTask.agentStubMap; + agentStubMap.each(function (stub) { + if (needSetDirty(opt, stub)) { + stub.dirty(); + overallNeedDirty = true; + } + }); + overallNeedDirty && overallTask.dirty(); + updatePayload(overallTask, payload); + var performArgs = scheduler.getPerformArgs(overallTask, opt.block); + // Execute stubs firstly, which may set the overall task dirty, + // then execute the overall task. And stub will call seriesModel.setData, + // which ensures that in the overallTask seriesModel.getData() will not + // return incorrect data. + agentStubMap.each(function (stub) { + stub.perform(performArgs); + }); + unfinished |= overallTask.perform(performArgs); + } + else if (seriesTaskMap) { + seriesTaskMap.each(function (task, pipelineId) { + if (needSetDirty(opt, task)) { + task.dirty(); + } + var performArgs = scheduler.getPerformArgs(task, opt.block); + performArgs.skip = !stageHandler.performRawSeries + && ecModel.isSeriesFiltered(task.context.model); + updatePayload(task, payload); + unfinished |= task.perform(performArgs); + }); + } + }); + + function needSetDirty(opt, task) { + return opt.setDirty && (!opt.dirtyMap || opt.dirtyMap.get(task.__pipeline.id)); + } + + scheduler.unfinished |= unfinished; +} + +proto.performSeriesTasks = function (ecModel) { + var unfinished; + + ecModel.eachSeries(function (seriesModel) { + // Progress to the end for dataInit and dataRestore. + unfinished |= seriesModel.dataTask.perform(); + }); + + this.unfinished |= unfinished; +}; + +proto.plan = function () { + // Travel pipelines, check block. + this._pipelineMap.each(function (pipeline) { + var task = pipeline.tail; + do { + if (task.__block) { + pipeline.blockIndex = task.__idxInPipeline; + break; + } + task = task.getUpstream(); + } + while (task); + }); +}; + +var updatePayload = proto.updatePayload = function (task, payload) { + payload !== 'remain' && (task.context.payload = payload); +}; + +function createSeriesStageTask(scheduler, stageHandler, stageHandlerRecord, ecModel, api) { + var seriesTaskMap = stageHandlerRecord.seriesTaskMap + || (stageHandlerRecord.seriesTaskMap = createHashMap()); + var seriesType = stageHandler.seriesType; + var getTargetSeries = stageHandler.getTargetSeries; + + // If a stageHandler should cover all series, `createOnAllSeries` should be declared mandatorily, + // to avoid some typo or abuse. Otherwise if an extension do not specify a `seriesType`, + // it works but it may cause other irrelevant charts blocked. + if (stageHandler.createOnAllSeries) { + ecModel.eachRawSeries(create); + } + else if (seriesType) { + ecModel.eachRawSeriesByType(seriesType, create); + } + else if (getTargetSeries) { + getTargetSeries(ecModel, api).each(create); + } + + function create(seriesModel) { + var pipelineId = seriesModel.uid; + + // Init tasks for each seriesModel only once. + // Reuse original task instance. + var task = seriesTaskMap.get(pipelineId) + || seriesTaskMap.set(pipelineId, createTask({ + plan: seriesTaskPlan, + reset: seriesTaskReset, + count: seriesTaskCount + })); + task.context = { + model: seriesModel, + ecModel: ecModel, + api: api, + useClearVisual: stageHandler.isVisual && !stageHandler.isLayout, + plan: stageHandler.plan, + reset: stageHandler.reset, + scheduler: scheduler + }; + pipe(scheduler, seriesModel, task); + } + + // Clear unused series tasks. + var pipelineMap = scheduler._pipelineMap; + seriesTaskMap.each(function (task, pipelineId) { + if (!pipelineMap.get(pipelineId)) { + task.dispose(); + seriesTaskMap.removeKey(pipelineId); + } + }); +} + +function createOverallStageTask(scheduler, stageHandler, stageHandlerRecord, ecModel, api) { + var overallTask = stageHandlerRecord.overallTask = stageHandlerRecord.overallTask + // For overall task, the function only be called on reset stage. + || createTask({reset: overallTaskReset}); + + overallTask.context = { + ecModel: ecModel, + api: api, + overallReset: stageHandler.overallReset, + scheduler: scheduler + }; + + // Reuse orignal stubs. + var agentStubMap = overallTask.agentStubMap = overallTask.agentStubMap || createHashMap(); + + var seriesType = stageHandler.seriesType; + var getTargetSeries = stageHandler.getTargetSeries; + var overallProgress = true; + var modifyOutputEnd = stageHandler.modifyOutputEnd; + + // An overall task with seriesType detected or has `getTargetSeries`, we add + // stub in each pipelines, it will set the overall task dirty when the pipeline + // progress. Moreover, to avoid call the overall task each frame (too frequent), + // we set the pipeline block. + if (seriesType) { + ecModel.eachRawSeriesByType(seriesType, createStub); + } + else if (getTargetSeries) { + getTargetSeries(ecModel, api).each(createStub); + } + // Otherwise, (usually it is legancy case), the overall task will only be + // executed when upstream dirty. Otherwise the progressive rendering of all + // pipelines will be disabled unexpectedly. But it still needs stubs to receive + // dirty info from upsteam. + else { + overallProgress = false; + each$1(ecModel.getSeries(), createStub); + } + + function createStub(seriesModel) { + var pipelineId = seriesModel.uid; + var stub = agentStubMap.get(pipelineId); + if (!stub) { + stub = agentStubMap.set(pipelineId, createTask( + {reset: stubReset, onDirty: stubOnDirty} + )); + // When the result of `getTargetSeries` changed, the overallTask + // should be set as dirty and re-performed. + overallTask.dirty(); + } + stub.context = { + model: seriesModel, + overallProgress: overallProgress, + modifyOutputEnd: modifyOutputEnd + }; + stub.agent = overallTask; + stub.__block = overallProgress; + + pipe(scheduler, seriesModel, stub); + } + + // Clear unused stubs. + var pipelineMap = scheduler._pipelineMap; + agentStubMap.each(function (stub, pipelineId) { + if (!pipelineMap.get(pipelineId)) { + stub.dispose(); + // When the result of `getTargetSeries` changed, the overallTask + // should be set as dirty and re-performed. + overallTask.dirty(); + agentStubMap.removeKey(pipelineId); + } + }); +} + +function overallTaskReset(context) { + context.overallReset( + context.ecModel, context.api, context.payload + ); +} + +function stubReset(context, upstreamContext) { + return context.overallProgress && stubProgress; +} + +function stubProgress() { + this.agent.dirty(); + this.getDownstream().dirty(); +} + +function stubOnDirty() { + this.agent && this.agent.dirty(); +} + +function seriesTaskPlan(context) { + return context.plan && context.plan( + context.model, context.ecModel, context.api, context.payload + ); +} + +function seriesTaskReset(context) { + if (context.useClearVisual) { + context.data.clearAllVisual(); + } + var resetDefines = context.resetDefines = normalizeToArray(context.reset( + context.model, context.ecModel, context.api, context.payload + )); + return resetDefines.length > 1 + ? map(resetDefines, function (v, idx) { + return makeSeriesTaskProgress(idx); + }) + : singleSeriesTaskProgress; +} + +var singleSeriesTaskProgress = makeSeriesTaskProgress(0); + +function makeSeriesTaskProgress(resetDefineIdx) { + return function (params, context) { + var data = context.data; + var resetDefine = context.resetDefines[resetDefineIdx]; + + if (resetDefine && resetDefine.dataEach) { + for (var i = params.start; i < params.end; i++) { + resetDefine.dataEach(data, i); + } + } + else if (resetDefine && resetDefine.progress) { + resetDefine.progress(params, data); + } + }; +} + +function seriesTaskCount(context) { + return context.data.count(); +} + +function pipe(scheduler, seriesModel, task) { + var pipelineId = seriesModel.uid; + var pipeline = scheduler._pipelineMap.get(pipelineId); + !pipeline.head && (pipeline.head = task); + pipeline.tail && pipeline.tail.pipe(task); + pipeline.tail = task; + task.__idxInPipeline = pipeline.count++; + task.__pipeline = pipeline; +} + +Scheduler.wrapStageHandler = function (stageHandler, visualType) { + if (isFunction$1(stageHandler)) { + stageHandler = { + overallReset: stageHandler, + seriesType: detectSeriseType(stageHandler) + }; + } + + stageHandler.uid = getUID('stageHandler'); + visualType && (stageHandler.visualType = visualType); + + return stageHandler; +}; + + + +/** + * Only some legacy stage handlers (usually in echarts extensions) are pure function. + * To ensure that they can work normally, they should work in block mode, that is, + * they should not be started util the previous tasks finished. So they cause the + * progressive rendering disabled. We try to detect the series type, to narrow down + * the block range to only the series type they concern, but not all series. + */ +function detectSeriseType(legacyFunc) { + seriesType = null; + try { + // Assume there is no async when calling `eachSeriesByType`. + legacyFunc(ecModelMock, apiMock); + } + catch (e) { + } + return seriesType; +} + +var ecModelMock = {}; +var apiMock = {}; +var seriesType; + +mockMethods(ecModelMock, GlobalModel); +mockMethods(apiMock, ExtensionAPI); +ecModelMock.eachSeriesByType = ecModelMock.eachRawSeriesByType = function (type) { + seriesType = type; +}; +ecModelMock.eachComponent = function (cond) { + if (cond.mainType === 'series' && cond.subType) { + seriesType = cond.subType; + } +}; + +function mockMethods(target, Clz) { + for (var name in Clz.prototype) { + // Do not use hasOwnProperty + target[name] = noop; + } +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var colorAll = ['#37A2DA', '#32C5E9', '#67E0E3', '#9FE6B8', '#FFDB5C','#ff9f7f', '#fb7293', '#E062AE', '#E690D1', '#e7bcf3', '#9d96f5', '#8378EA', '#96BFFF']; + +var lightTheme = { + + color: colorAll, + + colorLayer: [ + ['#37A2DA', '#ffd85c', '#fd7b5f'], + ['#37A2DA', '#67E0E3', '#FFDB5C', '#ff9f7f', '#E062AE', '#9d96f5'], + ['#37A2DA', '#32C5E9', '#9FE6B8', '#FFDB5C', '#ff9f7f', '#fb7293', '#e7bcf3', '#8378EA', '#96BFFF'], + colorAll + ] +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var contrastColor = '#eee'; +var axisCommon = function () { + return { + axisLine: { + lineStyle: { + color: contrastColor + } + }, + axisTick: { + lineStyle: { + color: contrastColor + } + }, + axisLabel: { + textStyle: { + color: contrastColor + } + }, + splitLine: { + lineStyle: { + type: 'dashed', + color: '#aaa' + } + }, + splitArea: { + areaStyle: { + color: contrastColor + } + } + }; +}; + +var colorPalette = ['#dd6b66','#759aa0','#e69d87','#8dc1a9','#ea7e53','#eedd78','#73a373','#73b9bc','#7289ab', '#91ca8c','#f49f42']; +var theme = { + color: colorPalette, + backgroundColor: '#333', + tooltip: { + axisPointer: { + lineStyle: { + color: contrastColor + }, + crossStyle: { + color: contrastColor + } + } + }, + legend: { + textStyle: { + color: contrastColor + } + }, + textStyle: { + color: contrastColor + }, + title: { + textStyle: { + color: contrastColor + } + }, + toolbox: { + iconStyle: { + normal: { + borderColor: contrastColor + } + } + }, + dataZoom: { + textStyle: { + color: contrastColor + } + }, + visualMap: { + textStyle: { + color: contrastColor + } + }, + timeline: { + lineStyle: { + color: contrastColor + }, + itemStyle: { + normal: { + color: colorPalette[1] + } + }, + label: { + normal: { + textStyle: { + color: contrastColor + } + } + }, + controlStyle: { + normal: { + color: contrastColor, + borderColor: contrastColor + } + } + }, + timeAxis: axisCommon(), + logAxis: axisCommon(), + valueAxis: axisCommon(), + categoryAxis: axisCommon(), + + line: { + symbol: 'circle' + }, + graph: { + color: colorPalette + }, + gauge: { + title: { + textStyle: { + color: contrastColor + } + } + }, + candlestick: { + itemStyle: { + normal: { + color: '#FD1050', + color0: '#0CF49B', + borderColor: '#FD1050', + borderColor0: '#0CF49B' + } + } + } +}; +theme.categoryAxis.splitLine.show = false; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * This module is imported by echarts directly. + * + * Notice: + * Always keep this file exists for backward compatibility. + * Because before 4.1.0, dataset is an optional component, + * some users may import this module manually. + */ + +ComponentModel.extend({ + + type: 'dataset', + + /** + * @protected + */ + defaultOption: { + + // 'row', 'column' + seriesLayoutBy: SERIES_LAYOUT_BY_COLUMN, + + // null/'auto': auto detect header, see "module:echarts/data/helper/sourceHelper" + sourceHeader: null, + + dimensions: null, + + source: null + }, + + optionUpdated: function () { + detectSourceFormat(this); + } + +}); + +Component.extend({ + + type: 'dataset' + +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ +var assert = assert$1; +var each = each$1; +var isFunction = isFunction$1; +var isObject = isObject$1; +var parseClassType = ComponentModel.parseClassType; + +var version = '4.1.0'; + +var dependencies = { + zrender: '4.0.4' +}; + +var TEST_FRAME_REMAIN_TIME = 1; + +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; +// FIXME +// necessary? +var PRIORITY_VISUAL_BRUSH = 5000; + +var 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 + } +}; + +// 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 = '__flagInMainProcess'; +var OPTION_UPDATED = '__optionUpdated'; +var ACTION_REG = /^[a-zA-Z0-9_]+$/; + + +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'); +mixin(MessageCenter, Eventful); + +/** + * @module echarts~ECharts + */ +function ECharts(dom, theme$$1, opts) { + opts = opts || {}; + + // Get theme by name + if (typeof theme$$1 === 'string') { + theme$$1 = themeStorage[theme$$1]; + } + + /** + * @type {string} + */ + this.id; + + /** + * Group id + * @type {string} + */ + this.group; + + /** + * @type {HTMLElement} + * @private + */ + this._dom = dom; + + var defaultRenderer = 'canvas'; + if (__DEV__) { + defaultRenderer = ( + typeof window === 'undefined' ? global : window + ).__ECHARTS__DEFAULT__RENDERER__ || defaultRenderer; + } + + /** + * @type {module:zrender/ZRender} + * @private + */ + var zr = this._zr = init$1(dom, { + renderer: opts.renderer || defaultRenderer, + devicePixelRatio: opts.devicePixelRatio, + width: opts.width, + height: opts.height + }); + + /** + * Expect 60 pfs. + * @type {Function} + * @private + */ + this._throttledZrFlush = throttle(bind(zr.flush, zr), 17); + + var theme$$1 = clone(theme$$1); + theme$$1 && backwardCompat(theme$$1, true); + /** + * @type {Object} + * @private + */ + this._theme = theme$$1; + + /** + * @type {Array.} + * @private + */ + this._chartsViews = []; + + /** + * @type {Object.} + * @private + */ + this._chartsMap = {}; + + /** + * @type {Array.} + * @private + */ + this._componentsViews = []; + + /** + * @type {Object.} + * @private + */ + this._componentsMap = {}; + + /** + * @type {module:echarts/CoordinateSystem} + * @private + */ + this._coordSysMgr = new CoordinateSystemManager(); + + /** + * @type {module:echarts/ExtensionAPI} + * @private + */ + var api = this._api = createExtensionAPI(this); + + // Sort on demand + function prioritySortFunc(a, b) { + return a.__prio - b.__prio; + } + sort(visualFuncs, prioritySortFunc); + sort(dataProcessorFuncs, prioritySortFunc); + + /** + * @type {module:echarts/stream/Scheduler} + */ + this._scheduler = new Scheduler(this, api, dataProcessorFuncs, visualFuncs); + + 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 = bind(this.resize, this); + + // Can't dispatch action during rendering procedure + this._pendingActions = []; + + zr.animation.on('frame', this._onframe, this); + + bindRenderedEvent(zr, this); + + // ECharts instance can be used as value. + setAsPrimitive(this); +} + +var echartsProto = ECharts.prototype; + +echartsProto._onframe = function () { + if (this._disposed) { + return; + } + + var scheduler = this._scheduler; + + // Lazy update + if (this[OPTION_UPDATED]) { + var silent = this[OPTION_UPDATED].silent; + + this[IN_MAIN_PROCESS] = true; + + prepare(this); + updateMethods.update.call(this); + + this[IN_MAIN_PROCESS] = false; + + this[OPTION_UPDATED] = false; + + flushPendingActions.call(this, silent); + + triggerUpdatedEvent.call(this, silent); + } + // Avoid do both lazy update and progress in one frame. + else if (scheduler.unfinished) { + // Stream progress. + var remainTime = TEST_FRAME_REMAIN_TIME; + var ecModel = this._model; + var api = this._api; + scheduler.unfinished = false; + do { + var startTime = +new Date(); + + scheduler.performSeriesTasks(ecModel); + + // Currently dataProcessorFuncs do not check threshold. + scheduler.performDataProcessorTasks(ecModel); + + updateStreamModes(this, ecModel); + + // Do not update coordinate system here. Because that coord system update in + // each frame is not a good user experience. So we follow the rule that + // the extent of the coordinate system is determin in the first frame (the + // frame is executed immedietely after task reset. + // this._coordSysMgr.update(ecModel, api); + + // console.log('--- ec frame visual ---', remainTime); + scheduler.performVisualTasks(ecModel); + + renderSeries(this, this._model, api, 'remain'); + + remainTime -= (+new Date() - startTime); + } + while (remainTime > 0 && scheduler.unfinished); + + // Call flush explicitly for trigger finished event. + if (!scheduler.unfinished) { + this._zr.flush(); + } + // Else, zr flushing be ensue within the same frame, + // because zr flushing is after onframe event. + } +}; + +/** + * @return {HTMLElement} + */ +echartsProto.getDom = function () { + return this._dom; +}; + +/** + * @return {module:zrender~ZRender} + */ +echartsProto.getZr = function () { + return this._zr; +}; + +/** + * Usage: + * chart.setOption(option, notMerge, lazyUpdate); + * chart.setOption(option, { + * notMerge: ..., + * lazyUpdate: ..., + * silent: ... + * }); + * + * @param {Object} option + * @param {Object|boolean} [opts] opts or notMerge. + * @param {boolean} [opts.notMerge=false] + * @param {boolean} [opts.lazyUpdate=false] Useful when setOption frequently. + */ +echartsProto.setOption = function (option, notMerge, lazyUpdate) { + if (__DEV__) { + assert(!this[IN_MAIN_PROCESS], '`setOption` should not be called during main process.'); + } + + var silent; + if (isObject(notMerge)) { + lazyUpdate = notMerge.lazyUpdate; + silent = notMerge.silent; + notMerge = notMerge.notMerge; + } + + this[IN_MAIN_PROCESS] = true; + + if (!this._model || notMerge) { + var optionManager = new OptionManager(this._api); + var theme$$1 = this._theme; + var ecModel = this._model = new GlobalModel(null, null, theme$$1, optionManager); + ecModel.scheduler = this._scheduler; + ecModel.init(null, null, theme$$1, optionManager); + } + + this._model.setOption(option, optionPreprocessorFuncs); + + if (lazyUpdate) { + this[OPTION_UPDATED] = {silent: silent}; + this[IN_MAIN_PROCESS] = false; + } + else { + prepare(this); + + updateMethods.update.call(this); + + // Ensure zr refresh sychronously, and then pixel in canvas can be + // fetched after `setOption`. + this._zr.flush(); + + this[OPTION_UPDATED] = false; + this[IN_MAIN_PROCESS] = false; + + flushPendingActions.call(this, silent); + triggerUpdatedEvent.call(this, silent); + } +}; + +/** + * @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(); +}; + +/** + * @return {number} + */ +echartsProto.getDevicePixelRatio = function () { + return this._zr.painter.dpr || window.devicePixelRatio || 1; +}; + +/** + * Get canvas which has all thing rendered + * @param {Object} opts + * @param {string} [opts.backgroundColor] + * @return {string} + */ +echartsProto.getRenderedCanvas = function (opts) { + if (!env$1.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 + // Never works before in init animation, so remove it. + // zrUtil.each(list, function (el) { + // el.stopAnimation(true); + // }); + return zr.painter.getRenderedCanvas(opts); +}; + +/** + * Get svg data url + * @return {string} + */ +echartsProto.getSvgDataUrl = function () { + if (!env$1.svgSupported) { + return; + } + + var zr = this._zr; + var list = zr.storage.getDisplayList(); + // Stop animations + each$1(list, function (el) { + el.stopAnimation(true); + }); + + return zr.painter.pathToDataUrl(); +}; + +/** + * @return {string} + * @param {Object} opts + * @param {string} [opts.type='png'] + * @param {string} [opts.pixelRatio=1] + * @param {string} [opts.backgroundColor] + * @param {string} [opts.excludeComponents] + */ +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._zr.painter.getType() === 'svg' + ? this.getSvgDataUrl() + : 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$1.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; + + each$1(instances, function (chart, id) { + if (chart.group === groupId) { + var canvas = chart.getRenderedCanvas( + 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 = createCanvas(); + targetCanvas.width = width; + targetCanvas.height = height; + var zr = init$1(targetCanvas); + + each(canvasList, function (item) { + var img = new ZImage({ + 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); + } +}; + +/** + * Convert from logical coordinate system to pixel coordinate system. + * See CoordinateSystem#convertToPixel. + * @param {string|Object} finder + * If string, e.g., 'geo', means {geoIndex: 0}. + * If Object, could contain some of these properties below: + * { + * seriesIndex / seriesId / seriesName, + * geoIndex / geoId, geoName, + * bmapIndex / bmapId / bmapName, + * xAxisIndex / xAxisId / xAxisName, + * yAxisIndex / yAxisId / yAxisName, + * gridIndex / gridId / gridName, + * ... (can be extended) + * } + * @param {Array|number} value + * @return {Array|number} result + */ +echartsProto.convertToPixel = curry(doConvertPixel, 'convertToPixel'); + +/** + * Convert from pixel coordinate system to logical coordinate system. + * See CoordinateSystem#convertFromPixel. + * @param {string|Object} finder + * If string, e.g., 'geo', means {geoIndex: 0}. + * If Object, could contain some of these properties below: + * { + * seriesIndex / seriesId / seriesName, + * geoIndex / geoId / geoName, + * bmapIndex / bmapId / bmapName, + * xAxisIndex / xAxisId / xAxisName, + * yAxisIndex / yAxisId / yAxisName + * gridIndex / gridId / gridName, + * ... (can be extended) + * } + * @param {Array|number} value + * @return {Array|number} result + */ +echartsProto.convertFromPixel = curry(doConvertPixel, 'convertFromPixel'); + +function doConvertPixel(methodName, finder, value) { + var ecModel = this._model; + var coordSysList = this._coordSysMgr.getCoordinateSystems(); + var result; + + finder = parseFinder(ecModel, finder); + + for (var i = 0; i < coordSysList.length; i++) { + var coordSys = coordSysList[i]; + if (coordSys[methodName] + && (result = coordSys[methodName](ecModel, finder, value)) != null + ) { + return result; + } + } + + if (__DEV__) { + console.warn( + 'No coordinate system that supports ' + methodName + ' found by the given finder.' + ); + } +} + +/** + * Is the specified coordinate systems or components contain the given pixel point. + * @param {string|Object} finder + * If string, e.g., 'geo', means {geoIndex: 0}. + * If Object, could contain some of these properties below: + * { + * seriesIndex / seriesId / seriesName, + * geoIndex / geoId / geoName, + * bmapIndex / bmapId / bmapName, + * xAxisIndex / xAxisId / xAxisName, + * yAxisIndex / yAxisId / yAxisName, + * gridIndex / gridId / gridName, + * ... (can be extended) + * } + * @param {Array|number} value + * @return {boolean} result + */ +echartsProto.containPixel = function (finder, value) { + var ecModel = this._model; + var result; + + finder = parseFinder(ecModel, finder); + + each$1(finder, function (models, key) { + key.indexOf('Models') >= 0 && each$1(models, function (model) { + var coordSys = model.coordinateSystem; + if (coordSys && coordSys.containPoint) { + result |= !!coordSys.containPoint(value); + } + else if (key === 'seriesModels') { + var view = this._chartsMap[model.__viewId]; + if (view && view.containPoint) { + result |= view.containPoint(value, model); + } + else { + if (__DEV__) { + console.warn(key + ': ' + (view + ? 'The found component do not support containPoint.' + : 'No view mapping to the found component.' + )); + } + } + } + else { + if (__DEV__) { + console.warn(key + ': containPoint is not supported'); + } + } + }, this); + }, this); + + return !!result; +}; + +/** + * Get visual from series or data. + * @param {string|Object} finder + * If string, e.g., 'series', means {seriesIndex: 0}. + * If Object, could contain some of these properties below: + * { + * seriesIndex / seriesId / seriesName, + * dataIndex / dataIndexInside + * } + * If dataIndex is not specified, series visual will be fetched, + * but not data item visual. + * If all of seriesIndex, seriesId, seriesName are not specified, + * visual will be fetched from first series. + * @param {string} visualType 'color', 'symbol', 'symbolSize' + */ +echartsProto.getVisual = function (finder, visualType) { + var ecModel = this._model; + + finder = parseFinder(ecModel, finder, {defaultMainType: 'series'}); + + var seriesModel = finder.seriesModel; + + if (__DEV__) { + if (!seriesModel) { + console.warn('There is no specified seires model'); + } + } + + var data = seriesModel.getData(); + + var dataIndexInside = finder.hasOwnProperty('dataIndexInside') + ? finder.dataIndexInside + : finder.hasOwnProperty('dataIndex') + ? data.indexOfRawIndex(finder.dataIndex) + : null; + + return dataIndexInside != null + ? data.getItemVisual(dataIndexInside, visualType) + : data.getVisual(visualType); +}; + +/** + * Get view of corresponding component model + * @param {module:echarts/model/Component} componentModel + * @return {module:echarts/view/Component} + */ +echartsProto.getViewOfComponentModel = function (componentModel) { + return this._componentsMap[componentModel.__viewId]; +}; + +/** + * Get view of corresponding series model + * @param {module:echarts/model/Series} seriesModel + * @return {module:echarts/view/Chart} + */ +echartsProto.getViewOfSeriesModel = function (seriesModel) { + return this._chartsMap[seriesModel.__viewId]; +}; + +var updateMethods = { + + prepareAndUpdate: function (payload) { + prepare(this); + updateMethods.update.call(this, payload); + }, + + /** + * @param {Object} payload + * @private + */ + update: function (payload) { + // console.profile && console.profile('update'); + + var ecModel = this._model; + var api = this._api; + var zr = this._zr; + var coordSysMgr = this._coordSysMgr; + var scheduler = this._scheduler; + + // update before setOption + if (!ecModel) { + return; + } + + scheduler.restoreData(ecModel, payload); + + scheduler.performSeriesTasks(ecModel); + + // 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(ecModel, api); + + scheduler.performDataProcessorTasks(ecModel, payload); + + // Current stream render is not supported in data process. So we can update + // stream modes after data processing, where the filtered data is used to + // deteming whether use progressive rendering. + updateStreamModes(this, ecModel); + + // We update stream modes before coordinate system updated, then the modes info + // can be fetched when coord sys updating (consider the barGrid extent fix). But + // the drawback is the full coord info can not be fetched. Fortunately this full + // coord is not requied in stream mode updater currently. + coordSysMgr.update(ecModel, api); + + clearColorPalette(ecModel); + scheduler.performVisualTasks(ecModel, payload); + + render(this, ecModel, api, payload); + + // Set background + var backgroundColor = ecModel.get('backgroundColor') || 'transparent'; + + // In IE8 + if (!env$1.canvasSupported) { + var colorArr = parse(backgroundColor); + backgroundColor = stringify(colorArr, 'rgb'); + if (colorArr[3] === 0) { + backgroundColor = 'transparent'; + } + } + else { + zr.setBackgroundColor(backgroundColor); + } + + performPostUpdateFuncs(ecModel, api); + + // console.profile && console.profileEnd('update'); + }, + + /** + * @param {Object} payload + * @private + */ + updateTransform: function (payload) { + var ecModel = this._model; + var ecIns = this; + var api = this._api; + + // update before setOption + if (!ecModel) { + return; + } + + // ChartView.markUpdateMethod(payload, 'updateTransform'); + + var componentDirtyList = []; + ecModel.eachComponent(function (componentType, componentModel) { + var componentView = ecIns.getViewOfComponentModel(componentModel); + if (componentView && componentView.__alive) { + if (componentView.updateTransform) { + var result = componentView.updateTransform(componentModel, ecModel, api, payload); + result && result.update && componentDirtyList.push(componentView); + } + else { + componentDirtyList.push(componentView); + } + } + }); + + var seriesDirtyMap = createHashMap(); + ecModel.eachSeries(function (seriesModel) { + var chartView = ecIns._chartsMap[seriesModel.__viewId]; + if (chartView.updateTransform) { + var result = chartView.updateTransform(seriesModel, ecModel, api, payload); + result && result.update && seriesDirtyMap.set(seriesModel.uid, 1); + } + else { + seriesDirtyMap.set(seriesModel.uid, 1); + } + }); + + clearColorPalette(ecModel); + // Keep pipe to the exist pipeline because it depends on the render task of the full pipeline. + // this._scheduler.performVisualTasks(ecModel, payload, 'layout', true); + this._scheduler.performVisualTasks( + ecModel, payload, {setDirty: true, dirtyMap: seriesDirtyMap} + ); + + // Currently, not call render of components. Geo render cost a lot. + // renderComponents(ecIns, ecModel, api, payload, componentDirtyList); + renderSeries(ecIns, ecModel, api, payload, seriesDirtyMap); + + performPostUpdateFuncs(ecModel, this._api); + }, + + /** + * @param {Object} payload + * @private + */ + updateView: function (payload) { + var ecModel = this._model; + + // update before setOption + if (!ecModel) { + return; + } + + Chart.markUpdateMethod(payload, 'updateView'); + + clearColorPalette(ecModel); + + // Keep pipe to the exist pipeline because it depends on the render task of the full pipeline. + this._scheduler.performVisualTasks(ecModel, payload, {setDirty: true}); + + render(this, this._model, this._api, payload); + + performPostUpdateFuncs(ecModel, this._api); + }, + + /** + * @param {Object} payload + * @private + */ + updateVisual: function (payload) { + updateMethods.update.call(this, payload); + + // var ecModel = this._model; + + // // update before setOption + // if (!ecModel) { + // return; + // } + + // ChartView.markUpdateMethod(payload, 'updateVisual'); + + // clearColorPalette(ecModel); + + // // Keep pipe to the exist pipeline because it depends on the render task of the full pipeline. + // this._scheduler.performVisualTasks(ecModel, payload, {visualType: 'visual', setDirty: true}); + + // render(this, this._model, this._api, payload); + + // performPostUpdateFuncs(ecModel, this._api); + }, + + /** + * @param {Object} payload + * @private + */ + updateLayout: function (payload) { + updateMethods.update.call(this, payload); + + // var ecModel = this._model; + + // // update before setOption + // if (!ecModel) { + // return; + // } + + // ChartView.markUpdateMethod(payload, 'updateLayout'); + + // // Keep pipe to the exist pipeline because it depends on the render task of the full pipeline. + // // this._scheduler.performVisualTasks(ecModel, payload, 'layout', true); + // this._scheduler.performVisualTasks(ecModel, payload, {setDirty: true}); + + // render(this, this._model, this._api, payload); + + // performPostUpdateFuncs(ecModel, this._api); + } +}; + +function prepare(ecIns) { + var ecModel = ecIns._model; + var scheduler = ecIns._scheduler; + + scheduler.restorePipelines(ecModel); + + scheduler.prepareStageTasks(); + + prepareView(ecIns, 'component', ecModel, scheduler); + + prepareView(ecIns, 'chart', ecModel, scheduler); + + scheduler.plan(); +} + +/** + * @private + */ +function updateDirectly(ecIns, method, payload, mainType, subType) { + var ecModel = ecIns._model; + + // broadcast + if (!mainType) { + // FIXME + // Chart will not be update directly here, except set dirty. + // But there is no such scenario now. + each(ecIns._componentsViews.concat(ecIns._chartsViews), callView); + return; + } + + var query = {}; + query[mainType + 'Id'] = payload[mainType + 'Id']; + query[mainType + 'Index'] = payload[mainType + 'Index']; + query[mainType + 'Name'] = payload[mainType + 'Name']; + + var condition = {mainType: mainType, query: query}; + subType && (condition.subType = subType); // subType may be '' by parseClassType; + + var excludeSeriesId = payload.excludeSeriesId; + if (excludeSeriesId != null) { + excludeSeriesId = createHashMap(normalizeToArray(excludeSeriesId)); + } + + // If dispatchAction before setOption, do nothing. + ecModel && ecModel.eachComponent(condition, function (model) { + if (!excludeSeriesId || excludeSeriesId.get(model.id) == null) { + callView(ecIns[ + mainType === 'series' ? '_chartsMap' : '_componentsMap' + ][model.__viewId]); + } + }, ecIns); + + function callView(view) { + view && view.__alive && view[method] && view[method]( + view.__model, ecModel, ecIns._api, payload + ); + } +} + +/** + * Resize the chart + * @param {Object} opts + * @param {number} [opts.width] Can be 'auto' (the same as null/undefined) + * @param {number} [opts.height] Can be 'auto' (the same as null/undefined) + * @param {boolean} [opts.silent=false] + */ +echartsProto.resize = function (opts) { + if (__DEV__) { + assert(!this[IN_MAIN_PROCESS], '`resize` should not be called during main process.'); + } + + this._zr.resize(opts); + + var ecModel = this._model; + + // Resize loading effect + this._loadingFX && this._loadingFX.resize(); + + if (!ecModel) { + return; + } + + var optionChanged = ecModel.resetOption('media'); + + var silent = opts && opts.silent; + + this[IN_MAIN_PROCESS] = true; + + optionChanged && prepare(this); + updateMethods.update.call(this); + + this[IN_MAIN_PROCESS] = false; + + flushPendingActions.call(this, silent); + + triggerUpdatedEvent.call(this, silent); +}; + +function updateStreamModes(ecIns, ecModel) { + var chartsMap = ecIns._chartsMap; + var scheduler = ecIns._scheduler; + ecModel.eachSeries(function (seriesModel) { + scheduler.updateStreamModes(seriesModel, chartsMap[seriesModel.__viewId]); + }); +} + +/** + * Show loading effect + * @param {string} [name='default'] + * @param {Object} [cfg] + */ +echartsProto.showLoading = function (name, cfg) { + if (isObject(name)) { + cfg = name; + name = ''; + } + name = name || 'default'; + + this.hideLoading(); + if (!loadingEffects[name]) { + if (__DEV__) { + 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 = extend({}, eventObj); + payload.type = eventActionMap[eventObj.type]; + return payload; +}; + +/** + * @pubilc + * @param {Object} payload + * @param {string} [payload.type] Action type + * @param {Object|boolean} [opt] If pass boolean, means opt.silent + * @param {boolean} [opt.silent=false] Whether trigger events. + * @param {boolean} [opt.flush=undefined] + * true: Flush immediately, and then pixel in canvas can be fetched + * immediately. Caution: it might affect performance. + * false: Not not flush. + * undefined: Auto decide whether perform flush. + */ +echartsProto.dispatchAction = function (payload, opt) { + if (!isObject(opt)) { + opt = {silent: !!opt}; + } + + if (!actions[payload.type]) { + return; + } + + // Avoid dispatch action before setOption. Especially in `connect`. + if (!this._model) { + return; + } + + // May dispatchAction in rendering procedure + if (this[IN_MAIN_PROCESS]) { + this._pendingActions.push(payload); + return; + } + + doDispatchAction.call(this, payload, opt.silent); + + if (opt.flush) { + this._zr.flush(true); + } + else if (opt.flush !== false && env$1.browser.weChat) { + // In WeChat embeded browser, `requestAnimationFrame` and `setInterval` + // hang when sliding page (on touch event), which cause that zr does not + // refresh util user interaction finished, which is not expected. + // But `dispatchAction` may be called too frequently when pan on touch + // screen, which impacts performance if do not throttle them. + this._throttledZrFlush(); + } + + flushPendingActions.call(this, opt.silent); + + triggerUpdatedEvent.call(this, opt.silent); +}; + +function doDispatchAction(payload, silent) { + var payloadType = payload.type; + var escapeConnect = payload.escapeConnect; + var actionWrap = actions[payloadType]; + var actionInfo = actionWrap.actionInfo; + + var cptType = (actionInfo.update || 'update').split(':'); + var updateMethod = cptType.pop(); + cptType = cptType[0] != null && parseClassType(cptType[0]); + + this[IN_MAIN_PROCESS] = true; + + var payloads = [payload]; + var batched = false; + // Batch action + if (payload.batch) { + batched = true; + payloads = map(payload.batch, function (item) { + item = defaults(extend({}, item), payload); + item.batch = null; + return item; + }); + } + + var eventObjBatch = []; + var eventObj; + var isHighDown = payloadType === 'highlight' || payloadType === 'downplay'; + + each(payloads, function (batchItem) { + // Action can specify the event by return it. + eventObj = actionWrap.action(batchItem, this._model, this._api); + // Emit event outside + eventObj = eventObj || extend({}, batchItem); + // Convert type to eventType + eventObj.type = actionInfo.event || eventObj.type; + eventObjBatch.push(eventObj); + + // light update does not perform data process, layout and visual. + if (isHighDown) { + // method, payload, mainType, subType + updateDirectly(this, updateMethod, batchItem, 'series'); + } + else if (cptType) { + updateDirectly(this, updateMethod, batchItem, cptType.main, cptType.sub); + } + }, this); + + if (updateMethod !== 'none' && !isHighDown && !cptType) { + // Still dirty + if (this[OPTION_UPDATED]) { + // FIXME Pass payload ? + prepare(this); + updateMethods.update.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 || payloadType, + escapeConnect: escapeConnect, + batch: eventObjBatch + }; + } + else { + eventObj = eventObjBatch[0]; + } + + this[IN_MAIN_PROCESS] = false; + + !silent && this._messageCenter.trigger(eventObj.type, eventObj); +} + +function flushPendingActions(silent) { + var pendingActions = this._pendingActions; + while (pendingActions.length) { + var payload = pendingActions.shift(); + doDispatchAction.call(this, payload, silent); + } +} + +function triggerUpdatedEvent(silent) { + !silent && this.trigger('updated'); +} + +/** + * Event `rendered` is triggered when zr + * rendered. It is useful for realtime + * snapshot (reflect animation). + * + * Event `finished` is triggered when: + * (1) zrender rendering finished. + * (2) initial animation finished. + * (3) progressive rendering finished. + * (4) no pending action. + * (5) no delayed setOption needs to be processed. + */ +function bindRenderedEvent(zr, ecIns) { + zr.on('rendered', function () { + + ecIns.trigger('rendered'); + + // The `finished` event should not be triggered repeatly, + // so it should only be triggered when rendering indeed happend + // in zrender. (Consider the case that dipatchAction is keep + // triggering when mouse move). + if ( + // Although zr is dirty if initial animation is not finished + // and this checking is called on frame, we also check + // animation finished for robustness. + zr.animation.isFinished() + && !ecIns[OPTION_UPDATED] + && !ecIns._scheduler.unfinished + && !ecIns._pendingActions.length + ) { + ecIns.trigger('finished'); + } + }); +} + +/** + * @param {Object} params + * @param {number} params.seriesIndex + * @param {Array|TypedArray} params.data + */ +echartsProto.appendData = function (params) { + var seriesIndex = params.seriesIndex; + var ecModel = this.getModel(); + var seriesModel = ecModel.getSeriesByIndex(seriesIndex); + + if (__DEV__) { + assert(params.data && seriesModel); + } + + seriesModel.appendData(params); + + // Note: `appendData` does not support that update extent of coordinate + // system, util some scenario require that. In the expected usage of + // `appendData`, the initial extent of coordinate system should better + // be fixed by axis `min`/`max` setting or initial data, otherwise if + // the extent changed while `appendData`, the location of the painted + // graphic elements have to be changed, which make the usage of + // `appendData` meaningless. + + this._scheduler.unfinished = true; +}; + +/** + * Register event + * @method + */ +echartsProto.on = createRegisterEventWithLowercaseName('on'); +echartsProto.off = createRegisterEventWithLowercaseName('off'); +echartsProto.one = createRegisterEventWithLowercaseName('one'); + +/** + * Prepare view instances of charts and components + * @param {module:echarts/model/Global} ecModel + * @private + */ +function prepareView(ecIns, type, ecModel, scheduler) { + var isComponent = type === 'component'; + var viewList = isComponent ? ecIns._componentsViews : ecIns._chartsViews; + var viewMap = isComponent ? ecIns._componentsMap : ecIns._chartsMap; + var zr = ecIns._zr; + var api = ecIns._api; + + for (var i = 0; i < viewList.length; i++) { + viewList[i].__alive = false; + } + + isComponent + ? ecModel.eachComponent(function (componentType, model) { + componentType !== 'series' && doPrepare(model); + }) + : ecModel.eachSeries(doPrepare); + + function doPrepare(model) { + // Consider: id same and type changed. + var viewId = '_ec_' + model.id + '_' + model.type; + var view = viewMap[viewId]; + if (!view) { + var classType = parseClassType(model.type); + var Clazz = isComponent + ? Component.getClass(classType.main, classType.sub) + : Chart.getClass(classType.sub); + + if (__DEV__) { + assert(Clazz, classType.sub + ' does not exist.'); + } + + view = new Clazz(); + view.init(ecModel, api); + viewMap[viewId] = view; + viewList.push(view); + zr.add(view.group); + } + + model.__viewId = view.__id = viewId; + view.__alive = true; + view.__model = model; + view.group.__ecComponentInfo = { + mainType: model.mainType, + index: model.componentIndex + }; + !isComponent && scheduler.prepareView(view, model, ecModel, api); + } + + for (var i = 0; i < viewList.length;) { + var view = viewList[i]; + if (!view.__alive) { + !isComponent && view.renderTask.dispose(); + zr.remove(view.group); + view.dispose(ecModel, api); + viewList.splice(i, 1); + delete viewMap[view.__id]; + view.__id = view.group.__ecComponentInfo = null; + } + else { + i++; + } + } +} + +// /** +// * Encode visual infomation from data after data processing +// * +// * @param {module:echarts/model/Global} ecModel +// * @param {object} layout +// * @param {boolean} [layoutFilter] `true`: only layout, +// * `false`: only not layout, +// * `null`/`undefined`: all. +// * @param {string} taskBaseTag +// * @private +// */ +// function startVisualEncoding(ecIns, ecModel, api, payload, layoutFilter) { +// each(visualFuncs, function (visual, index) { +// var isLayout = visual.isLayout; +// if (layoutFilter == null +// || (layoutFilter === false && !isLayout) +// || (layoutFilter === true && isLayout) +// ) { +// visual.func(ecModel, api, payload); +// } +// }); +// } + +function clearColorPalette(ecModel) { + ecModel.clearColorPalette(); + ecModel.eachSeries(function (seriesModel) { + seriesModel.clearColorPalette(); + }); +} + +function render(ecIns, ecModel, api, payload) { + + renderComponents(ecIns, ecModel, api, payload); + + each(ecIns._chartsViews, function (chart) { + chart.__alive = false; + }); + + renderSeries(ecIns, ecModel, api, payload); + + // Remove groups of unrendered charts + each(ecIns._chartsViews, function (chart) { + if (!chart.__alive) { + chart.remove(ecModel, api); + } + }); +} + +function renderComponents(ecIns, ecModel, api, payload, dirtyList) { + each(dirtyList || ecIns._componentsViews, function (componentView) { + var componentModel = componentView.__model; + componentView.render(componentModel, ecModel, api, payload); + + updateZ(componentModel, componentView); + }); +} + +/** + * Render each chart and component + * @private + */ +function renderSeries(ecIns, ecModel, api, payload, dirtyMap) { + // Render all charts + var scheduler = ecIns._scheduler; + var unfinished; + ecModel.eachSeries(function (seriesModel) { + var chartView = ecIns._chartsMap[seriesModel.__viewId]; + chartView.__alive = true; + + var renderTask = chartView.renderTask; + scheduler.updatePayload(renderTask, payload); + + if (dirtyMap && dirtyMap.get(seriesModel.uid)) { + renderTask.dirty(); + } + + unfinished |= renderTask.perform(scheduler.getPerformArgs(renderTask)); + + chartView.group.silent = !!seriesModel.get('silent'); + + updateZ(seriesModel, chartView); + + updateBlend(seriesModel, chartView); + }); + scheduler.unfinished |= unfinished; + + // If use hover layer + updateHoverLayerStatus(ecIns._zr, ecModel); + + // Add aria + aria(ecIns._zr.dom, ecModel); +} + +function performPostUpdateFuncs(ecModel, api) { + each(postUpdateFuncs, function (func) { + func(ecModel, api); + }); +} + + +var MOUSE_EVENT_NAMES = [ + 'click', 'dblclick', 'mouseover', 'mouseout', 'mousemove', + 'mousedown', 'mouseup', 'globalout', 'contextmenu' +]; + +/** + * @private + */ +echartsProto._initEvents = function () { + each(MOUSE_EVENT_NAMES, function (eveName) { + this._zr.on(eveName, function (e) { + var ecModel = this.getModel(); + var el = e.target; + var params; + + // no e.target when 'globalout'. + if (eveName === 'globalout') { + params = {}; + } + else if (el && el.dataIndex != null) { + var dataModel = el.dataModel || ecModel.getSeriesByIndex(el.seriesIndex); + params = dataModel && dataModel.getDataParams(el.dataIndex, el.dataType) || {}; + } + // If element has custom eventData of components + else if (el && el.eventData) { + params = extend({}, el.eventData); + } + + if (params) { + params.event = e; + params.type = eveName; + this.trigger(eveName, params); + } + + }, 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 (__DEV__) { + console.warn('Instance ' + this.id + ' has been disposed'); + } + return; + } + this._disposed = true; + + setAttribute(this.getDom(), DOM_ATTRIBUTE_KEY, ''); + + 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]; +}; + +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$1.node) { + storage.traverse(function (el) { + if (!el.isGroup) { + // Don't switch back. + 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 updateBlend(seriesModel, chartView) { + var blendMode = seriesModel.get('blendMode') || null; + if (__DEV__) { + if (!env$1.canvasSupported && blendMode && blendMode !== 'source-over') { + console.warn('Only canvas support blendMode'); + } + } + chartView.group.traverse(function (el) { + // FIXME marker and other components + if (!el.isGroup) { + // Only set if blendMode is changed. In case element is incremental and don't wan't to rerender. + if (el.style.blend !== blendMode) { + el.setStyle('blend', blendMode); + } + } + if (el.eachPendingDisplayable) { + el.eachPendingDisplayable(function (displayable) { + displayable.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); + } + }); +} + +function createExtensionAPI(ecInstance) { + var coordSysMgr = ecInstance._coordSysMgr; + return extend(new ExtensionAPI(ecInstance), { + // Inject methods + getCoordinateSystems: bind( + coordSysMgr.getCoordinateSystems, coordSysMgr + ), + getComponentByElement: function (el) { + while (el) { + var modelInfo = el.__ecComponentInfo; + if (modelInfo != null) { + return ecInstance._model.getComponent(modelInfo.mainType, modelInfo.index); + } + el = el.parent; + } + } + }); +} + +/** + * @type {Object} key: actionType. + * @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 = []; + +/** + * @type {Array.} + * @inner + */ +var postUpdateFuncs = []; + +/** + * Visual encoding functions of each stage + * @type {Array.>} + */ +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_'; + +var mapDataStores = {}; + +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; + } + } + + each(eventActionMap, function (actionType, eventType) { + chart._messageCenter.on(eventType, function (event) { + if (connectedGroups[chart.group] && chart[STATUS_KEY] !== STATUS_PENDING) { + if (event && event.escapeConnect) { + return; + } + + var action = chart.makeActionFromEvent(event); + var otherCharts = []; + + each(instances, function (otherChart) { + 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 {HTMLElement} dom + * @param {Object} [theme] + * @param {Object} opts + * @param {number} [opts.devicePixelRatio] Use window.devicePixelRatio by default + * @param {string} [opts.renderer] Currently only 'canvas' is supported. + * @param {number} [opts.width] Use clientWidth of the input `dom` by default. + * Can be 'auto' (the same as null/undefined) + * @param {number} [opts.height] Use clientHeight of the input `dom` by default. + * Can be 'auto' (the same as null/undefined) + */ +function init(dom, theme$$1, opts) { + if (__DEV__) { + // Check version + if ((version$1.replace('.', '') - 0) < (dependencies.zrender.replace('.', '') - 0)) { + throw new Error( + 'zrender/src ' + version$1 + + ' is too old for ECharts ' + version + + '. Current version need ZRender ' + + dependencies.zrender + '+' + ); + } + + if (!dom) { + throw new Error('Initialize failed: invalid dom.'); + } + } + + var existInstance = getInstanceByDom(dom); + if (existInstance) { + if (__DEV__) { + console.warn('There is a chart instance already initialized on the dom.'); + } + return existInstance; + } + + if (__DEV__) { + if (isDom(dom) + && dom.nodeName.toUpperCase() !== 'CANVAS' + && ( + (!dom.clientWidth && (!opts || opts.width == null)) + || (!dom.clientHeight && (!opts || opts.height == null)) + ) + ) { + console.warn('Can\'t get dom width or height'); + } + } + + var chart = new ECharts(dom, theme$$1, opts); + chart.id = 'ec_' + idBase++; + instances[chart.id] = chart; + + setAttribute(dom, DOM_ATTRIBUTE_KEY, chart.id); + + enableConnect(chart); + + return chart; +} + +/** + * @return {string|Array.} groupId + */ +function connect(groupId) { + // Is array of charts + if (isArray(groupId)) { + var charts = groupId; + groupId = null; + // If any chart has group + each(charts, function (chart) { + if (chart.group != null) { + groupId = chart.group; + } + }); + groupId = groupId || ('g_' + groupIdBase++); + each(charts, function (chart) { + chart.group = groupId; + }); + } + connectedGroups[groupId] = true; + return groupId; +} + +/** + * @DEPRECATED + * @return {string} groupId + */ +function disConnect(groupId) { + connectedGroups[groupId] = false; +} + +/** + * @return {string} groupId + */ +var disconnect = disConnect; + +/** + * Dispose a chart instance + * @param {module:echarts~ECharts|HTMLDomElement|string} chart + */ +function dispose(chart) { + if (typeof chart === 'string') { + chart = instances[chart]; + } + else if (!(chart instanceof ECharts)){ + // Try to treat as dom + chart = getInstanceByDom(chart); + } + if ((chart instanceof ECharts) && !chart.isDisposed()) { + chart.dispose(); + } +} + +/** + * @param {HTMLElement} dom + * @return {echarts~ECharts} + */ +function getInstanceByDom(dom) { + return instances[getAttribute(dom, DOM_ATTRIBUTE_KEY)]; +} + +/** + * @param {string} key + * @return {echarts~ECharts} + */ +function getInstanceById(key) { + return instances[key]; +} + +/** + * Register theme + */ +function registerTheme(name, theme$$1) { + themeStorage[name] = theme$$1; +} + +/** + * Register option preprocessor + * @param {Function} preprocessorFunc + */ +function registerPreprocessor(preprocessorFunc) { + optionPreprocessorFuncs.push(preprocessorFunc); +} + +/** + * @param {number} [priority=1000] + * @param {Object|Function} processor + */ +function registerProcessor(priority, processor) { + normalizeRegister(dataProcessorFuncs, priority, processor, PRIORITY_PROCESSOR_FILTER); +} + +/** + * Register postUpdater + * @param {Function} postUpdateFunc + */ +function registerPostUpdate(postUpdateFunc) { + postUpdateFuncs.push(postUpdateFunc); +} + +/** + * 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 + */ +function registerAction(actionInfo, eventName, action) { + if (typeof eventName === 'function') { + action = eventName; + eventName = ''; + } + var actionType = isObject(actionInfo) + ? actionInfo.type + : ([actionInfo, actionInfo = { + event: eventName + }][0]); + + // Event name is all lowercase + actionInfo.event = (actionInfo.event || actionType).toLowerCase(); + eventName = actionInfo.event; + + // Validate action type and event name. + assert(ACTION_REG.test(actionType) && ACTION_REG.test(eventName)); + + if (!actions[actionType]) { + actions[actionType] = {action: action, actionInfo: actionInfo}; + } + eventActionMap[eventName] = actionType; +} + +/** + * @param {string} type + * @param {*} CoordinateSystem + */ +function registerCoordinateSystem(type, CoordinateSystem$$1) { + CoordinateSystemManager.register(type, CoordinateSystem$$1); +} + +/** + * Get dimensions of specified coordinate system. + * @param {string} type + * @return {Array.} + */ +function getCoordinateSystemDimensions(type) { + var coordSysCreator = CoordinateSystemManager.get(type); + if (coordSysCreator) { + return coordSysCreator.getDimensionsInfo + ? coordSysCreator.getDimensionsInfo() + : coordSysCreator.dimensions.slice(); + } +} + +/** + * 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} layoutTask + */ +function registerLayout(priority, layoutTask) { + normalizeRegister(visualFuncs, priority, layoutTask, PRIORITY_VISUAL_LAYOUT, 'layout'); +} + +/** + * @param {number} [priority=3000] + * @param {module:echarts/stream/Task} visualTask + */ +function registerVisual(priority, visualTask) { + normalizeRegister(visualFuncs, priority, visualTask, PRIORITY_VISUAL_CHART, 'visual'); +} + +/** + * @param {Object|Function} fn: {seriesType, createOnAllSeries, performRawSeries, reset} + */ +function normalizeRegister(targetList, priority, fn, defaultPriority, visualType) { + if (isFunction(priority) || isObject(priority)) { + fn = priority; + priority = defaultPriority; + } + + if (__DEV__) { + if (isNaN(priority) || priority == null) { + throw new Error('Illegal priority'); + } + // Check duplicate + each(targetList, function (wrap) { + assert(wrap.__raw !== fn); + }); + } + + var stageHandler = Scheduler.wrapStageHandler(fn, visualType); + + stageHandler.__prio = priority; + stageHandler.__raw = fn; + targetList.push(stageHandler); + + return stageHandler; +} + +/** + * @param {string} name + */ +function registerLoading(name, loadingFx) { + loadingEffects[name] = loadingFx; +} + +/** + * @param {Object} opts + * @param {string} [superClass] + */ +function extendComponentModel(opts/*, superClass*/) { + // var Clazz = ComponentModel; + // if (superClass) { + // var classType = parseClassType(superClass); + // Clazz = ComponentModel.getClass(classType.main, classType.sub, true); + // } + return ComponentModel.extend(opts); +} + +/** + * @param {Object} opts + * @param {string} [superClass] + */ +function extendComponentView(opts/*, superClass*/) { + // var Clazz = ComponentView; + // if (superClass) { + // var classType = parseClassType(superClass); + // Clazz = ComponentView.getClass(classType.main, classType.sub, true); + // } + return Component.extend(opts); +} + +/** + * @param {Object} opts + * @param {string} [superClass] + */ +function extendSeriesModel(opts/*, superClass*/) { + // var Clazz = SeriesModel; + // if (superClass) { + // superClass = 'series.' + superClass.replace('series.', ''); + // var classType = parseClassType(superClass); + // Clazz = ComponentModel.getClass(classType.main, classType.sub, true); + // } + return SeriesModel.extend(opts); +} + +/** + * @param {Object} opts + * @param {string} [superClass] + */ +function extendChartView(opts/*, superClass*/) { + // var Clazz = ChartView; + // if (superClass) { + // superClass = superClass.replace('series.', ''); + // var classType = parseClassType(superClass); + // Clazz = ChartView.getClass(classType.main, true); + // } + return Chart.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); + * }); + */ +function setCanvasCreator(creator) { + $override('createCanvas', creator); +} + +/** + * @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: {} + * }) + * }); + */ +function registerMap(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} + */ +function getMap(mapName) { + return mapDataStores[mapName]; +} + +registerVisual(PRIORITY_VISUAL_GLOBAL, seriesColor); +registerPreprocessor(backwardCompat); +registerProcessor(PRIORITY_PROCESSOR_STATISTIC, dataStack); +registerLoading('default', loadingDefault); + +// Default actions + +registerAction({ + type: 'highlight', + event: 'highlight', + update: 'highlight' +}, noop); + +registerAction({ + type: 'downplay', + event: 'downplay', + update: 'downplay' +}, noop); + +// Default theme +registerTheme('light', lightTheme); +registerTheme('dark', theme); + +// For backward compatibility, where the namespace `dataTool` will +// be mounted on `echarts` is the extension `dataTool` is imported. +var dataTool = {}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + + +function defaultKeyGetter(item) { + return item; +} + +/** + * @param {Array} oldArr + * @param {Array} newArr + * @param {Function} oldKeyGetter + * @param {Function} newKeyGetter + * @param {Object} [context] Can be visited by this.context in callback. + */ +function DataDiffer(oldArr, newArr, oldKeyGetter, newKeyGetter, context) { + this._old = oldArr; + this._new = newArr; + + this._oldKeyGetter = oldKeyGetter || defaultKeyGetter; + this._newKeyGetter = newKeyGetter || defaultKeyGetter; + + this.context = context; +} + +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 oldDataIndexMap = {}; + var newDataIndexMap = {}; + var oldDataKeyArr = []; + var newDataKeyArr = []; + var i; + + initIndexMap(oldArr, oldDataIndexMap, oldDataKeyArr, '_oldKeyGetter', this); + initIndexMap(newArr, newDataIndexMap, newDataKeyArr, '_newKeyGetter', this); + + // 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, keyGetterName, dataDiffer) { + for (var i = 0; i < arr.length; i++) { + // Add prefix to avoid conflict with Object.prototype. + var key = '_ec_' + dataDiffer[keyGetterName](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); + } + } +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var OTHER_DIMENSIONS = createHashMap([ + 'tooltip', 'label', 'itemName', 'itemId', 'seriesName' +]); + +function summarizeDimensions(data) { + var summary = {}; + var encode = summary.encode = {}; + var notExtraCoordDimMap = createHashMap(); + var defaultedLabel = []; + var defaultedTooltip = []; + + each$1(data.dimensions, function (dimName) { + var dimItem = data.getDimensionInfo(dimName); + + var coordDim = dimItem.coordDim; + if (coordDim) { + if (__DEV__) { + assert$1(OTHER_DIMENSIONS.get(coordDim) == null); + } + var coordDimArr = encode[coordDim]; + if (!encode.hasOwnProperty(coordDim)) { + coordDimArr = encode[coordDim] = []; + } + coordDimArr[dimItem.coordDimIndex] = dimName; + + if (!dimItem.isExtraCoord) { + notExtraCoordDimMap.set(coordDim, 1); + + // Use the last coord dim (and label friendly) as default label, + // because when dataset is used, it is hard to guess which dimension + // can be value dimension. If both show x, y on label is not look good, + // and conventionally y axis is focused more. + if (mayLabelDimType(dimItem.type)) { + defaultedLabel[0] = dimName; + } + } + if (dimItem.defaultTooltip) { + defaultedTooltip.push(dimName); + } + } + + OTHER_DIMENSIONS.each(function (v, otherDim) { + var otherDimArr = encode[otherDim]; + if (!encode.hasOwnProperty(otherDim)) { + otherDimArr = encode[otherDim] = []; + } + + var dimIndex = dimItem.otherDims[otherDim]; + if (dimIndex != null && dimIndex !== false) { + otherDimArr[dimIndex] = dimItem.name; + } + }); + }); + + var dataDimsOnCoord = []; + var encodeFirstDimNotExtra = {}; + + notExtraCoordDimMap.each(function (v, coordDim) { + var dimArr = encode[coordDim]; + // ??? FIXME extra coord should not be set in dataDimsOnCoord. + // But should fix the case that radar axes: simplify the logic + // of `completeDimension`, remove `extraPrefix`. + encodeFirstDimNotExtra[coordDim] = dimArr[0]; + // Not necessary to remove duplicate, because a data + // dim canot on more than one coordDim. + dataDimsOnCoord = dataDimsOnCoord.concat(dimArr); + }); + + summary.dataDimsOnCoord = dataDimsOnCoord; + summary.encodeFirstDimNotExtra = encodeFirstDimNotExtra; + + var encodeLabel = encode.label; + // FIXME `encode.label` is not recommanded, because formatter can not be set + // in this way. Use label.formatter instead. May be remove this approach someday. + if (encodeLabel && encodeLabel.length) { + defaultedLabel = encodeLabel.slice(); + } + + var encodeTooltip = encode.tooltip; + if (encodeTooltip && encodeTooltip.length) { + defaultedTooltip = encodeTooltip.slice(); + } + else if (!defaultedTooltip.length) { + defaultedTooltip = defaultedLabel.slice(); + } + + encode.defaultedLabel = defaultedLabel; + encode.defaultedTooltip = defaultedTooltip; + + return summary; +} + +function getDimensionTypeByAxis(axisType) { + return axisType === 'category' + ? 'ordinal' + : axisType === 'time' + ? 'time' + : 'float'; +} + +function mayLabelDimType(dimType) { + // In most cases, ordinal and time do not suitable for label. + // Ordinal info can be displayed on axis. Time is too long. + return !(dimType === 'ordinal' || dimType === 'time'); +} + +// function findTheLastDimMayLabel(data) { +// // Get last value dim +// var dimensions = data.dimensions.slice(); +// var valueType; +// var valueDim; +// while (dimensions.length && ( +// valueDim = dimensions.pop(), +// valueType = data.getDimensionInfo(valueDim).type, +// valueType === 'ordinal' || valueType === 'time' +// )) {} // jshint ignore:line +// return valueDim; +// } + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * List for data storage + * @module echarts/data/List + */ + +var isObject$4 = isObject$1; + +var UNDEFINED = 'undefined'; + +// Use prefix to avoid index to be the same as otherIdList[idx], +// which will cause weird udpate animation. +var ID_PREFIX = 'e\0\0'; + +var dataCtors = { + 'float': typeof Float64Array === UNDEFINED + ? Array : Float64Array, + 'int': typeof Int32Array === UNDEFINED + ? Array : Int32Array, + // Ordinal data type can be string or int + 'ordinal': Array, + 'number': Array, + 'time': Array +}; + +// Caution: MUST not use `new CtorUint32Array(arr, 0, len)`, because the Ctor of array is +// different from the Ctor of typed array. +var CtorUint32Array = typeof Uint32Array === UNDEFINED ? Array : Uint32Array; +var CtorUint16Array = typeof Uint16Array === UNDEFINED ? Array : Uint16Array; + +function getIndicesCtor(list) { + // The possible max value in this._indicies is always this._rawCount despite of filtering. + return list._rawCount > 65535 ? CtorUint32Array : CtorUint16Array; +} + +function cloneChunk(originalChunk) { + var Ctor = originalChunk.constructor; + // Only shallow clone is enough when Array. + return Ctor === Array ? originalChunk.slice() : new Ctor(originalChunk); +} + +var TRANSFERABLE_PROPERTIES = [ + 'hasItemOption', '_nameList', '_idList', '_invertedIndicesMap', + '_rawData', '_chunkSize', '_chunkCount', '_dimValueGetter', + '_count', '_rawCount', '_nameDimIdx', '_idDimIdx' +]; +var CLONE_PROPERTIES = [ + '_extent', '_approximateExtent', '_rawExtent' +]; + +function transferProperties(target, source) { + each$1(TRANSFERABLE_PROPERTIES.concat(source.__wrappedMethods || []), function (propName) { + if (source.hasOwnProperty(propName)) { + target[propName] = source[propName]; + } + }); + + target.__wrappedMethods = source.__wrappedMethods; + + each$1(CLONE_PROPERTIES, function (propName) { + target[propName] = clone(source[propName]); + }); + + target._calculationInfo = extend(source._calculationInfo); +} + + + + + +/** + * @constructor + * @alias module:echarts/data/List + * + * @param {Array.} dimensions + * For example, ['someDimName', {name: 'someDimName', type: 'someDimType'}, ...]. + * Dimensions should be concrete names like x, y, z, lng, lat, angle, radius + * Spetial fields: { + * ordinalMeta: + * createInvertedIndices: + * } + * @param {module:echarts/model/Model} hostModel + */ +var List = function (dimensions, hostModel) { + + dimensions = dimensions || ['x', 'y']; + + var dimensionInfos = {}; + var dimensionNames = []; + var invertedIndicesMap = {}; + + for (var i = 0; i < dimensions.length; i++) { + // Use the original dimensions[i], where other flag props may exists. + var dimensionInfo = dimensions[i]; + + if (isString(dimensionInfo)) { + dimensionInfo = {name: dimensionInfo}; + } + + var dimensionName = dimensionInfo.name; + dimensionInfo.type = dimensionInfo.type || 'float'; + if (!dimensionInfo.coordDim) { + dimensionInfo.coordDim = dimensionName; + dimensionInfo.coordDimIndex = 0; + } + + dimensionInfo.otherDims = dimensionInfo.otherDims || {}; + dimensionNames.push(dimensionName); + dimensionInfos[dimensionName] = dimensionInfo; + + dimensionInfo.index = i; + + if (dimensionInfo.createInvertedIndices) { + invertedIndicesMap[dimensionName] = []; + } + } + + /** + * @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 = null; + + this._count = 0; + this._rawCount = 0; + + /** + * 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 = []; + + /** + * 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 = []; + + /** + * Key: visual type, Value: boolean + * @type {Object} + * @readOnly + */ + this.hasItemVisual = {}; + + /** + * Item layout properties after layout + * @type {Array.} + * @private + */ + this._itemLayouts = []; + + /** + * Graphic elemnents + * @type {Array.} + * @private + */ + this._graphicEls = []; + + /** + * Max size of each chunk. + * @type {number} + * @private + */ + this._chunkSize = 1e5; + + /** + * @type {number} + * @private + */ + this._chunkCount = 0; + + /** + * @type {Array.} + * @private + */ + this._rawData; + + /** + * Raw extent will not be cloned, but only transfered. + * It will not be calculated util needed. + * key: dim, + * value: {end: number, extent: Array.} + * @type {Object} + * @private + */ + this._rawExtent = {}; + + /** + * @type {Object} + * @private + */ + this._extent = {}; + + /** + * key: dim + * value: extent + * @type {Object} + * @private + */ + this._approximateExtent = {}; + + /** + * Cache summary info for fast visit. See "dimensionHelper". + * @type {Object} + * @private + */ + this._dimensionsSummary = summarizeDimensions(this); + + /** + * @type {Object.} + * @private + */ + this._invertedIndicesMap = invertedIndicesMap; + + /** + * @type {Object} + * @private + */ + this._calculationInfo = {}; +}; + +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 calculation 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) { + // Do not clone, because there may be categories in dimInfo. + return this._dimensionInfos[this.getDimension(dim)]; +}; + +/** + * @return {Array.} concrete dimension name list on coord. + */ +listProto.getDimensionsOnCoord = function () { + return this._dimensionsSummary.dataDimsOnCoord.slice(); +}; + +/** + * @param {string} coordDim + * @param {number} [idx] A coordDim may map to more than one data dim. + * If idx is `true`, return a array of all mapped dims. + * If idx is not specified, return the first dim not extra. + * @return {string|Array.} concrete data dim. + * If idx is number, and not found, return null/undefined. + * If idx is `true`, and not found, return empty array (always return array). + */ +listProto.mapDimension = function (coordDim, idx) { + var dimensionsSummary = this._dimensionsSummary; + + if (idx == null) { + return dimensionsSummary.encodeFirstDimNotExtra[coordDim]; + } + + var dims = dimensionsSummary.encode[coordDim]; + return idx === true + // always return array if idx is `true` + ? (dims || []).slice() + : (dims && dims[idx]); +}; + +/** + * Initialize from data + * @param {Array.} data source or data or data provider. + * @param {Array.} [nameLIst] The name of a datum is used on data diff and + * defualt label/tooltip. + * A name can be specified in encode.itemName, + * or dataItem.name (only for series option data), + * or provided in nameList from outside. + * @param {Function} [dimValueGetter] (dataItem, dimName, dataIndex, dimIndex) => number + */ +listProto.initData = function (data, nameList, dimValueGetter) { + + var notProvider = Source.isInstance(data) || isArrayLike(data); + if (notProvider) { + data = new DefaultDataProvider(data, this.dimensions.length); + } + + if (__DEV__) { + if (!notProvider && (typeof data.getItem != 'function' || typeof data.count != 'function')) { + throw new Error('Inavlid data provider.'); + } + } + + this._rawData = data; + + // Clear + this._storage = {}; + this._indices = null; + + this._nameList = nameList || []; + + this._idList = []; + + this._nameRepeatCount = {}; + + if (!dimValueGetter) { + this.hasItemOption = false; + } + + /** + * @readOnly + */ + this.defaultDimValueGetter = defaultDimValueGetters[ + this._rawData.getSource().sourceFormat + ]; + + // Default dim value getter + this._dimValueGetter = dimValueGetter = dimValueGetter + || this.defaultDimValueGetter; + + // Reset raw extent. + this._rawExtent = {}; + + this._initDataFromProvider(0, data.count()); + + // If data has no item option. + if (data.pure) { + this.hasItemOption = false; + } +}; + +listProto.getProvider = function () { + return this._rawData; +}; + +listProto.appendData = function (data) { + if (__DEV__) { + assert$1(!this._indices, 'appendData can only be called on raw data.'); + } + + var rawData = this._rawData; + var start = this.count(); + rawData.appendData(data); + var end = rawData.count(); + if (!rawData.persistent) { + end += start; + } + this._initDataFromProvider(start, end); +}; + +listProto._initDataFromProvider = function (start, end) { + // Optimize. + if (start >= end) { + return; + } + + var chunkSize = this._chunkSize; + var rawData = this._rawData; + var storage = this._storage; + var dimensions = this.dimensions; + var dimLen = dimensions.length; + var dimensionInfoMap = this._dimensionInfos; + var nameList = this._nameList; + var idList = this._idList; + var rawExtent = this._rawExtent; + var nameRepeatCount = this._nameRepeatCount = {}; + var nameDimIdx; + + var chunkCount = this._chunkCount; + var lastChunkIndex = chunkCount - 1; + for (var i = 0; i < dimLen; i++) { + var dim = dimensions[i]; + if (!rawExtent[dim]) { + rawExtent[dim] = getInitialExtent(); + } + + var dimInfo = dimensionInfoMap[dim]; + if (dimInfo.otherDims.itemName === 0) { + nameDimIdx = this._nameDimIdx = i; + } + if (dimInfo.otherDims.itemId === 0) { + this._idDimIdx = i; + } + var DataCtor = dataCtors[dimInfo.type]; + + if (!storage[dim]) { + storage[dim] = []; + } + var resizeChunkArray = storage[dim][lastChunkIndex]; + if (resizeChunkArray && resizeChunkArray.length < chunkSize) { + var newStore = new DataCtor(Math.min(end - lastChunkIndex * chunkSize, chunkSize)); + // The cost of the copy is probably inconsiderable + // within the initial chunkSize. + for (var j = 0; j < resizeChunkArray.length; j++) { + newStore[j] = resizeChunkArray[j]; + } + storage[dim][lastChunkIndex] = newStore; + } + + // Create new chunks. + for (var k = chunkCount * chunkSize; k < end; k += chunkSize) { + storage[dim].push(new DataCtor(Math.min(end - k, chunkSize))); + } + this._chunkCount = storage[dim].length; + } + + var dataItem = new Array(dimLen); + for (var idx = start; idx < end; idx++) { + // NOTICE: Try not to write things into dataItem + dataItem = rawData.getItem(idx, dataItem); + // 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 category + // Use a tempValue to normalize the value to be a (x, y) value + var chunkIndex = Math.floor(idx / chunkSize); + var chunkOffset = idx % chunkSize; + + // Store the data by dimensions + for (var k = 0; k < dimLen; k++) { + var dim = dimensions[k]; + var dimStorage = storage[dim][chunkIndex]; + // PENDING NULL is empty or zero + var val = this._dimValueGetter(dataItem, dim, idx, k); + dimStorage[chunkOffset] = val; + + var dimRawExtent = rawExtent[dim]; + if (val < dimRawExtent[0]) { + dimRawExtent[0] = val; + } + if (val > dimRawExtent[1]) { + dimRawExtent[1] = val; + } + } + + // ??? FIXME not check by pure but sourceFormat? + // TODO refactor these logic. + if (!rawData.pure) { + var name = nameList[idx]; + + if (dataItem && name == null) { + // If dataItem is {name: ...}, it has highest priority. + // That is appropriate for many common cases. + if (dataItem.name != null) { + // There is no other place to persistent dataItem.name, + // so save it to nameList. + nameList[idx] = name = dataItem.name; + } + else if (nameDimIdx != null) { + var nameDim = dimensions[nameDimIdx]; + var nameDimChunk = storage[nameDim][chunkIndex]; + if (nameDimChunk) { + name = nameDimChunk[chunkOffset]; + var ordinalMeta = dimensionInfoMap[nameDim].ordinalMeta; + if (ordinalMeta && ordinalMeta.categories.length) { + name = ordinalMeta.categories[name]; + } + } + } + } + + // Try using the id in option + // id or name is used on dynamical data, mapping old and new items. + var id = dataItem == null ? null : dataItem.id; + + if (id == null && name != null) { + // 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 != null && (idList[idx] = id); + } + } + + if (!rawData.persistent && rawData.clean) { + // Clean unused data if data source is typed array. + rawData.clean(); + } + + this._rawCount = this._count = end; + + // Reset data extent + this._extent = {}; + + prepareInvertedIndex(this); +}; + +function prepareInvertedIndex(list) { + var invertedIndicesMap = list._invertedIndicesMap; + each$1(invertedIndicesMap, function (invertedIndices, dim) { + var dimInfo = list._dimensionInfos[dim]; + + // Currently, only dimensions that has ordinalMeta can create inverted indices. + var ordinalMeta = dimInfo.ordinalMeta; + if (ordinalMeta) { + invertedIndices = invertedIndicesMap[dim] = new CtorUint32Array( + ordinalMeta.categories.length + ); + // The default value of TypedArray is 0. To avoid miss + // mapping to 0, we should set it as NaN. + for (var i = 0; i < invertedIndices.length; i++) { + invertedIndices[i] = NaN; + } + for (var i = 0; i < list._count; i++) { + // Only support the case that all values are distinct. + invertedIndices[list.get(dim, i)] = i; + } + } + }); +} + +function getRawValueFromStore(list, dimIndex, rawIndex) { + var val; + if (dimIndex != null) { + var chunkSize = list._chunkSize; + var chunkIndex = Math.floor(rawIndex / chunkSize); + var chunkOffset = rawIndex % chunkSize; + var dim = list.dimensions[dimIndex]; + var chunk = list._storage[dim][chunkIndex]; + if (chunk) { + val = chunk[chunkOffset]; + var ordinalMeta = list._dimensionInfos[dim].ordinalMeta; + if (ordinalMeta && ordinalMeta.categories.length) { + val = ordinalMeta.categories[val]; + } + } + } + return val; +} + +/** + * @return {number} + */ +listProto.count = function () { + return this._count; +}; + +listProto.getIndices = function () { + var newIndices; + + var indices = this._indices; + if (indices) { + var Ctor = indices.constructor; + var thisCount = this._count; + // `new Array(a, b, c)` is different from `new Uint32Array(a, b, c)`. + if (Ctor === Array) { + newIndices = new Ctor(thisCount); + for (var i = 0; i < thisCount; i++) { + newIndices[i] = indices[i]; + } + } + else { + newIndices = new Ctor(indices.buffer, 0, thisCount); + } + } + else { + var Ctor = getIndicesCtor(this); + var newIndices = new Ctor(this.count()); + for (var i = 0; i < newIndices.length; i++) { + newIndices[i] = i; + } + } + + return newIndices; +}; + +/** + * 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 */) { + if (!(idx >= 0 && idx < this._count)) { + return NaN; + } + var storage = this._storage; + if (!storage[dim]) { + // TODO Warn ? + return NaN; + } + + idx = this.getRawIndex(idx); + + var chunkIndex = Math.floor(idx / this._chunkSize); + var chunkOffset = idx % this._chunkSize; + + var chunkStore = storage[dim][chunkIndex]; + var value = chunkStore[chunkOffset]; + // 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; +}; + +/** + * @param {string} dim concrete dim + * @param {number} rawIndex + * @return {number|string} + */ +listProto.getByRawIndex = function (dim, rawIdx) { + if (!(rawIdx >= 0 && rawIdx < this._rawCount)) { + return NaN; + } + var dimStore = this._storage[dim]; + if (!dimStore) { + // TODO Warn ? + return NaN; + } + + var chunkIndex = Math.floor(rawIdx / this._chunkSize); + var chunkOffset = rawIdx % this._chunkSize; + var chunkStore = dimStore[chunkIndex]; + return chunkStore[chunkOffset]; +}; + +/** + * FIXME Use `get` on chrome maybe slow(in filterSelf and selectRange). + * Hack a much simpler _getFast + * @private + */ +listProto._getFast = function (dim, rawIdx) { + var chunkIndex = Math.floor(rawIdx / this._chunkSize); + var chunkOffset = rawIdx % this._chunkSize; + var chunkStore = this._storage[dim][chunkIndex]; + return chunkStore[chunkOffset]; +}; + +/** + * Get value for multi dimensions. + * @param {Array.} [dimensions] If ignored, using all dimensions. + * @param {number} idx + * @return {number} + */ +listProto.getValues = function (dimensions, idx /*, stack */) { + var values = []; + + if (!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 '-' + * Only check the coord dimensions. + * @param {string} dim + * @param {number} idx + * @return {number} + */ +listProto.hasValue = function (idx) { + var dataDimsOnCoord = this._dimensionsSummary.dataDimsOnCoord; + var dimensionInfos = this._dimensionInfos; + for (var i = 0, len = dataDimsOnCoord.length; i < len; i++) { + if ( + // Ordinal type can be string or number + dimensionInfos[dataDimsOnCoord[i]].type !== 'ordinal' + // FIXME check ordinal when using index? + && isNaN(this.get(dataDimsOnCoord[i], idx)) + ) { + return false; + } + } + return true; +}; + +/** + * Get extent of data in one dimension + * @param {string} dim + * @param {boolean} stack + */ +listProto.getDataExtent = function (dim /*, stack */) { + // Make sure use concrete dim as cache name. + dim = this.getDimension(dim); + var dimData = this._storage[dim]; + var initialExtent = getInitialExtent(); + + // stack = !!((stack || false) && this.getCalculationInfo(dim)); + + if (!dimData) { + return initialExtent; + } + + // Make more strict checkings to ensure hitting cache. + var currEnd = this.count(); + // var cacheName = [dim, !!stack].join('_'); + // var cacheName = dim; + + // Consider the most cases when using data zoom, `getDataExtent` + // happened before filtering. We cache raw extent, which is not + // necessary to be cleared and recalculated when restore data. + var useRaw = !this._indices; // && !stack; + var dimExtent; + + if (useRaw) { + return this._rawExtent[dim].slice(); + } + dimExtent = this._extent[dim]; + if (dimExtent) { + return dimExtent.slice(); + } + dimExtent = initialExtent; + + var min = dimExtent[0]; + var max = dimExtent[1]; + + for (var i = 0; i < currEnd; i++) { + // var value = stack ? this.get(dim, i, true) : this._getFast(dim, this.getRawIndex(i)); + var value = this._getFast(dim, this.getRawIndex(i)); + value < min && (min = value); + value > max && (max = value); + } + + dimExtent = [min, max]; + + this._extent[dim] = dimExtent; + + return dimExtent; +}; + +/** + * Optimize for the scenario that data is filtered by a given extent. + * Consider that if data amount is more than hundreds of thousand, + * extent calculation will cost more than 10ms and the cache will + * be erased because of the filtering. + */ +listProto.getApproximateExtent = function (dim /*, stack */) { + dim = this.getDimension(dim); + return this._approximateExtent[dim] || this.getDataExtent(dim /*, stack */); +}; + +listProto.setApproximateExtent = function (extent, dim /*, stack */) { + dim = this.getDimension(dim); + this._approximateExtent[dim] = extent.slice(); +}; + +/** + * @param {string} key + * @return {*} + */ +listProto.getCalculationInfo = function (key) { + return this._calculationInfo[key]; +}; + +/** + * @param {string|Object} key or k-v object + * @param {*} [value] + */ +listProto.setCalculationInfo = function (key, value) { + isObject$4(key) + ? extend(this._calculationInfo, key) + : (this._calculationInfo[key] = value); +}; + +/** + * Get sum of data in one dimension + * @param {string} dim + */ +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; +}; + +/** + * Get median of data in one dimension + * @param {string} dim + */ +listProto.getMedian = function (dim /*, stack */) { + var dimDataArray = []; + // map all data of one dimension + this.each(dim, function (val, idx) { + if (!isNaN(val)) { + dimDataArray.push(val); + } + }); + + // TODO + // Use quick select? + + // immutability & sort + var sortedDimDataArray = [].concat(dimDataArray).sort(function(a, b) { + return a - b; + }); + var len = this.count(); + // calculate median + return len === 0 ? 0 : + len % 2 === 1 ? sortedDimDataArray[(len - 1) / 2] : + (sortedDimDataArray[len / 2] + sortedDimDataArray[len / 2 - 1]) / 2; +}; + +// /** +// * Retreive the index with given value +// * @param {string} dim Concrete dimension. +// * @param {number} value +// * @return {number} +// */ +// Currently incorrect: should return dataIndex but not rawIndex. +// Do not fix it until this method is to be used somewhere. +// FIXME Precision of float value +// listProto.indexOf = function (dim, value) { +// var storage = this._storage; +// var dimData = storage[dim]; +// var chunkSize = this._chunkSize; +// if (dimData) { +// for (var i = 0, len = this.count(); i < len; i++) { +// var chunkIndex = Math.floor(i / chunkSize); +// var chunkOffset = i % chunkSize; +// if (dimData[chunkIndex][chunkOffset] === value) { +// return i; +// } +// } +// } +// return -1; +// }; + +/** + * Only support the dimension which inverted index created. + * Do not support other cases until required. + * @param {string} concrete dim + * @param {number|string} value + * @return {number} rawIndex + */ +listProto.rawIndexOf = function (dim, value) { + var invertedIndices = dim && this._invertedIndicesMap[dim]; + if (__DEV__) { + if (!invertedIndices) { + throw new Error('Do not supported yet'); + } + } + var rawIndex = invertedIndices[value]; + if (rawIndex == null || isNaN(rawIndex)) { + return -1; + } + return rawIndex; +}; + +/** + * Retreive the index with given name + * @param {number} idx + * @param {number} name + * @return {number} + */ +listProto.indexOfName = function (name) { + for (var i = 0, len = this.count(); i < len; i++) { + if (this.getName(i) === 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) { + if (!this._indices) { + return rawIndex; + } + + if (rawIndex >= this._rawCount || rawIndex < 0) { + return -1; + } + + // Indices are ascending + var indices = this._indices; + + // If rawIndex === dataIndex + var rawDataIndex = indices[rawIndex]; + if (rawDataIndex != null && rawDataIndex < this._count && rawDataIndex === rawIndex) { + return rawIndex; + } + + var left = 0; + var right = this._count - 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 {number} [maxDistance=Infinity] + * @return {Array.} Considere multiple points has the same value. + */ +listProto.indicesOfNearest = function (dim, value, maxDistance) { + var storage = this._storage; + var dimData = storage[dim]; + var nearestIndices = []; + + if (!dimData) { + return nearestIndices; + } + + if (maxDistance == null) { + maxDistance = Infinity; + } + + var minDist = Number.MAX_VALUE; + var minDiff = -1; + 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 + if (dist < minDist || (diff >= 0 && minDiff < 0)) { + minDist = dist; + minDiff = diff; + nearestIndices.length = 0; + } + nearestIndices.push(i); + } + } + return nearestIndices; +}; + +/** + * Get raw data index + * @param {number} idx + * @return {number} + */ +listProto.getRawIndex = getRawIndexWithoutIndices; + +function getRawIndexWithoutIndices(idx) { + return idx; +} + +function getRawIndexWithIndices(idx) { + if (idx < this._count && idx >= 0) { + return this._indices[idx]; + } + return -1; +} + +/** + * Get raw data item + * @param {number} idx + * @return {number} + */ +listProto.getRawDataItem = function (idx) { + if (!this._rawData.persistent) { + var val = []; + for (var i = 0; i < this.dimensions.length; i++) { + var dim = this.dimensions[i]; + val.push(this.get(dim, idx)); + } + return val; + } + else { + return this._rawData.getItem(this.getRawIndex(idx)); + } +}; + +/** + * @param {number} idx + * @param {boolean} [notDefaultIdx=false] + * @return {string} + */ +listProto.getName = function (idx) { + var rawIndex = this.getRawIndex(idx); + return this._nameList[rawIndex] + || getRawValueFromStore(this, this._nameDimIdx, rawIndex) + || ''; +}; + +/** + * @param {number} idx + * @param {boolean} [notDefaultIdx=false] + * @return {string} + */ +listProto.getId = function (idx) { + return getId(this, this.getRawIndex(idx)); +}; + +function getId(list, rawIndex) { + var id = list._idList[rawIndex]; + if (id == null) { + id = getRawValueFromStore(list, list._idDimIdx, rawIndex); + } + if (id == null) { + // FIXME Check the usage in graph, should not use prefix. + id = ID_PREFIX + rawIndex; + } + return id; +} + +function normalizeDimensions(dimensions) { + if (!isArray(dimensions)) { + dimensions = [dimensions]; + } + return dimensions; +} + +function validateDimensions(list, dims) { + for (var i = 0; i < dims.length; i++) { + // stroage may be empty when no data, so use + // dimensionInfos to check. + if (!list._dimensionInfos[dims[i]]) { + console.error('Unkown dimension ' + dims[i]); + } + } +} + +/** + * Data iteration + * @param {string|Array.} + * @param {Function} cb + * @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, context, contextCompat) { + 'use strict'; + + if (!this._count) { + return; + } + + if (typeof dims === 'function') { + contextCompat = context; + context = cb; + cb = dims; + dims = []; + } + + // contextCompat just for compat echarts3 + context = context || contextCompat || this; + + dims = map(normalizeDimensions(dims), this.getDimension, this); + + if (__DEV__) { + validateDimensions(this, dims); + } + + var dimSize = dims.length; + + for (var i = 0; i < this.count(); i++) { + // Simple optimization + switch (dimSize) { + case 0: + cb.call(context, i); + break; + case 1: + cb.call(context, this.get(dims[0], i), i); + break; + case 2: + cb.call(context, this.get(dims[0], i), this.get(dims[1], i), i); + break; + default: + var k = 0; + var value = []; + for (; k < dimSize; k++) { + value[k] = this.get(dims[k], i); + } + // Index + value[k] = i; + cb.apply(context, value); + } + } +}; + +/** + * Data filter + * @param {string|Array.} + * @param {Function} cb + * @param {*} [context=this] + */ +listProto.filterSelf = function (dimensions, cb, context, contextCompat) { + 'use strict'; + + if (!this._count) { + return; + } + + if (typeof dimensions === 'function') { + contextCompat = context; + context = cb; + cb = dimensions; + dimensions = []; + } + + // contextCompat just for compat echarts3 + context = context || contextCompat || this; + + dimensions = map( + normalizeDimensions(dimensions), this.getDimension, this + ); + + if (__DEV__) { + validateDimensions(this, dimensions); + } + + + var count = this.count(); + var Ctor = getIndicesCtor(this); + var newIndices = new Ctor(count); + var value = []; + var dimSize = dimensions.length; + + var offset = 0; + var dim0 = dimensions[0]; + + for (var i = 0; i < count; i++) { + var keep; + var rawIdx = this.getRawIndex(i); + // Simple optimization + if (dimSize === 0) { + keep = cb.call(context, i); + } + else if (dimSize === 1) { + var val = this._getFast(dim0, rawIdx); + keep = cb.call(context, val, i); + } + else { + for (var k = 0; k < dimSize; k++) { + value[k] = this._getFast(dim0, rawIdx); + } + value[k] = i; + keep = cb.apply(context, value); + } + if (keep) { + newIndices[offset++] = rawIdx; + } + } + + // Set indices after filtered. + if (offset < count) { + this._indices = newIndices; + } + this._count = offset; + // Reset data extent + this._extent = {}; + + this.getRawIndex = this._indices ? getRawIndexWithIndices : getRawIndexWithoutIndices; + + return this; +}; + +/** + * Select data in range. (For optimization of filter) + * (Manually inline code, support 5 million data filtering in data zoom.) + */ +listProto.selectRange = function (range) { + 'use strict'; + + if (!this._count) { + return; + } + + var dimensions = []; + for (var dim in range) { + if (range.hasOwnProperty(dim)) { + dimensions.push(dim); + } + } + + if (__DEV__) { + validateDimensions(this, dimensions); + } + + var dimSize = dimensions.length; + if (!dimSize) { + return; + } + + var originalCount = this.count(); + var Ctor = getIndicesCtor(this); + var newIndices = new Ctor(originalCount); + + var offset = 0; + var dim0 = dimensions[0]; + + var min = range[dim0][0]; + var max = range[dim0][1]; + + var quickFinished = false; + if (!this._indices) { + // Extreme optimization for common case. About 2x faster in chrome. + var idx = 0; + if (dimSize === 1) { + var dimStorage = this._storage[dimensions[0]]; + for (var k = 0; k < this._chunkCount; k++) { + var chunkStorage = dimStorage[k]; + var len = Math.min(this._count - k * this._chunkSize, this._chunkSize); + for (var i = 0; i < len; i++) { + var val = chunkStorage[i]; + // NaN will not be filtered. Consider the case, in line chart, empty + // value indicates the line should be broken. But for the case like + // scatter plot, a data item with empty value will not be rendered, + // but the axis extent may be effected if some other dim of the data + // item has value. Fortunately it is not a significant negative effect. + if ( + (val >= min && val <= max) || isNaN(val) + ) { + newIndices[offset++] = idx; + } + idx++; + } + } + quickFinished = true; + } + else if (dimSize === 2) { + var dimStorage = this._storage[dim0]; + var dimStorage2 = this._storage[dimensions[1]]; + var min2 = range[dimensions[1]][0]; + var max2 = range[dimensions[1]][1]; + for (var k = 0; k < this._chunkCount; k++) { + var chunkStorage = dimStorage[k]; + var chunkStorage2= dimStorage2[k]; + var len = Math.min(this._count - k * this._chunkSize, this._chunkSize); + for (var i = 0; i < len; i++) { + var val = chunkStorage[i]; + var val2 = chunkStorage2[i]; + // Do not filter NaN, see comment above. + if (( + (val >= min && val <= max) || isNaN(val) + ) + && ( + (val2 >= min2 && val2 <= max2) || isNaN(val2) + ) + ) { + newIndices[offset++] = idx; + } + idx++; + } + } + quickFinished = true; + } + } + if (!quickFinished) { + if (dimSize === 1) { + for (var i = 0; i < originalCount; i++) { + var rawIndex = this.getRawIndex(i); + var val = this._getFast(dim0, rawIndex); + // Do not filter NaN, see comment above. + if ( + (val >= min && val <= max) || isNaN(val) + ) { + newIndices[offset++] = rawIndex; + } + } + } + else { + for (var i = 0; i < originalCount; i++) { + var keep = true; + var rawIndex = this.getRawIndex(i); + for (var k = 0; k < dimSize; k++) { + var dimk = dimensions[k]; + var val = this._getFast(dim, rawIndex); + // Do not filter NaN, see comment above. + if (val < range[dimk][0] || val > range[dimk][1]) { + keep = false; + } + } + if (keep) { + newIndices[offset++] = this.getRawIndex(i); + } + } + } + } + + // Set indices after filtered. + if (offset < originalCount) { + this._indices = newIndices; + } + this._count = offset; + // Reset data extent + this._extent = {}; + + this.getRawIndex = this._indices ? getRawIndexWithIndices : getRawIndexWithoutIndices; + + return this; +}; + +/** + * Data mapping to a plain array + * @param {string|Array.} [dimensions] + * @param {Function} cb + * @param {*} [context=this] + * @return {Array} + */ +listProto.mapArray = function (dimensions, cb, context, contextCompat) { + 'use strict'; + + if (typeof dimensions === 'function') { + contextCompat = context; + context = cb; + cb = dimensions; + dimensions = []; + } + + // contextCompat just for compat echarts3 + context = context || contextCompat || this; + + var result = []; + this.each(dimensions, function () { + result.push(cb && cb.apply(this, arguments)); + }, context); + return result; +}; + +// Data in excludeDimensions is copied, otherwise transfered. +function cloneListForMapAndSample(original, excludeDimensions) { + var allDimensions = original.dimensions; + var list = new List( + 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]; + if (originalStorage[dim]) { + // Notice that we do not reset invertedIndicesMap here, becuase + // there is no scenario of mapping or sampling ordinal dimension. + if (indexOf(excludeDimensions, dim) >= 0) { + storage[dim] = cloneDimStore(originalStorage[dim]); + list._rawExtent[dim] = getInitialExtent(); + list._extent[dim] = null; + } + else { + // Direct reference for other dimensions + storage[dim] = originalStorage[dim]; + } + } + } + return list; +} + +function cloneDimStore(originalDimStore) { + var newDimStore = new Array(originalDimStore.length); + for (var j = 0; j < originalDimStore.length; j++) { + newDimStore[j] = cloneChunk(originalDimStore[j]); + } + return newDimStore; +} + +function getInitialExtent() { + return [Infinity, -Infinity]; +} + +/** + * Data mapping to a new List with given dimensions + * @param {string|Array.} dimensions + * @param {Function} cb + * @param {*} [context=this] + * @return {Array} + */ +listProto.map = function (dimensions, cb, context, contextCompat) { + 'use strict'; + + // contextCompat just for compat echarts3 + context = context || contextCompat || this; + + dimensions = map( + normalizeDimensions(dimensions), this.getDimension, this + ); + + if (__DEV__) { + validateDimensions(this, dimensions); + } + + var list = cloneListForMapAndSample(this, dimensions); + + // Following properties are all immutable. + // So we can reference to the same value + list._indices = this._indices; + list.getRawIndex = list._indices ? getRawIndexWithIndices : getRawIndexWithoutIndices; + + var storage = list._storage; + + var tmpRetValue = []; + var chunkSize = this._chunkSize; + var dimSize = dimensions.length; + var dataCount = this.count(); + var values = []; + var rawExtent = list._rawExtent; + + for (var dataIndex = 0; dataIndex < dataCount; dataIndex++) { + for (var dimIndex = 0; dimIndex < dimSize; dimIndex++) { + values[dimIndex] = this.get(dimensions[dimIndex], dataIndex /*, stack */); + } + values[dimSize] = dataIndex; + + var retValue = cb && cb.apply(context, values); + if (retValue != null) { + // a number or string (in oridinal dimension)? + if (typeof retValue !== 'object') { + tmpRetValue[0] = retValue; + retValue = tmpRetValue; + } + + var rawIndex = this.getRawIndex(dataIndex); + var chunkIndex = Math.floor(rawIndex / chunkSize); + var chunkOffset = rawIndex % chunkSize; + + for (var i = 0; i < retValue.length; i++) { + var dim = dimensions[i]; + var val = retValue[i]; + var rawExtentOnDim = rawExtent[dim]; + + var dimStore = storage[dim]; + if (dimStore) { + dimStore[chunkIndex][chunkOffset] = val; + } + + if (val < rawExtentOnDim[0]) { + rawExtentOnDim[0] = val; + } + if (val > rawExtentOnDim[1]) { + rawExtentOnDim[1] = val; + } + } + } + } + + 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 targetStorage = list._storage; + + var frameValues = []; + var frameSize = Math.floor(1 / rate); + + var dimStore = targetStorage[dimension]; + var len = this.count(); + var chunkSize = this._chunkSize; + var rawExtentOnDim = list._rawExtent[dimension]; + + var newIndices = new (getIndicesCtor(this))(len); + + var offset = 0; + 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 dataIdx = this.getRawIndex(i + k); + var originalChunkIndex = Math.floor(dataIdx / chunkSize); + var originalChunkOffset = dataIdx % chunkSize; + frameValues[k] = dimStore[originalChunkIndex][originalChunkOffset]; + } + var value = sampleValue(frameValues); + var sampleFrameIdx = this.getRawIndex( + Math.min(i + sampleIndex(frameValues, value) || 0, len - 1) + ); + var sampleChunkIndex = Math.floor(sampleFrameIdx / chunkSize); + var sampleChunkOffset = sampleFrameIdx % chunkSize; + // Only write value on the filtered data + dimStore[sampleChunkIndex][sampleChunkOffset] = value; + + if (value < rawExtentOnDim[0]) { + rawExtentOnDim[0] = value; + } + if (value > rawExtentOnDim[1]) { + rawExtentOnDim[1] = value; + } + + newIndices[offset++] = sampleFrameIdx; + } + + list._count = offset; + list._indices = newIndices; + + list.getRawIndex = getRawIndexWithIndices; + + return list; +}; + +/** + * Get model of one data item. + * + * @param {number} idx + */ +// FIXME Model proxy ? +listProto.getItemModel = function (idx) { + var hostModel = this.hostModel; + return new Model(this.getRawDataItem(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 thisList = this; + + return new DataDiffer( + otherList ? otherList.getIndices() : [], + this.getIndices(), + function (idx) { + return getId(otherList, idx); + }, + function (idx) { + return getId(thisList, 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$4(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|Object} key + * @param {*} [val] + */ +listProto.setLayout = function (key, val) { + if (isObject$4(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$$1) { + this._itemLayouts[idx] = merge$$1 + ? 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=false] + */ +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] || {}; + var hasItemVisual = this.hasItemVisual; + this._itemVisuals[idx] = itemVisual; + + if (isObject$4(key)) { + for (var name in key) { + if (key.hasOwnProperty(name)) { + itemVisual[name] = key[name]; + hasItemVisual[name] = true; + } + } + return; + } + itemVisual[key] = value; + hasItemVisual[key] = true; +}; + +/** + * Clear itemVisuals and list visual. + */ +listProto.clearAllVisual = function () { + this._visual = {}; + this._itemVisuals = []; + this.hasItemVisual = {}; +}; + +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) { + each$1(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 (list) { + if (!list) { + var dimensionInfoList = map(this.dimensions, this.getDimensionInfo, this); + list = new List(dimensionInfoList, this.hostModel); + } + + // FIXME + list._storage = this._storage; + + transferProperties(list, this); + + // Clone will not change the data extent and indices + if (this._indices) { + var Ctor = this._indices.constructor; + list._indices = new Ctor(this._indices); + } + else { + list._indices = null; + } + list.getRawIndex = list._indices ? getRawIndexWithIndices : getRawIndexWithoutIndices; + + 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(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', 'selectRange']; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * @deprecated + * Use `echarts/data/helper/createDimensions` instead. + */ + +/** + * @see {module:echarts/test/ut/spec/data/completeDimensions} + * + * Complete the dimensions array, by user defined `dimension` and `encode`, + * and guessing from the data structure. + * If no 'value' dimension specified, the first no-named dimension will be + * named as 'value'. + * + * @param {Array.} sysDims Necessary dimensions, like ['x', 'y'], which + * provides not only dim template, but also default order. + * properties: 'name', 'type', 'displayName'. + * `name` of each item provides default coord name. + * [{dimsDef: [string|Object, ...]}, ...] dimsDef of sysDim item provides default dim name, and + * provide dims count that the sysDim required. + * [{ordinalMeta}] can be specified. + * @param {module:echarts/data/Source|Array|Object} source or data (for compatibal with pervious) + * @param {Object} [opt] + * @param {Array.} [opt.dimsDef] option.series.dimensions User defined dimensions + * For example: ['asdf', {name, type}, ...]. + * @param {Object|HashMap} [opt.encodeDef] option.series.encode {x: 2, y: [3, 1], tooltip: [1, 2], label: 3} + * @param {string} [opt.generateCoord] Generate coord dim with the given name. + * If not specified, extra dim names will be: + * 'value', 'value0', 'value1', ... + * @param {number} [opt.generateCoordCount] By default, the generated dim name is `generateCoord`. + * If `generateCoordCount` specified, the generated dim names will be: + * `generateCoord` + 0, `generateCoord` + 1, ... + * can be Infinity, indicate that use all of the remain columns. + * @param {number} [opt.dimCount] If not specified, guess by the first data item. + * @param {number} [opt.encodeDefaulter] If not specified, auto find the next available data dim. + * @return {Array.} [{ + * name: string mandatory, + * displayName: string, the origin name in dimsDef, see source helper. + * If displayName given, the tooltip will displayed vertically. + * coordDim: string mandatory, + * coordDimIndex: number mandatory, + * type: string optional, + * otherDims: { never null/undefined + * tooltip: number optional, + * label: number optional, + * itemName: number optional, + * seriesName: number optional, + * }, + * isExtraCoord: boolean true if coord is generated + * (not specified in encode and not series specified) + * other props ... + * }] + */ +function completeDimensions(sysDims, source, opt) { + if (!Source.isInstance(source)) { + source = Source.seriesDataToSource(source); + } + + opt = opt || {}; + sysDims = (sysDims || []).slice(); + var dimsDef = (opt.dimsDef || []).slice(); + var encodeDef = createHashMap(opt.encodeDef); + var dataDimNameMap = createHashMap(); + var coordDimNameMap = createHashMap(); + // var valueCandidate; + var result = []; + + var dimCount = getDimCount(source, sysDims, dimsDef, opt.dimCount); + + // Apply user defined dims (`name` and `type`) and init result. + for (var i = 0; i < dimCount; i++) { + var dimDefItem = dimsDef[i] = extend( + {}, isObject$1(dimsDef[i]) ? dimsDef[i] : {name: dimsDef[i]} + ); + var userDimName = dimDefItem.name; + var resultItem = result[i] = {otherDims: {}}; + // Name will be applied later for avoiding duplication. + if (userDimName != null && dataDimNameMap.get(userDimName) == null) { + // Only if `series.dimensions` is defined in option + // displayName, will be set, and dimension will be diplayed vertically in + // tooltip by default. + resultItem.name = resultItem.displayName = userDimName; + dataDimNameMap.set(userDimName, i); + } + dimDefItem.type != null && (resultItem.type = dimDefItem.type); + dimDefItem.displayName != null && (resultItem.displayName = dimDefItem.displayName); + } + + // Set `coordDim` and `coordDimIndex` by `encodeDef` and normalize `encodeDef`. + encodeDef.each(function (dataDims, coordDim) { + dataDims = normalizeToArray(dataDims).slice(); + var validDataDims = encodeDef.set(coordDim, []); + each$1(dataDims, function (resultDimIdx, idx) { + // The input resultDimIdx can be dim name or index. + isString(resultDimIdx) && (resultDimIdx = dataDimNameMap.get(resultDimIdx)); + if (resultDimIdx != null && resultDimIdx < dimCount) { + validDataDims[idx] = resultDimIdx; + applyDim(result[resultDimIdx], coordDim, idx); + } + }); + }); + + // Apply templetes and default order from `sysDims`. + var availDimIdx = 0; + each$1(sysDims, function (sysDimItem, sysDimIndex) { + var coordDim; + var sysDimItem; + var sysDimItemDimsDef; + var sysDimItemOtherDims; + if (isString(sysDimItem)) { + coordDim = sysDimItem; + sysDimItem = {}; + } + else { + coordDim = sysDimItem.name; + var ordinalMeta = sysDimItem.ordinalMeta; + sysDimItem.ordinalMeta = null; + sysDimItem = clone(sysDimItem); + sysDimItem.ordinalMeta = ordinalMeta; + // `coordDimIndex` should not be set directly. + sysDimItemDimsDef = sysDimItem.dimsDef; + sysDimItemOtherDims = sysDimItem.otherDims; + sysDimItem.name = sysDimItem.coordDim = sysDimItem.coordDimIndex + = sysDimItem.dimsDef = sysDimItem.otherDims = null; + } + + var dataDims = normalizeToArray(encodeDef.get(coordDim)); + // dimensions provides default dim sequences. + if (!dataDims.length) { + for (var i = 0; i < (sysDimItemDimsDef && sysDimItemDimsDef.length || 1); i++) { + while (availDimIdx < result.length && result[availDimIdx].coordDim != null) { + availDimIdx++; + } + availDimIdx < result.length && dataDims.push(availDimIdx++); + } + } + + // Apply templates. + each$1(dataDims, function (resultDimIdx, coordDimIndex) { + var resultItem = result[resultDimIdx]; + applyDim(defaults(resultItem, sysDimItem), coordDim, coordDimIndex); + if (resultItem.name == null && sysDimItemDimsDef) { + var sysDimItemDimsDefItem = sysDimItemDimsDef[coordDimIndex]; + !isObject$1(sysDimItemDimsDefItem) && (sysDimItemDimsDefItem = {name: sysDimItemDimsDefItem}); + resultItem.name = resultItem.displayName = sysDimItemDimsDefItem.name; + resultItem.defaultTooltip = sysDimItemDimsDefItem.defaultTooltip; + } + // FIXME refactor, currently only used in case: {otherDims: {tooltip: false}} + sysDimItemOtherDims && defaults(resultItem.otherDims, sysDimItemOtherDims); + }); + }); + + function applyDim(resultItem, coordDim, coordDimIndex) { + if (OTHER_DIMENSIONS.get(coordDim) != null) { + resultItem.otherDims[coordDim] = coordDimIndex; + } + else { + resultItem.coordDim = coordDim; + resultItem.coordDimIndex = coordDimIndex; + coordDimNameMap.set(coordDim, true); + } + } + + // Make sure the first extra dim is 'value'. + var generateCoord = opt.generateCoord; + var generateCoordCount = opt.generateCoordCount; + var fromZero = generateCoordCount != null; + generateCoordCount = generateCoord ? (generateCoordCount || 1) : 0; + var extra = generateCoord || 'value'; + + // Set dim `name` and other `coordDim` and other props. + for (var resultDimIdx = 0; resultDimIdx < dimCount; resultDimIdx++) { + var resultItem = result[resultDimIdx] = result[resultDimIdx] || {}; + var coordDim = resultItem.coordDim; + + if (coordDim == null) { + resultItem.coordDim = genName( + extra, coordDimNameMap, fromZero + ); + resultItem.coordDimIndex = 0; + if (!generateCoord || generateCoordCount <= 0) { + resultItem.isExtraCoord = true; + } + generateCoordCount--; + } + + resultItem.name == null && (resultItem.name = genName( + resultItem.coordDim, + dataDimNameMap + )); + + if (resultItem.type == null && guessOrdinal(source, resultDimIdx, resultItem.name)) { + resultItem.type = 'ordinal'; + } + } + + return result; +} + +// ??? TODO +// Originally detect dimCount by data[0]. Should we +// optimize it to only by sysDims and dimensions and encode. +// So only necessary dims will be initialized. +// But +// (1) custom series should be considered. where other dims +// may be visited. +// (2) sometimes user need to calcualte bubble size or use visualMap +// on other dimensions besides coordSys needed. +// So, dims that is not used by system, should be shared in storage? +function getDimCount(source, sysDims, dimsDef, optDimCount) { + // Note that the result dimCount should not small than columns count + // of data, otherwise `dataDimNameMap` checking will be incorrect. + var dimCount = Math.max( + source.dimensionsDetectCount || 1, + sysDims.length, + dimsDef.length, + optDimCount || 0 + ); + each$1(sysDims, function (sysDimItem) { + var sysDimItemDimsDef = sysDimItem.dimsDef; + sysDimItemDimsDef && (dimCount = Math.max(dimCount, sysDimItemDimsDef.length)); + }); + return dimCount; +} + +function genName(name, map$$1, fromZero) { + if (fromZero || map$$1.get(name) != null) { + var i = 0; + while (map$$1.get(name + i) != null) { + i++; + } + name += i; + } + map$$1.set(name, true); + return name; +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * Substitute `completeDimensions`. + * `completeDimensions` is to be deprecated. + */ +/** + * @param {module:echarts/data/Source|module:echarts/data/List} source or data. + * @param {Object|Array} [opt] + * @param {Array.} [opt.coordDimensions=[]] + * @param {number} [opt.dimensionsCount] + * @param {string} [opt.generateCoord] + * @param {string} [opt.generateCoordCount] + * @param {Array.} [opt.dimensionsDefine=source.dimensionsDefine] Overwrite source define. + * @param {Object|HashMap} [opt.encodeDefine=source.encodeDefine] Overwrite source define. + * @return {Array.} dimensionsInfo + */ +var createDimensions = function (source, opt) { + opt = opt || {}; + return completeDimensions(opt.coordDimensions || [], source, { + dimsDef: opt.dimensionsDefine || source.dimensionsDefine, + encodeDef: opt.encodeDefine || source.encodeDefine, + dimCount: opt.dimensionsCount, + generateCoord: opt.generateCoord, + generateCoordCount: opt.generateCoordCount + }); +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * Note that it is too complicated to support 3d stack by value + * (have to create two-dimension inverted index), so in 3d case + * we just support that stacked by index. + * + * @param {module:echarts/model/Series} seriesModel + * @param {Array.} dimensionInfoList The same as the input of . + * The input dimensionInfoList will be modified. + * @param {Object} [opt] + * @param {boolean} [opt.stackedCoordDimension=''] Specify a coord dimension if needed. + * @param {boolean} [opt.byIndex=false] + * @return {Object} calculationInfo + * { + * stackedDimension: string + * stackedByDimension: string + * isStackedByIndex: boolean + * stackedOverDimension: string + * stackResultDimension: string + * } + */ +function enableDataStack(seriesModel, dimensionInfoList, opt) { + opt = opt || {}; + var byIndex = opt.byIndex; + var stackedCoordDimension = opt.stackedCoordDimension; + + // Compatibal: when `stack` is set as '', do not stack. + var mayStack = !!(seriesModel && seriesModel.get('stack')); + var stackedByDimInfo; + var stackedDimInfo; + var stackResultDimension; + var stackedOverDimension; + + each$1(dimensionInfoList, function (dimensionInfo, index) { + if (isString(dimensionInfo)) { + dimensionInfoList[index] = dimensionInfo = {name: dimensionInfo}; + } + + if (mayStack && !dimensionInfo.isExtraCoord) { + // Find the first ordinal dimension as the stackedByDimInfo. + if (!byIndex && !stackedByDimInfo && dimensionInfo.ordinalMeta) { + stackedByDimInfo = dimensionInfo; + } + // Find the first stackable dimension as the stackedDimInfo. + if (!stackedDimInfo + && dimensionInfo.type !== 'ordinal' + && dimensionInfo.type !== 'time' + && (!stackedCoordDimension || stackedCoordDimension === dimensionInfo.coordDim) + ) { + stackedDimInfo = dimensionInfo; + } + } + }); + + if (stackedDimInfo && !byIndex && !stackedByDimInfo) { + // Compatible with previous design, value axis (time axis) only stack by index. + // It may make sense if the user provides elaborately constructed data. + byIndex = true; + } + + // Add stack dimension, they can be both calculated by coordinate system in `unionExtent`. + // That put stack logic in List is for using conveniently in echarts extensions, but it + // might not be a good way. + if (stackedDimInfo) { + // Use a weird name that not duplicated with other names. + stackResultDimension = '__\0ecstackresult'; + stackedOverDimension = '__\0ecstackedover'; + + // Create inverted index to fast query index by value. + if (stackedByDimInfo) { + stackedByDimInfo.createInvertedIndices = true; + } + + var stackedDimCoordDim = stackedDimInfo.coordDim; + var stackedDimType = stackedDimInfo.type; + var stackedDimCoordIndex = 0; + + each$1(dimensionInfoList, function (dimensionInfo) { + if (dimensionInfo.coordDim === stackedDimCoordDim) { + stackedDimCoordIndex++; + } + }); + + dimensionInfoList.push({ + name: stackResultDimension, + coordDim: stackedDimCoordDim, + coordDimIndex: stackedDimCoordIndex, + type: stackedDimType, + isExtraCoord: true, + isCalculationCoord: true + }); + + stackedDimCoordIndex++; + + dimensionInfoList.push({ + name: stackedOverDimension, + // This dimension contains stack base (generally, 0), so do not set it as + // `stackedDimCoordDim` to avoid extent calculation, consider log scale. + coordDim: stackedOverDimension, + coordDimIndex: stackedDimCoordIndex, + type: stackedDimType, + isExtraCoord: true, + isCalculationCoord: true + }); + } + + return { + stackedDimension: stackedDimInfo && stackedDimInfo.name, + stackedByDimension: stackedByDimInfo && stackedByDimInfo.name, + isStackedByIndex: byIndex, + stackedOverDimension: stackedOverDimension, + stackResultDimension: stackResultDimension + }; +} + +/** + * @param {module:echarts/data/List} data + * @param {string} stackedDim + */ +function isDimensionStacked(data, stackedDim /*, stackedByDim*/) { + // Each single series only maps to one pair of axis. So we do not need to + // check stackByDim, whatever stacked by a dimension or stacked by index. + return !!stackedDim && stackedDim === data.getCalculationInfo('stackedDimension'); + // && ( + // stackedByDim != null + // ? stackedByDim === data.getCalculationInfo('stackedByDimension') + // : data.getCalculationInfo('isStackedByIndex') + // ); +} + +/** + * @param {module:echarts/data/List} data + * @param {string} targetDim + * @param {string} [stackedByDim] If not input this parameter, check whether + * stacked by index. + * @return {string} dimension + */ +function getStackedDimension(data, targetDim) { + return isDimensionStacked(data, targetDim) + ? data.getCalculationInfo('stackResultDimension') + : targetDim; +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * @param {module:echarts/data/Source|Array} source Or raw data. + * @param {module:echarts/model/Series} seriesModel + * @param {Object} [opt] + * @param {string} [opt.generateCoord] + */ +function createListFromArray(source, seriesModel, opt) { + opt = opt || {}; + + if (!Source.isInstance(source)) { + source = Source.seriesDataToSource(source); + } + + var coordSysName = seriesModel.get('coordinateSystem'); + var registeredCoordSys = CoordinateSystemManager.get(coordSysName); + + var coordSysDefine = getCoordSysDefineBySeries(seriesModel); + + var coordSysDimDefs; + + if (coordSysDefine) { + coordSysDimDefs = map(coordSysDefine.coordSysDims, function (dim) { + var dimInfo = {name: dim}; + var axisModel = coordSysDefine.axisMap.get(dim); + if (axisModel) { + var axisType = axisModel.get('type'); + dimInfo.type = getDimensionTypeByAxis(axisType); + // dimInfo.stackable = isStackable(axisType); + } + return dimInfo; + }); + } + + if (!coordSysDimDefs) { + // Get dimensions from registered coordinate system + coordSysDimDefs = (registeredCoordSys && ( + registeredCoordSys.getDimensionsInfo + ? registeredCoordSys.getDimensionsInfo() + : registeredCoordSys.dimensions.slice() + )) || ['x', 'y']; + } + + var dimInfoList = createDimensions(source, { + coordDimensions: coordSysDimDefs, + generateCoord: opt.generateCoord + }); + + var firstCategoryDimIndex; + var hasNameEncode; + coordSysDefine && each$1(dimInfoList, function (dimInfo, dimIndex) { + var coordDim = dimInfo.coordDim; + var categoryAxisModel = coordSysDefine.categoryAxisMap.get(coordDim); + if (categoryAxisModel) { + if (firstCategoryDimIndex == null) { + firstCategoryDimIndex = dimIndex; + } + dimInfo.ordinalMeta = categoryAxisModel.getOrdinalMeta(); + } + if (dimInfo.otherDims.itemName != null) { + hasNameEncode = true; + } + }); + if (!hasNameEncode && firstCategoryDimIndex != null) { + dimInfoList[firstCategoryDimIndex].otherDims.itemName = 0; + } + + var stackCalculationInfo = enableDataStack(seriesModel, dimInfoList); + + var list = new List(dimInfoList, seriesModel); + + list.setCalculationInfo(stackCalculationInfo); + + var dimValueGetter = (firstCategoryDimIndex != null && isNeedCompleteOrdinalData(source)) + ? function (itemOpt, dimName, dataIndex, dimIndex) { + // Use dataIndex as ordinal value in categoryAxis + return dimIndex === firstCategoryDimIndex + ? dataIndex + : this.defaultDimValueGetter(itemOpt, dimName, dataIndex, dimIndex); + } + : null; + + list.hasItemOption = false; + list.initData(source, null, dimValueGetter); + + return list; +} + +function isNeedCompleteOrdinalData(source) { + if (source.sourceFormat === SOURCE_FORMAT_ORIGINAL) { + var sampleItem = firstDataNotNull(source.data || []); + return sampleItem != null + && !isArray(getDataItemValue(sampleItem)); + } +} + +function firstDataNotNull(data) { + var i = 0; + while (i < data.length && data[i] == null) { + i++; + } + return data[i]; +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * // Scale class management + * @module echarts/scale/Scale + */ + +/** + * @param {Object} [setting] + */ +function Scale(setting) { + this._setting = setting || {}; + + /** + * 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); +} + +/** + * Parse input val to valid inner number. + * @param {*} val + * @return {number} + */ +Scale.prototype.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; +}; + +Scale.prototype.getSetting = function (name) { + return this._setting[name]; +}; + +Scale.prototype.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} + */ +Scale.prototype.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} + */ +Scale.prototype.scale = function (val) { + var extent = this._extent; + return val * (extent[1] - extent[0]) + extent[0]; +}; + +/** + * Set extent from data + * @param {Array.} other + */ +Scale.prototype.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]); +}; + +/** + * Set extent from data + * @param {module:echarts/data/List} data + * @param {string} dim + */ +Scale.prototype.unionExtentFromData = function (data, dim) { + this.unionExtent(data.getApproximateExtent(dim)); +}; + +/** + * Get extent + * @return {Array.} + */ +Scale.prototype.getExtent = function () { + return this._extent.slice(); +}; + +/** + * Set extent + * @param {number} start + * @param {number} end + */ +Scale.prototype.setExtent = function (start, end) { + var thisExtent = this._extent; + if (!isNaN(start)) { + thisExtent[0] = start; + } + if (!isNaN(end)) { + thisExtent[1] = end; + } +}; + +/** + * When axis extent depends on data and no data exists, + * axis ticks should not be drawn, which is named 'blank'. + */ +Scale.prototype.isBlank = function () { + return this._isBlank; +}, + +/** + * When axis extent depends on data and no data exists, + * axis ticks should not be drawn, which is named 'blank'. + */ +Scale.prototype.setBlank = function (isBlank) { + this._isBlank = isBlank; +}; + +/** + * @abstract + * @param {*} tick + * @return {string} label of the tick. + */ +Scale.prototype.getLabel = null; + + +enableClassExtend(Scale); +enableClassManagement(Scale, { + registerWhenExtend: true +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * @constructor + * @param {Object} [opt] + * @param {Object} [opt.categories=[]] + * @param {Object} [opt.needCollect=false] + * @param {Object} [opt.deduplication=false] + */ +function OrdinalMeta(opt) { + + /** + * @readOnly + * @type {Array.} + */ + this.categories = opt.categories || []; + + /** + * @private + * @type {boolean} + */ + this._needCollect = opt.needCollect; + + /** + * @private + * @type {boolean} + */ + this._deduplication = opt.deduplication; + + /** + * @private + * @type {boolean} + */ + this._map; +} + +/** + * @param {module:echarts/model/Model} axisModel + * @return {module:echarts/data/OrdinalMeta} + */ +OrdinalMeta.createByAxisModel = function (axisModel) { + var option = axisModel.option; + var data = option.data; + var categories = data && map(data, getName); + + return new OrdinalMeta({ + categories: categories, + needCollect: !categories, + // deduplication is default in axis. + deduplication: option.dedplication !== false + }); +}; + +var proto$1 = OrdinalMeta.prototype; + +/** + * @param {string} category + * @return {number} ordinal + */ +proto$1.getOrdinal = function (category) { + return getOrCreateMap(this).get(category); +}; + +/** + * @param {*} category + * @return {number} The ordinal. If not found, return NaN. + */ +proto$1.parseAndCollect = function (category) { + var index; + var needCollect = this._needCollect; + + // The value of category dim can be the index of the given category set. + // This feature is only supported when !needCollect, because we should + // consider a common case: a value is 2017, which is a number but is + // expected to be tread as a category. This case usually happen in dataset, + // where it happent to be no need of the index feature. + if (typeof category !== 'string' && !needCollect) { + return category; + } + + // Optimize for the scenario: + // category is ['2012-01-01', '2012-01-02', ...], where the input + // data has been ensured not duplicate and is large data. + // Notice, if a dataset dimension provide categroies, usually echarts + // should remove duplication except user tell echarts dont do that + // (set axis.deduplication = false), because echarts do not know whether + // the values in the category dimension has duplication (consider the + // parallel-aqi example) + if (needCollect && !this._deduplication) { + index = this.categories.length; + this.categories[index] = category; + return index; + } + + var map$$1 = getOrCreateMap(this); + index = map$$1.get(category); + + if (index == null) { + if (needCollect) { + index = this.categories.length; + this.categories[index] = category; + map$$1.set(category, index); + } + else { + index = NaN; + } + } + + return index; +}; + +// Consider big data, do not create map until needed. +function getOrCreateMap(ordinalMeta) { + return ordinalMeta._map || ( + ordinalMeta._map = createHashMap(ordinalMeta.categories) + ); +} + +function getName(obj) { + if (isObject$1(obj) && obj.value != null) { + return obj.value; + } + else { + return obj + ''; + } +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * Linear continuous scale + * @module echarts/coord/scale/Ordinal + * + * http://en.wikipedia.org/wiki/Level_of_measurement + */ + +// FIXME only one data + +var scaleProto = Scale.prototype; + +var OrdinalScale = Scale.extend({ + + type: 'ordinal', + + /** + * @param {module:echarts/data/OrdianlMeta|Array.} ordinalMeta + */ + init: function (ordinalMeta, extent) { + // Caution: Should not use instanceof, consider ec-extensions using + // import approach to get OrdinalMeta class. + if (!ordinalMeta || isArray(ordinalMeta)) { + ordinalMeta = new OrdinalMeta({categories: ordinalMeta}); + } + this._ordinalMeta = ordinalMeta; + this._extent = extent || [0, ordinalMeta.categories.length - 1]; + }, + + parse: function (val) { + return typeof val === 'string' + ? this._ordinalMeta.getOrdinal(val) + // val might be float. + : Math.round(val); + }, + + contain: function (rank) { + rank = this.parse(rank); + return scaleProto.contain.call(this, rank) + && this._ordinalMeta.categories[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) { + if (!this.isBlank()) { + // Note that if no data, ordinalMeta.categories is an empty array. + return this._ordinalMeta.categories[n]; + } + }, + + /** + * @return {number} + */ + count: function () { + return this._extent[1] - this._extent[0] + 1; + }, + + /** + * @override + */ + unionExtentFromData: function (data, dim) { + this.unionExtent(data.getApproximateExtent(dim)); + }, + + getOrdinalMeta: function () { + return this._ordinalMeta; + }, + + niceTicks: noop, + niceExtent: noop +}); + +/** + * @return {module:echarts/scale/Time} + */ +OrdinalScale.create = function () { + return new OrdinalScale(); +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * For testable. + */ + +var roundNumber$1 = round$1; + +/** + * @param {Array.} extent Both extent[0] and extent[1] should be valid number. + * Should be extent[0] < extent[1]. + * @param {number} splitNumber splitNumber should be >= 1. + * @param {number} [minInterval] + * @param {number} [maxInterval] + * @return {Object} {interval, intervalPrecision, niceTickExtent} + */ +function intervalScaleNiceTicks(extent, splitNumber, minInterval, maxInterval) { + var result = {}; + var span = extent[1] - extent[0]; + + var interval = result.interval = nice(span / splitNumber, true); + if (minInterval != null && interval < minInterval) { + interval = result.interval = minInterval; + } + if (maxInterval != null && interval > maxInterval) { + interval = result.interval = maxInterval; + } + // Tow more digital for tick. + var precision = result.intervalPrecision = getIntervalPrecision(interval); + // Niced extent inside original extent + var niceTickExtent = result.niceTickExtent = [ + roundNumber$1(Math.ceil(extent[0] / interval) * interval, precision), + roundNumber$1(Math.floor(extent[1] / interval) * interval, precision) + ]; + + fixExtent(niceTickExtent, extent); + + return result; +} + +/** + * @param {number} interval + * @return {number} interval precision + */ +function getIntervalPrecision(interval) { + // Tow more digital for tick. + return getPrecisionSafe(interval) + 2; +} + +function clamp(niceTickExtent, idx, extent) { + niceTickExtent[idx] = Math.max(Math.min(niceTickExtent[idx], extent[1]), extent[0]); +} + +// In some cases (e.g., splitNumber is 1), niceTickExtent may be out of extent. +function fixExtent(niceTickExtent, extent) { + !isFinite(niceTickExtent[0]) && (niceTickExtent[0] = extent[0]); + !isFinite(niceTickExtent[1]) && (niceTickExtent[1] = extent[1]); + clamp(niceTickExtent, 0, extent); + clamp(niceTickExtent, 1, extent); + if (niceTickExtent[0] > niceTickExtent[1]) { + niceTickExtent[0] = niceTickExtent[1]; + } +} + +function intervalScaleGetTicks(interval, extent, niceTickExtent, intervalPrecision) { + var ticks = []; + + // If interval is 0, return []; + if (!interval) { + return ticks; + } + + // Consider this case: using dataZoom toolbox, zoom and zoom. + var safeLimit = 10000; + + if (extent[0] < niceTickExtent[0]) { + ticks.push(extent[0]); + } + var tick = niceTickExtent[0]; + + while (tick <= niceTickExtent[1]) { + ticks.push(tick); + // Avoid rounding error + tick = roundNumber$1(tick + interval, intervalPrecision); + if (tick === ticks[ticks.length - 1]) { + // Consider out of safe float point, e.g., + // -3711126.9907707 + 2e-10 === -3711126.9907707 + break; + } + if (ticks.length > safeLimit) { + return []; + } + } + // Consider this case: the last item of ticks is smaller + // than niceTickExtent[1] and niceTickExtent[1] === extent[1]. + if (extent[1] > (ticks.length ? ticks[ticks.length - 1] : niceTickExtent[1])) { + ticks.push(extent[1]); + } + + return ticks; +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * Interval scale + * @module echarts/scale/Interval + */ + + +var roundNumber = round$1; + +/** + * @alias module:echarts/coord/scale/Interval + * @constructor + */ +var IntervalScale = Scale.extend({ + + type: 'interval', + + _interval: 0, + + _intervalPrecision: 2, + + 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 () { + 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(); + + this._intervalPrecision = getIntervalPrecision(interval); + }, + + /** + * @return {Array.} + */ + getTicks: function () { + return intervalScaleGetTicks( + this._interval, this._extent, this._niceExtent, this._intervalPrecision + ); + }, + + /** + * @param {number} data + * @param {Object} [opt] + * @param {number|string} [opt.precision] If 'auto', use nice presision. + * @param {boolean} [opt.pad] returns 1.50 but not 1.5 if precision is 2. + * @return {string} + */ + getLabel: function (data, opt) { + if (data == null) { + return ''; + } + + var precision = opt && opt.precision; + + if (precision == null) { + precision = getPrecisionSafe(data) || 0; + } + else if (precision === 'auto') { + // Should be more precise then tick. + precision = this._intervalPrecision; + } + + // (1) If `precision` is set, 12.005 should be display as '12.00500'. + // (2) Use roundNumber (toFixed) to avoid scientific notation like '3.5e-7'. + data = roundNumber(data, precision, true); + + return addCommas(data); + }, + + /** + * Update interval and extent of intervals for nice ticks + * + * @param {number} [splitNumber = 5] Desired number of ticks + * @param {number} [minInterval] + * @param {number} [maxInterval] + */ + niceTicks: function (splitNumber, minInterval, maxInterval) { + 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(); + } + + var result = intervalScaleNiceTicks( + extent, splitNumber, minInterval, maxInterval + ); + + this._intervalPrecision = result.intervalPrecision; + this._interval = result.interval; + this._niceExtent = result.niceTickExtent; + }, + + /** + * Nice extent. + * @param {Object} opt + * @param {number} [opt.splitNumber = 5] Given approx tick number + * @param {boolean} [opt.fixMin=false] + * @param {boolean} [opt.fixMax=false] + * @param {boolean} [opt.minInterval] + * @param {boolean} [opt.maxInterval] + */ + niceExtent: function (opt) { + 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 (!opt.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(opt.splitNumber, opt.minInterval, opt.maxInterval); + + // var extent = this._extent; + var interval = this._interval; + + if (!opt.fixMin) { + extent[0] = roundNumber(Math.floor(extent[0] / interval) * interval); + } + if (!opt.fixMax) { + extent[1] = roundNumber(Math.ceil(extent[1] / interval) * interval); + } + } +}); + +/** + * @return {module:echarts/scale/Time} + */ +IntervalScale.create = function () { + return new IntervalScale(); +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var STACK_PREFIX = '__ec_stack_'; +var LARGE_BAR_MIN_WIDTH = 0.5; + +var LargeArr = typeof Float32Array !== 'undefined' ? Float32Array : Array; + +function getSeriesStackId(seriesModel) { + return seriesModel.get('stack') || STACK_PREFIX + seriesModel.seriesIndex; +} + +function getAxisKey(axis) { + return axis.dim + axis.index; +} + +/** + * @param {Object} opt + * @param {module:echarts/coord/Axis} opt.axis Only support category axis currently. + * @param {number} opt.count Positive interger. + * @param {number} [opt.barWidth] + * @param {number} [opt.barMaxWidth] + * @param {number} [opt.barGap] + * @param {number} [opt.barCategoryGap] + * @return {Object} {width, offset, offsetCenter} If axis.type is not 'category', return undefined. + */ +function getLayoutOnAxis(opt) { + var params = []; + var baseAxis = opt.axis; + var axisKey = 'axis0'; + + if (baseAxis.type !== 'category') { + return; + } + var bandWidth = baseAxis.getBandWidth(); + + for (var i = 0; i < opt.count || 0; i++) { + params.push(defaults({ + bandWidth: bandWidth, + axisKey: axisKey, + stackId: STACK_PREFIX + i + }, opt)); + } + var widthAndOffsets = doCalBarWidthAndOffset(params); + + var result = []; + for (var i = 0; i < opt.count; i++) { + var item = widthAndOffsets[axisKey][STACK_PREFIX + i]; + item.offsetCenter = item.offset + item.width / 2; + result.push(item); + } + + return result; +} + +function prepareLayoutBarSeries(seriesType, ecModel) { + var seriesModels = []; + ecModel.eachSeriesByType(seriesType, function (seriesModel) { + // Check series coordinate, do layout for cartesian2d only + if (isOnCartesian(seriesModel) && !isInLargeMode(seriesModel)) { + seriesModels.push(seriesModel); + } + }); + return seriesModels; +} + +function makeColumnLayout(barSeries) { + var seriesInfoList = []; + each$1(barSeries, function (seriesModel) { + 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 barWidth = parsePercent$1( + seriesModel.get('barWidth'), bandWidth + ); + var barMaxWidth = parsePercent$1( + seriesModel.get('barMaxWidth'), bandWidth + ); + var barGap = seriesModel.get('barGap'); + var barCategoryGap = seriesModel.get('barCategoryGap'); + + seriesInfoList.push({ + bandWidth: bandWidth, + barWidth: barWidth, + barMaxWidth: barMaxWidth, + barGap: barGap, + barCategoryGap: barCategoryGap, + axisKey: getAxisKey(baseAxis), + stackId: getSeriesStackId(seriesModel) + }); + }); + + return doCalBarWidthAndOffset(seriesInfoList); +} + +function doCalBarWidthAndOffset(seriesInfoList) { + // Columns info on each category axis. Key is cartesian name + var columnsMap = {}; + + each$1(seriesInfoList, function (seriesInfo, idx) { + var axisKey = seriesInfo.axisKey; + var bandWidth = seriesInfo.bandWidth; + var columnsOnAxis = columnsMap[axisKey] || { + bandWidth: bandWidth, + remainedWidth: bandWidth, + autoWidthCount: 0, + categoryGap: '20%', + gap: '30%', + stacks: {} + }; + var stacks = columnsOnAxis.stacks; + columnsMap[axisKey] = columnsOnAxis; + + var stackId = seriesInfo.stackId; + + if (!stacks[stackId]) { + columnsOnAxis.autoWidthCount++; + } + stacks[stackId] = stacks[stackId] || { + width: 0, + maxWidth: 0 + }; + + // Caution: In a single coordinate system, these barGrid attributes + // will be shared by series. Consider that they have default values, + // only the attributes set on the last series will work. + // Do not change this fact unless there will be a break change. + + // TODO + var barWidth = seriesInfo.barWidth; + if (barWidth && !stacks[stackId].width) { + // See #6312, do not restrict width. + stacks[stackId].width = barWidth; + barWidth = Math.min(columnsOnAxis.remainedWidth, barWidth); + columnsOnAxis.remainedWidth -= barWidth; + } + + var barMaxWidth = seriesInfo.barMaxWidth; + barMaxWidth && (stacks[stackId].maxWidth = barMaxWidth); + var barGap = seriesInfo.barGap; + (barGap != null) && (columnsOnAxis.gap = barGap); + var barCategoryGap = seriesInfo.barCategoryGap; + (barCategoryGap != null) && (columnsOnAxis.categoryGap = barCategoryGap); + }); + + var result = {}; + + each$1(columnsMap, function (columnsOnAxis, coordSysName) { + + result[coordSysName] = {}; + + var stacks = columnsOnAxis.stacks; + var bandWidth = columnsOnAxis.bandWidth; + var categoryGap = parsePercent$1(columnsOnAxis.categoryGap, bandWidth); + var barGapPercent = parsePercent$1(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 + each$1(stacks, function (column, stack) { + var maxWidth = column.maxWidth; + if (maxWidth && maxWidth < autoWidth) { + maxWidth = Math.min(maxWidth, remainedWidth); + if (column.width) { + maxWidth = Math.min(maxWidth, column.width); + } + 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; + each$1(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; + each$1(stacks, function (column, stackId) { + result[coordSysName][stackId] = result[coordSysName][stackId] || { + offset: offset, + width: column.width + }; + + offset += column.width * (1 + barGapPercent); + }); + }); + + return result; +} + +/** + * @param {Object} barWidthAndOffset The result of makeColumnLayout + * @param {module:echarts/coord/Axis} axis + * @param {module:echarts/model/Series} [seriesModel] If not provided, return all. + * @return {Object} {stackId: {offset, width}} or {offset, width} if seriesModel provided. + */ +function retrieveColumnLayout(barWidthAndOffset, axis, seriesModel) { + if (barWidthAndOffset && axis) { + var result = barWidthAndOffset[getAxisKey(axis)]; + if (result != null && seriesModel != null) { + result = result[getSeriesStackId(seriesModel)]; + } + return result; + } +} + +/** + * @param {string} seriesType + * @param {module:echarts/model/Global} ecModel + */ +function layout(seriesType, ecModel) { + + var seriesModels = prepareLayoutBarSeries(seriesType, ecModel); + var barWidthAndOffset = makeColumnLayout(seriesModels); + + var lastStackCoords = {}; + each$1(seriesModels, 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; + + lastStackCoords[stackId] = lastStackCoords[stackId] || []; + data.setLayout({ + offset: columnOffset, + size: columnWidth + }); + + var valueDim = data.mapDimension(valueAxis.dim); + var baseDim = data.mapDimension(baseAxis.dim); + var stacked = isDimensionStacked(data, valueDim /*, baseDim*/); + var isValueAxisH = valueAxis.isHorizontal(); + + var valueAxisStart = getValueAxisStart(baseAxis, valueAxis, stacked); + + for (var idx = 0, len = data.count(); idx < len; idx++) { + var value = data.get(valueDim, idx); + var baseValue = data.get(baseDim, idx); + + if (isNaN(value)) { + continue; + } + + var sign = value >= 0 ? 'p' : 'n'; + var baseCoord = valueAxisStart; + + // Because of the barMinHeight, we can not use the value in + // stackResultDimension directly. + if (stacked) { + // Only ordinal axis can be stacked. + if (!lastStackCoords[stackId][baseValue]) { + lastStackCoords[stackId][baseValue] = { + p: valueAxisStart, // Positive stack + n: valueAxisStart // Negative stack + }; + } + // Should also consider #4243 + baseCoord = lastStackCoords[stackId][baseValue][sign]; + } + + var x; + var y; + var width; + var height; + + if (isValueAxisH) { + var coord = cartesian.dataToPoint([value, baseValue]); + x = baseCoord; + y = coord[1] + columnOffset; + width = coord[0] - valueAxisStart; + height = columnWidth; + + if (Math.abs(width) < barMinHeight) { + width = (width < 0 ? -1 : 1) * barMinHeight; + } + stacked && (lastStackCoords[stackId][baseValue][sign] += width); + } + else { + var coord = cartesian.dataToPoint([baseValue, value]); + x = coord[0] + columnOffset; + y = baseCoord; + width = columnWidth; + height = coord[1] - valueAxisStart; + + if (Math.abs(height) < barMinHeight) { + // Include zero to has a positive bar + height = (height <= 0 ? -1 : 1) * barMinHeight; + } + stacked && (lastStackCoords[stackId][baseValue][sign] += height); + } + + data.setItemLayout(idx, { + x: x, + y: y, + width: width, + height: height + }); + } + + }, this); +} + +// TODO: Do not support stack in large mode yet. +var largeLayout = { + + seriesType: 'bar', + + plan: createRenderPlanner(), + + reset: function (seriesModel) { + if (!isOnCartesian(seriesModel) || !isInLargeMode(seriesModel)) { + return; + } + + var data = seriesModel.getData(); + var cartesian = seriesModel.coordinateSystem; + var baseAxis = cartesian.getBaseAxis(); + var valueAxis = cartesian.getOtherAxis(baseAxis); + var valueDim = data.mapDimension(valueAxis.dim); + var baseDim = data.mapDimension(baseAxis.dim); + var valueAxisHorizontal = valueAxis.isHorizontal(); + var valueDimIdx = valueAxisHorizontal ? 0 : 1; + + var barWidth = retrieveColumnLayout( + makeColumnLayout([seriesModel]), baseAxis, seriesModel + ).width; + if (!(barWidth > LARGE_BAR_MIN_WIDTH)) { // jshint ignore:line + barWidth = LARGE_BAR_MIN_WIDTH; + } + + return {progress: progress}; + + function progress(params, data) { + var largePoints = new LargeArr(params.count * 2); + var dataIndex; + var coord = []; + var valuePair = []; + var offset = 0; + + while ((dataIndex = params.next()) != null) { + valuePair[valueDimIdx] = data.get(valueDim, dataIndex); + valuePair[1 - valueDimIdx] = data.get(baseDim, dataIndex); + + coord = cartesian.dataToPoint(valuePair, null, coord); + largePoints[offset++] = coord[0]; + largePoints[offset++] = coord[1]; + } + + data.setLayout({ + largePoints: largePoints, + barWidth: barWidth, + valueAxisStart: getValueAxisStart(baseAxis, valueAxis, false), + valueAxisHorizontal: valueAxisHorizontal + }); + } + } +}; + +function isOnCartesian(seriesModel) { + return seriesModel.coordinateSystem && seriesModel.coordinateSystem.type === 'cartesian2d'; +} + +function isInLargeMode(seriesModel) { + return seriesModel.pipelineContext && seriesModel.pipelineContext.large; +} + +function getValueAxisStart(baseAxis, valueAxis, stacked) { + return ( + indexOf(baseAxis.getAxesOnZeroOf(), valueAxis) >= 0 + || stacked + ) + ? valueAxis.toGlobalCoord(valueAxis.dataToCoord(0)) + : valueAxis.getGlobalExtent()[0]; +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/* +* The `scaleLevels` references to d3.js. The use of the source +* code of this file is also subject to the terms and consitions +* of its license (BSD-3Clause, see ). +*/ + +// [About UTC and local time zone]: +// In most cases, `number.parseDate` will treat input data string as local time +// (except time zone is specified in time string). And `format.formateTime` returns +// local time by default. option.useUTC is false by default. This design have +// concidered these common case: +// (1) Time that is persistent in server is in UTC, but it is needed to be diplayed +// in local time by default. +// (2) By default, the input data string (e.g., '2011-01-02') should be displayed +// as its original time, without any time difference. + +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][1] < x) { + lo = mid + 1; + } + else { + hi = mid; + } + } + return lo; +}; + +/** + * @alias module:echarts/coord/scale/Time + * @constructor + */ +var TimeScale = IntervalScale.extend({ + type: 'time', + + /** + * @override + */ + getLabel: function (val) { + var stepLvl = this._stepLvl; + + var date = new Date(val); + + return formatTime(stepLvl[0], date, this.getSetting('useUTC')); + }, + + /** + * @override + */ + niceExtent: function (opt) { + 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(opt.splitNumber, opt.minInterval, opt.maxInterval); + + // var extent = this._extent; + var interval = this._interval; + + if (!opt.fixMin) { + extent[0] = round$1(mathFloor(extent[0] / interval) * interval); + } + if (!opt.fixMax) { + extent[1] = round$1(mathCeil(extent[1] / interval) * interval); + } + }, + + /** + * @override + */ + niceTicks: function (approxTickNum, minInterval, maxInterval) { + approxTickNum = approxTickNum || 10; + + var extent = this._extent; + var span = extent[1] - extent[0]; + var approxInterval = span / approxTickNum; + + if (minInterval != null && approxInterval < minInterval) { + approxInterval = minInterval; + } + if (maxInterval != null && approxInterval > maxInterval) { + approxInterval = maxInterval; + } + + var scaleLevelsLen = scaleLevels.length; + var idx = bisect(scaleLevels, approxInterval, 0, scaleLevelsLen); + + var level = scaleLevels[Math.min(idx, scaleLevelsLen - 1)]; + var interval = level[1]; + // 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 = nice(yearSpan / approxTickNum, true); + + interval *= yearStep; + } + + var timezoneOffset = this.getSetting('useUTC') + ? 0 : (new Date(+extent[0] || +extent[1])).getTimezoneOffset() * 60 * 1000; + var niceExtent = [ + Math.round(mathCeil((extent[0] - timezoneOffset) / interval) * interval + timezoneOffset), + Math.round(mathFloor((extent[1] - timezoneOffset) / interval) * interval + timezoneOffset) + ]; + + fixExtent(niceExtent, extent); + + this._stepLvl = level; + // Interval will be used in getTicks + this._interval = interval; + this._niceExtent = niceExtent; + }, + + parse: function (val) { + // val might be float. + return +parseDate(val); + } +}); + +each$1(['contain', 'normalize'], function (methodName) { + TimeScale.prototype[methodName] = function (val) { + return intervalScaleProto[methodName].call(this, this.parse(val)); + }; +}); + +// Steps from d3, see the license statement at the top of this file. +var scaleLevels = [ + // Format interval + ['hh:mm:ss', ONE_SECOND], // 1s + ['hh:mm:ss', ONE_SECOND * 5], // 5s + ['hh:mm:ss', ONE_SECOND * 10], // 10s + ['hh:mm:ss', ONE_SECOND * 15], // 15s + ['hh:mm:ss', ONE_SECOND * 30], // 30s + ['hh:mm\nMM-dd', ONE_MINUTE], // 1m + ['hh:mm\nMM-dd', ONE_MINUTE * 5], // 5m + ['hh:mm\nMM-dd', ONE_MINUTE * 10], // 10m + ['hh:mm\nMM-dd', ONE_MINUTE * 15], // 15m + ['hh:mm\nMM-dd', ONE_MINUTE * 30], // 30m + ['hh:mm\nMM-dd', ONE_HOUR], // 1h + ['hh:mm\nMM-dd', ONE_HOUR * 2], // 2h + ['hh:mm\nMM-dd', ONE_HOUR * 6], // 6h + ['hh:mm\nMM-dd', ONE_HOUR * 12], // 12h + ['MM-dd\nyyyy', ONE_DAY], // 1d + ['MM-dd\nyyyy', ONE_DAY * 2], // 2d + ['MM-dd\nyyyy', ONE_DAY * 3], // 3d + ['MM-dd\nyyyy', ONE_DAY * 4], // 4d + ['MM-dd\nyyyy', ONE_DAY * 5], // 5d + ['MM-dd\nyyyy', ONE_DAY * 6], // 6d + ['week', ONE_DAY * 7], // 7d + ['MM-dd\nyyyy', ONE_DAY * 10], // 10d + ['week', ONE_DAY * 14], // 2w + ['week', ONE_DAY * 21], // 3w + ['month', ONE_DAY * 31], // 1M + ['week', ONE_DAY * 42], // 6w + ['month', ONE_DAY * 62], // 2M + ['week', ONE_DAY * 42], // 10w + ['quarter', ONE_DAY * 380 / 4], // 3M + ['month', ONE_DAY * 31 * 4], // 4M + ['month', ONE_DAY * 31 * 5], // 5M + ['half-year', ONE_DAY * 380 / 2], // 6M + ['month', ONE_DAY * 31 * 8], // 8M + ['month', ONE_DAY * 31 * 10], // 10M + ['year', ONE_DAY * 380] // 1Y +]; + +/** + * @param {module:echarts/model/Model} + * @return {module:echarts/scale/Time} + */ +TimeScale.create = function (model) { + return new TimeScale({useUTC: model.ecModel.get('useUTC')}); +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * Log scale + * @module echarts/scale/Log + */ + +// Use some method of IntervalScale +var scaleProto$1 = Scale.prototype; +var intervalScaleProto$1 = IntervalScale.prototype; + +var getPrecisionSafe$1 = getPrecisionSafe; +var roundingErrorFix = round$1; + +var mathFloor$1 = Math.floor; +var mathCeil$1 = Math.ceil; +var mathPow$1 = Math.pow; + +var mathLog = Math.log; + +var LogScale = Scale.extend({ + + type: 'log', + + base: 10, + + $constructor: function () { + Scale.apply(this, arguments); + this._originalScale = new IntervalScale(); + }, + + /** + * @return {Array.} + */ + getTicks: function () { + var originalScale = this._originalScale; + var extent = this._extent; + var originalExtent = originalScale.getExtent(); + + return map(intervalScaleProto$1.getTicks.call(this), function (val) { + var powVal = round$1(mathPow$1(this.base, val)); + + // Fix #4158 + powVal = (val === extent[0] && originalScale.__fixMin) + ? fixRoundingError(powVal, originalExtent[0]) + : powVal; + powVal = (val === extent[1] && originalScale.__fixMax) + ? fixRoundingError(powVal, originalExtent[1]) + : powVal; + + return powVal; + }, this); + }, + + /** + * @param {number} val + * @return {string} + */ + getLabel: intervalScaleProto$1.getLabel, + + /** + * @param {number} val + * @return {number} + */ + scale: function (val) { + val = scaleProto$1.scale.call(this, val); + return mathPow$1(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$1.setExtent.call(this, start, end); + }, + + /** + * @return {number} end + */ + getExtent: function () { + var base = this.base; + var extent = scaleProto$1.getExtent.call(this); + extent[0] = mathPow$1(base, extent[0]); + extent[1] = mathPow$1(base, extent[1]); + + // Fix #4158 + var originalScale = this._originalScale; + var originalExtent = originalScale.getExtent(); + originalScale.__fixMin && (extent[0] = fixRoundingError(extent[0], originalExtent[0])); + originalScale.__fixMax && (extent[1] = fixRoundingError(extent[1], originalExtent[1])); + + return extent; + }, + + /** + * @param {Array.} extent + */ + unionExtent: function (extent) { + this._originalScale.unionExtent(extent); + + var base = this.base; + extent[0] = mathLog(extent[0]) / mathLog(base); + extent[1] = mathLog(extent[1]) / mathLog(base); + scaleProto$1.unionExtent.call(this, extent); + }, + + /** + * @override + */ + unionExtentFromData: function (data, dim) { + // TODO + // filter value that <= 0 + this.unionExtent(data.getApproximateExtent(dim)); + }, + + /** + * 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 = 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 = [ + round$1(mathCeil$1(extent[0] / interval) * interval), + round$1(mathFloor$1(extent[1] / interval) * interval) + ]; + + this._interval = interval; + this._niceExtent = niceExtent; + }, + + /** + * Nice extent. + * @override + */ + niceExtent: function (opt) { + intervalScaleProto$1.niceExtent.call(this, opt); + + var originalScale = this._originalScale; + originalScale.__fixMin = opt.fixMin; + originalScale.__fixMax = opt.fixMax; + } + +}); + +each$1(['contain', 'normalize'], function (methodName) { + LogScale.prototype[methodName] = function (val) { + val = mathLog(val) / mathLog(this.base); + return scaleProto$1[methodName].call(this, val); + }; +}); + +LogScale.create = function () { + return new LogScale(); +}; + +function fixRoundingError(val, originalVal) { + return roundingErrorFix(val, getPrecisionSafe$1(originalVal)); +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * Get axis scale extent before niced. + * Item of returned array can only be number (including Infinity and NaN). + */ +function getScaleExtent(scale, model) { + var scaleType = scale.type; + + var min = model.getMin(); + var max = model.getMax(); + var fixMin = min != null; + var fixMax = max != null; + var originalExtent = scale.getExtent(); + + var axisDataLen; + var boundaryGap; + var span; + if (scaleType === 'ordinal') { + axisDataLen = model.getCategories().length; + } + else { + boundaryGap = model.get('boundaryGap'); + if (!isArray(boundaryGap)) { + boundaryGap = [boundaryGap || 0, boundaryGap || 0]; + } + if (typeof boundaryGap[0] === 'boolean') { + if (__DEV__) { + console.warn('Boolean type for boundaryGap is only ' + + 'allowed for ordinal axis. Please use string in ' + + 'percentage instead, e.g., "20%". Currently, ' + + 'boundaryGap is set to be 0.'); + } + boundaryGap = [0, 0]; + } + boundaryGap[0] = parsePercent$1(boundaryGap[0], 1); + boundaryGap[1] = parsePercent$1(boundaryGap[1], 1); + span = (originalExtent[1] - originalExtent[0]) + || Math.abs(originalExtent[0]); + } + + // Notice: When min/max is not set (that is, when there are null/undefined, + // which is the most common case), these cases should be ensured: + // (1) For 'ordinal', show all axis.data. + // (2) For others: + // + `boundaryGap` is applied (if min/max set, boundaryGap is + // disabled). + // + If `needCrossZero`, min/max should be zero, otherwise, min/max should + // be the result that originalExtent enlarged by boundaryGap. + // (3) If no data, it should be ensured that `scale.setBlank` is set. + + // FIXME + // (1) When min/max is 'dataMin' or 'dataMax', should boundaryGap be able to used? + // (2) When `needCrossZero` and all data is positive/negative, should it be ensured + // that the results processed by boundaryGap are positive/negative? + + if (min == null) { + min = scaleType === 'ordinal' + ? (axisDataLen ? 0 : NaN) + : originalExtent[0] - boundaryGap[0] * span; + } + if (max == null) { + max = scaleType === 'ordinal' + ? (axisDataLen ? axisDataLen - 1 : NaN) + : originalExtent[1] + boundaryGap[1] * span; + } + + if (min === 'dataMin') { + min = originalExtent[0]; + } + else if (typeof min === 'function') { + min = min({ + min: originalExtent[0], + max: originalExtent[1] + }); + } + + if (max === 'dataMax') { + max = originalExtent[1]; + } + else if (typeof max === 'function') { + max = max({ + min: originalExtent[0], + max: originalExtent[1] + }); + } + + (min == null || !isFinite(min)) && (min = NaN); + (max == null || !isFinite(max)) && (max = NaN); + + scale.setBlank( + eqNaN(min) + || eqNaN(max) + || (scaleType === 'ordinal' && !scale.getOrdinalMeta().categories.length) + ); + + // Evaluate if axis needs cross zero + if (model.getNeedCrossZero()) { + // 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; + } + } + + // If bars are placed on a base axis of type time or interval account for axis boundary overflow and current axis + // is base axis + // FIXME + // (1) Consider support value axis, where below zero and axis `onZero` should be handled properly. + // (2) Refactor the logic with `barGrid`. Is it not need to `makeBarWidthAndOffsetInfo` twice with different extent? + // Should not depend on series type `bar`? + // (3) Fix that might overlap when using dataZoom. + // (4) Consider other chart types using `barGrid`? + // See #6728, #4862, `test/bar-overflow-time-plot.html` + var ecModel = model.ecModel; + if (ecModel && (scaleType === 'time' /*|| scaleType === 'interval' */)) { + var barSeriesModels = prepareLayoutBarSeries('bar', ecModel); + var isBaseAxisAndHasBarSeries; + + each$1(barSeriesModels, function (seriesModel) { + isBaseAxisAndHasBarSeries |= seriesModel.getBaseAxis() === model.axis; + }); + + if (isBaseAxisAndHasBarSeries) { + // Calculate placement of bars on axis + var barWidthAndOffset = makeColumnLayout(barSeriesModels); + + // Adjust axis min and max to account for overflow + var adjustedScale = adjustScaleForOverflow(min, max, model, barWidthAndOffset); + min = adjustedScale.min; + max = adjustedScale.max; + } + } + + return [min, max]; +} + +function adjustScaleForOverflow(min, max, model, barWidthAndOffset) { + + // Get Axis Length + var axisExtent = model.axis.getExtent(); + var axisLength = axisExtent[1] - axisExtent[0]; + + // Get bars on current base axis and calculate min and max overflow + var barsOnCurrentAxis = retrieveColumnLayout(barWidthAndOffset, model.axis); + if (barsOnCurrentAxis === undefined) { + return {min: min, max: max}; + } + + var minOverflow = Infinity; + each$1(barsOnCurrentAxis, function (item) { + minOverflow = Math.min(item.offset, minOverflow); + }); + var maxOverflow = -Infinity; + each$1(barsOnCurrentAxis, function (item) { + maxOverflow = Math.max(item.offset + item.width, maxOverflow); + }); + minOverflow = Math.abs(minOverflow); + maxOverflow = Math.abs(maxOverflow); + var totalOverFlow = minOverflow + maxOverflow; + + // Calulate required buffer based on old range and overflow + var oldRange = max - min; + var oldRangePercentOfNew = (1 - (minOverflow + maxOverflow) / axisLength); + var overflowBuffer = ((oldRange / oldRangePercentOfNew) - oldRange); + + max += overflowBuffer * (maxOverflow / totalOverFlow); + min -= overflowBuffer * (minOverflow / totalOverFlow); + + return {min: min, max: max}; +} + +function niceScaleExtent(scale, model) { + var extent = getScaleExtent(scale, model); + var fixMin = model.getMin() != null; + var fixMax = model.getMax() != null; + var splitNumber = model.get('splitNumber'); + + if (scale.type === 'log') { + scale.base = model.get('logBase'); + } + + var scaleType = scale.type; + scale.setExtent(extent[0], extent[1]); + scale.niceExtent({ + splitNumber: splitNumber, + fixMin: fixMin, + fixMax: fixMax, + minInterval: (scaleType === 'interval' || scaleType === 'time') + ? model.get('minInterval') : null, + maxInterval: (scaleType === 'interval' || scaleType === 'time') + ? model.get('maxInterval') : null + }); + + // 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/*} + */ +function createScaleByModel(model, axisType) { + axisType = axisType || model.get('type'); + if (axisType) { + switch (axisType) { + // Buildin scale + case 'category': + return new OrdinalScale( + model.getOrdinalMeta + ? model.getOrdinalMeta() + : 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 + */ +function ifAxisCrossZero(axis) { + var dataExtent = axis.scale.getExtent(); + var min = dataExtent[0]; + var max = dataExtent[1]; + return !((min > 0 && max > 0) || (min < 0 && max < 0)); +} + +/** + * @param {module:echarts/coord/Axis} axis + * @return {Function} Label formatter function. + * param: {number} tickValue, + * param: {number} idx, the index in all ticks. + * If category axis, this param is not requied. + * return: {string} label string. + */ +function makeLabelFormatter(axis) { + var labelFormatter = axis.getLabelModel().get('formatter'); + var categoryTickStart = axis.type === 'category' ? axis.scale.getExtent()[0] : null; + + if (typeof labelFormatter === 'string') { + labelFormatter = (function (tpl) { + return function (val) { + return tpl.replace('{value}', val != null ? val : ''); + }; + })(labelFormatter); + // Consider empty array + return labelFormatter; + } + else if (typeof labelFormatter === 'function') { + return function (tickValue, idx) { + // The original intention of `idx` is "the index of the tick in all ticks". + // But the previous implementation of category axis do not consider the + // `axisLabel.interval`, which cause that, for example, the `interval` is + // `1`, then the ticks "name5", "name7", "name9" are displayed, where the + // corresponding `idx` are `0`, `2`, `4`, but not `0`, `1`, `2`. So we keep + // the definition here for back compatibility. + if (categoryTickStart != null) { + idx = tickValue - categoryTickStart; + } + return labelFormatter(getAxisRawValue(axis, tickValue), idx); + }; + } + else { + return function (tick) { + return axis.scale.getLabel(tick); + }; + } +} + +function getAxisRawValue(axis, value) { + // In category axis with data zoom, tick is not the original + // index of axis.data. So tick should not be exposed to user + // in category axis. + return axis.type === 'category' ? axis.scale.getLabel(value) : value; +} + +/** + * @param {module:echarts/coord/Axis} axis + * @return {module:zrender/core/BoundingRect} Be null/undefined if no labels. + */ +function estimateLabelUnionRect(axis) { + var axisModel = axis.model; + var scale = axis.scale; + + if (!axisModel.get('axisLabel.show') || scale.isBlank()) { + return; + } + + var isCategory = axis.type === 'category'; + + var realNumberScaleTicks; + var tickCount; + var categoryScaleExtent = scale.getExtent(); + + // Optimize for large category data, avoid call `getTicks()`. + if (isCategory) { + tickCount = scale.count(); + } + else { + realNumberScaleTicks = scale.getTicks(); + tickCount = realNumberScaleTicks.length; + } + + var axisLabelModel = axis.getLabelModel(); + var labelFormatter = makeLabelFormatter(axis); + + var rect; + var step = 1; + // Simple optimization for large amount of labels + if (tickCount > 40) { + step = Math.ceil(tickCount / 40); + } + for (var i = 0; i < tickCount; i += step) { + var tickValue = realNumberScaleTicks ? realNumberScaleTicks[i] : categoryScaleExtent[0] + i; + var label = labelFormatter(tickValue); + var unrotatedSingleRect = axisLabelModel.getTextRect(label); + var singleRect = rotateTextRect(unrotatedSingleRect, axisLabelModel.get('rotate') || 0); + + rect ? rect.union(singleRect) : (rect = singleRect); + } + + return rect; +} + +function rotateTextRect(textRect, rotate) { + var rotateRadians = rotate * Math.PI / 180; + var boundingBox = textRect.plain(); + var beforeWidth = boundingBox.width; + var beforeHeight = boundingBox.height; + var afterWidth = beforeWidth * Math.cos(rotateRadians) + beforeHeight * Math.sin(rotateRadians); + var afterHeight = beforeWidth * Math.sin(rotateRadians) + beforeHeight * Math.cos(rotateRadians); + var rotatedRect = new BoundingRect(boundingBox.x, boundingBox.y, afterWidth, afterHeight); + + return rotatedRect; +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var axisModelCommonMixin = { + + /** + * @param {boolean} origin + * @return {number|string} min value or 'dataMin' or null/undefined (means auto) or NaN + */ + getMin: function (origin) { + var option = this.option; + var min = (!origin && option.rangeStart != null) + ? option.rangeStart : option.min; + + if (this.axis + && min != null + && min !== 'dataMin' + && typeof min !== 'function' + && !eqNaN(min) + ) { + min = this.axis.scale.parse(min); + } + return min; + }, + + /** + * @param {boolean} origin + * @return {number|string} max value or 'dataMax' or null/undefined (means auto) or NaN + */ + getMax: function (origin) { + var option = this.option; + var max = (!origin && option.rangeEnd != null) + ? option.rangeEnd : option.max; + + if (this.axis + && max != null + && max !== 'dataMax' + && typeof max !== 'function' + && !eqNaN(max) + ) { + max = this.axis.scale.parse(max); + } + return max; + }, + + /** + * @return {boolean} + */ + getNeedCrossZero: function () { + var option = this.option; + return (option.rangeStart != null || option.rangeEnd != null) + ? false : !option.scale; + }, + + /** + * Should be implemented by each axis model if necessary. + * @return {module:echarts/model/Component} coordinate system model + */ + getCoordSysModel: noop, + + /** + * @param {number} rangeStart Can only be finite number or null/undefined or NaN. + * @param {number} rangeEnd Can only be finite number or null/undefined or NaN. + */ + setRange: function (rangeStart, rangeEnd) { + this.option.rangeStart = rangeStart; + this.option.rangeEnd = rangeEnd; + }, + + /** + * Reset range + */ + resetRange: function () { + // rangeStart and rangeEnd is readonly. + this.option.rangeStart = this.option.rangeEnd = null; + } +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +// Symbol factory + +/** + * Triangle shape + * @inner + */ +var Triangle = 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 = 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 = 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); + + var cpLen = r * 0.6; + var cpLen2 = r * 0.7; + + path.moveTo(x - dx, cy + dy); + + path.arc( + x, cy, r, + Math.PI - angle, + Math.PI * 2 + angle + ); + 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 = 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: Line, + + rect: Rect, + + roundRect: Rect, + + square: Rect, + + circle: 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 = {}; +each$1(symbolCtors, function (Ctor, name) { + symbolBuildProxies[name] = new Ctor(); +}); + +var SymbolClz = 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 +function symbolPathSetColor(color, innerColor) { + 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 = innerColor || '#fff'; + } + else { + // FIXME 判断图形默认是填充还是描边,使用 onlyStroke ? + symbolStyle.fill && (symbolStyle.fill = color); + symbolStyle.stroke && (symbolStyle.stroke = color); + } + this.dirty(false); + } +} + +/** + * 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 + * @param {boolean} [keepAspect=false] whether to keep the ratio of w/h, + * for path and image only. + */ +function createSymbol(symbolType, x, y, w, h, color, keepAspect) { + // TODO Support image object, DynamicImage. + + 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 = makeImage( + symbolType.slice(8), + new BoundingRect(x, y, w, h), + keepAspect ? 'center' : 'cover' + ); + } + else if (symbolType.indexOf('path://') === 0) { + symbolPath = makePath( + symbolType.slice(7), + {}, + new BoundingRect(x, y, w, h), + keepAspect ? 'center' : 'cover' + ); + } + else { + symbolPath = new SymbolClz({ + shape: { + symbolType: symbolType, + x: x, + y: y, + width: w, + height: h + } + }); + } + + symbolPath.__isEmptyBrush = isEmpty; + + symbolPath.setColor = symbolPathSetColor; + + symbolPath.setColor(color); + + return symbolPath; +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +// import createGraphFromNodeEdge from './chart/helper/createGraphFromNodeEdge'; +/** + * Create a muti dimension List structure from seriesModel. + * @param {module:echarts/model/Model} seriesModel + * @return {module:echarts/data/List} list + */ +function createList(seriesModel) { + return createListFromArray(seriesModel.getSource(), seriesModel); +} + +var dataStack$1 = { + isDimensionStacked: isDimensionStacked, + enableDataStack: enableDataStack, + getStackedDimension: getStackedDimension +}; + +/** + * Create scale + * @param {Array.} dataExtent + * @param {Object|module:echarts/Model} option + */ +function createScale(dataExtent, option) { + var axisModel = option; + if (!Model.isInstance(option)) { + axisModel = new Model(option); + mixin(axisModel, axisModelCommonMixin); + } + + var scale = createScaleByModel(axisModel); + scale.setExtent(dataExtent[0], dataExtent[1]); + + niceScaleExtent(scale, axisModel); + return scale; +} + +/** + * Mixin common methods to axis model, + * + * Inlcude methods + * `getFormattedLabels() => Array.` + * `getCategories() => Array.` + * `getMin(origin: boolean) => number` + * `getMax(origin: boolean) => number` + * `getNeedCrossZero() => boolean` + * `setRange(start: number, end: number)` + * `resetRange()` + */ +function mixinAxisModelCommonMethods(Model$$1) { + mixin(Model$$1, axisModelCommonMixin); +} + +var helper = (Object.freeze || Object)({ + createList: createList, + getLayoutRect: getLayoutRect, + dataStack: dataStack$1, + createScale: createScale, + mixinAxisModelCommonMethods: mixinAxisModelCommonMethods, + completeDimensions: completeDimensions, + createDimensions: createDimensions, + createSymbol: createSymbol +}); + +var EPSILON$3 = 1e-8; + +function isAroundEqual$1(a, b) { + return Math.abs(a - b) < EPSILON$3; +} + +function contain$1(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$1(p[0], p0[0]) || !isAroundEqual$1(p[1], p0[1])) { + w += windingLine(p[0], p[1], p0[0], p0[1], x, y); + } + + return w !== 0; +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * @module echarts/coord/geo/Region + */ + +/** + * @param {string} name + * @param {Array} geometries + * @param {Array.} cp + */ +function Region(name, geometries, cp) { + + /** + * @type {string} + * @readOnly + */ + this.name = name; + + /** + * @type {Array.} + * @readOnly + */ + this.geometries = geometries; + + 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, + + properties: null, + + /** + * @return {module:zrender/core/BoundingRect} + */ + getBoundingRect: function () { + var rect = this._rect; + if (rect) { + return rect; + } + + var MAX_NUMBER = Number.MAX_VALUE; + var min$$1 = [MAX_NUMBER, MAX_NUMBER]; + var max$$1 = [-MAX_NUMBER, -MAX_NUMBER]; + var min2 = []; + var max2 = []; + var geometries = this.geometries; + for (var i = 0; i < geometries.length; i++) { + // Only support polygon + if (geometries[i].type !== 'polygon') { + continue; + } + // Doesn't consider hole + var exterior = geometries[i].exterior; + fromPoints(exterior, min2, max2); + min(min$$1, min$$1, min2); + max(max$$1, max$$1, max2); + } + // No data + if (i === 0) { + min$$1[0] = min$$1[1] = max$$1[0] = max$$1[1] = 0; + } + + return (this._rect = new BoundingRect( + min$$1[0], min$$1[1], max$$1[0] - min$$1[0], max$$1[1] - min$$1[1] + )); + }, + + /** + * @param {} coord + * @return {boolean} + */ + contain: function (coord) { + var rect = this.getBoundingRect(); + var geometries = this.geometries; + if (!rect.contain(coord[0], coord[1])) { + return false; + } + loopGeo: for (var i = 0, len$$1 = geometries.length; i < len$$1; i++) { + // Only support polygon. + if (geometries[i].type !== 'polygon') { + continue; + } + var exterior = geometries[i].exterior; + var interiors = geometries[i].interiors; + if (contain$1(exterior, coord[0], coord[1])) { + // Not in the region if point is in the hole. + for (var k = 0; k < (interiors ? interiors.length : 0); k++) { + if (contain$1(interiors[k])) { + continue loopGeo; + } + } + 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 geometries = this.geometries; + for (var i = 0; i < geometries.length; i++) { + // Only support polygon. + if (geometries[i].type !== 'polygon') { + continue; + } + var exterior = geometries[i].exterior; + var interiors = geometries[i].interiors; + for (var p = 0; p < exterior.length; p++) { + applyTransform(exterior[p], exterior[p], transform); + } + for (var h = 0; h < (interiors ? interiors.length : 0); h++) { + for (var p = 0; p < interiors[h].length; p++) { + applyTransform(interiors[h][p], interiors[h][p], transform); + } + } + } + rect = this._rect; + rect.copy(target); + // Update center + this.center = [ + rect.x + rect.width / 2, + rect.y + rect.height / 2 + ]; + } +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * Parse and decode geo json + * @module echarts/coord/geo/parseGeoJson + */ + +function decode(json) { + if (!json.UTF8Encoding) { + return json; + } + var encodeScale = json.UTF8Scale; + if (encodeScale == null) { + encodeScale = 1024; + } + + 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], + encodeScale + ); + } + else if (geometry.type === 'MultiPolygon') { + for (var c2 = 0; c2 < coordinate.length; c2++) { + var polygon = coordinate[c2]; + coordinate[c2] = decodePolygon( + polygon, + encodeOffsets[c][c2], + encodeScale + ); + } + } + } + } + // Has been decoded + json.UTF8Encoding = false; + return json; +} + +function decodePolygon(coordinate, encodeOffsets, encodeScale) { + 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 / encodeScale, y / encodeScale]); + } + + return result; +} + +/** + * @alias module:echarts/coord/geo/parseGeoJson + * @param {Object} geoJson + * @return {module:zrender/container/Group} + */ +var parseGeoJson$1 = function (geoJson) { + + decode(geoJson); + + return map(filter(geoJson.features, function (featureObj) { + // Output of mapshaper may have geometry null + return featureObj.geometry + && featureObj.properties + && featureObj.geometry.coordinates.length > 0; + }), function (featureObj) { + var properties = featureObj.properties; + var geo = featureObj.geometry; + + var coordinates = geo.coordinates; + + var geometries = []; + if (geo.type === 'Polygon') { + geometries.push({ + type: 'polygon', + // According to the GeoJSON specification. + // First must be exterior, and the rest are all interior(holes). + exterior: coordinates[0], + interiors: coordinates.slice(1) + }); + } + if (geo.type === 'MultiPolygon') { + each$1(coordinates, function (item) { + if (item[0]) { + geometries.push({ + type: 'polygon', + exterior: item[0], + interiors: item.slice(1) + }); + } + }); + } + + var region = new Region( + properties.name, + geometries, + properties.cp + ); + region.properties = properties; + return region; + }); +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var inner$6 = makeInner(); + +/** + * @param {module:echats/coord/Axis} axis + * @return {Object} { + * labels: [{ + * formattedLabel: string, + * rawLabel: string, + * tickValue: number + * }, ...], + * labelCategoryInterval: number + * } + */ +function createAxisLabels(axis) { + // Only ordinal scale support tick interval + return axis.type === 'category' + ? makeCategoryLabels(axis) + : makeRealNumberLabels(axis); +} + +/** + * @param {module:echats/coord/Axis} axis + * @param {module:echarts/model/Model} tickModel For example, can be axisTick, splitLine, splitArea. + * @return {Object} { + * ticks: Array. + * tickCategoryInterval: number + * } + */ +function createAxisTicks(axis, tickModel) { + // Only ordinal scale support tick interval + return axis.type === 'category' + ? makeCategoryTicks(axis, tickModel) + : {ticks: axis.scale.getTicks()}; +} + +function makeCategoryLabels(axis) { + var labelModel = axis.getLabelModel(); + var result = makeCategoryLabelsActually(axis, labelModel); + + return (!labelModel.get('show') || axis.scale.isBlank()) + ? {labels: [], labelCategoryInterval: result.labelCategoryInterval} + : result; +} + +function makeCategoryLabelsActually(axis, labelModel) { + var labelsCache = getListCache(axis, 'labels'); + var optionLabelInterval = getOptionCategoryInterval(labelModel); + var result = listCacheGet(labelsCache, optionLabelInterval); + + if (result) { + return result; + } + + var labels; + var numericLabelInterval; + + if (isFunction$1(optionLabelInterval)) { + labels = makeLabelsByCustomizedCategoryInterval(axis, optionLabelInterval); + } + else { + numericLabelInterval = optionLabelInterval === 'auto' + ? makeAutoCategoryInterval(axis) : optionLabelInterval; + labels = makeLabelsByNumericCategoryInterval(axis, numericLabelInterval); + } + + // Cache to avoid calling interval function repeatly. + return listCacheSet(labelsCache, optionLabelInterval, { + labels: labels, labelCategoryInterval: numericLabelInterval + }); +} + +function makeCategoryTicks(axis, tickModel) { + var ticksCache = getListCache(axis, 'ticks'); + var optionTickInterval = getOptionCategoryInterval(tickModel); + var result = listCacheGet(ticksCache, optionTickInterval); + + if (result) { + return result; + } + + var ticks; + var tickCategoryInterval; + + // Optimize for the case that large category data and no label displayed, + // we should not return all ticks. + if (!tickModel.get('show') || axis.scale.isBlank()) { + ticks = []; + } + + if (isFunction$1(optionTickInterval)) { + ticks = makeLabelsByCustomizedCategoryInterval(axis, optionTickInterval, true); + } + // Always use label interval by default despite label show. Consider this + // scenario, Use multiple grid with the xAxis sync, and only one xAxis shows + // labels. `splitLine` and `axisTick` should be consistent in this case. + else if (optionTickInterval === 'auto') { + var labelsResult = makeCategoryLabelsActually(axis, axis.getLabelModel()); + tickCategoryInterval = labelsResult.labelCategoryInterval; + ticks = map(labelsResult.labels, function (labelItem) { + return labelItem.tickValue; + }); + } + else { + tickCategoryInterval = optionTickInterval; + ticks = makeLabelsByNumericCategoryInterval(axis, tickCategoryInterval, true); + } + + // Cache to avoid calling interval function repeatly. + return listCacheSet(ticksCache, optionTickInterval, { + ticks: ticks, tickCategoryInterval: tickCategoryInterval + }); +} + +function makeRealNumberLabels(axis) { + var ticks = axis.scale.getTicks(); + var labelFormatter = makeLabelFormatter(axis); + return { + labels: map(ticks, function (tickValue, idx) { + return { + formattedLabel: labelFormatter(tickValue, idx), + rawLabel: axis.scale.getLabel(tickValue), + tickValue: tickValue + }; + }) + }; +} + +// Large category data calculation is performence sensitive, and ticks and label +// probably be fetched by multiple times. So we cache the result. +// axis is created each time during a ec process, so we do not need to clear cache. +function getListCache(axis, prop) { + // Because key can be funciton, and cache size always be small, we use array cache. + return inner$6(axis)[prop] || (inner$6(axis)[prop] = []); +} + +function listCacheGet(cache, key) { + for (var i = 0; i < cache.length; i++) { + if (cache[i].key === key) { + return cache[i].value; + } + } +} + +function listCacheSet(cache, key, value) { + cache.push({key: key, value: value}); + return value; +} + +function makeAutoCategoryInterval(axis) { + var result = inner$6(axis).autoInterval; + return result != null + ? result + : (inner$6(axis).autoInterval = axis.calculateCategoryInterval()); +} + +/** + * Calculate interval for category axis ticks and labels. + * To get precise result, at least one of `getRotate` and `isHorizontal` + * should be implemented in axis. + */ +function calculateCategoryInterval(axis) { + var params = fetchAutoCategoryIntervalCalculationParams(axis); + var labelFormatter = makeLabelFormatter(axis); + var rotation = (params.axisRotate - params.labelRotate) / 180 * Math.PI; + + var ordinalScale = axis.scale; + var ordinalExtent = ordinalScale.getExtent(); + // Providing this method is for optimization: + // avoid generating a long array by `getTicks` + // in large category data case. + var tickCount = ordinalScale.count(); + + if (ordinalExtent[1] - ordinalExtent[0] < 1) { + return 0; + } + + var step = 1; + // Simple optimization. Empirical value: tick count should less than 40. + if (tickCount > 40) { + step = Math.max(1, Math.floor(tickCount / 40)); + } + var tickValue = ordinalExtent[0]; + var unitSpan = axis.dataToCoord(tickValue + 1) - axis.dataToCoord(tickValue); + var unitW = Math.abs(unitSpan * Math.cos(rotation)); + var unitH = Math.abs(unitSpan * Math.sin(rotation)); + + var maxW = 0; + var maxH = 0; + + // Caution: Performance sensitive for large category data. + // Consider dataZoom, we should make appropriate step to avoid O(n) loop. + for (; tickValue <= ordinalExtent[1]; tickValue += step) { + var width = 0; + var height = 0; + + // Polar is also calculated in assumptive linear layout here. + // Not precise, do not consider align and vertical align + // and each distance from axis line yet. + var rect = getBoundingRect( + labelFormatter(tickValue), params.font, 'center', 'top' + ); + // Magic number + width = rect.width * 1.3; + height = rect.height * 1.3; + + // Min size, void long loop. + maxW = Math.max(maxW, width, 7); + maxH = Math.max(maxH, height, 7); + } + + var dw = maxW / unitW; + var dh = maxH / unitH; + // 0/0 is NaN, 1/0 is Infinity. + isNaN(dw) && (dw = Infinity); + isNaN(dh) && (dh = Infinity); + var interval = Math.max(0, Math.floor(Math.min(dw, dh))); + + var cache = inner$6(axis.model); + var lastAutoInterval = cache.lastAutoInterval; + var lastTickCount = cache.lastTickCount; + + // Use cache to keep interval stable while moving zoom window, + // otherwise the calculated interval might jitter when the zoom + // window size is close to the interval-changing size. + if (lastAutoInterval != null + && lastTickCount != null + && Math.abs(lastAutoInterval - interval) <= 1 + && Math.abs(lastTickCount - tickCount) <= 1 + // Always choose the bigger one, otherwise the critical + // point is not the same when zooming in or zooming out. + && lastAutoInterval > interval + ) { + interval = lastAutoInterval; + } + // Only update cache if cache not used, otherwise the + // changing of interval is too insensitive. + else { + cache.lastTickCount = tickCount; + cache.lastAutoInterval = interval; + } + + return interval; +} + +function fetchAutoCategoryIntervalCalculationParams(axis) { + var labelModel = axis.getLabelModel(); + return { + axisRotate: axis.getRotate + ? axis.getRotate() + : (axis.isHorizontal && !axis.isHorizontal()) + ? 90 + : 0, + labelRotate: labelModel.get('rotate') || 0, + font: labelModel.getFont() + }; +} + +function makeLabelsByNumericCategoryInterval(axis, categoryInterval, onlyTick) { + var labelFormatter = makeLabelFormatter(axis); + var ordinalScale = axis.scale; + var ordinalExtent = ordinalScale.getExtent(); + var labelModel = axis.getLabelModel(); + var result = []; + + // TODO: axisType: ordinalTime, pick the tick from each month/day/year/... + + var step = Math.max((categoryInterval || 0) + 1, 1); + var startTick = ordinalExtent[0]; + var tickCount = ordinalScale.count(); + + // Calculate start tick based on zero if possible to keep label consistent + // while zooming and moving while interval > 0. Otherwise the selection + // of displayable ticks and symbols probably keep changing. + // 3 is empirical value. + if (startTick !== 0 && step > 1 && tickCount / step > 2) { + startTick = Math.round(Math.ceil(startTick / step) * step); + } + + // (1) Only add min max label here but leave overlap checking + // to render stage, which also ensure the returned list + // suitable for splitLine and splitArea rendering. + // (2) Scales except category always contain min max label so + // do not need to perform this process. + var showMinMax = { + min: labelModel.get('showMinLabel'), + max: labelModel.get('showMaxLabel') + }; + + if (showMinMax.min && startTick !== ordinalExtent[0]) { + addItem(ordinalExtent[0]); + } + + // Optimize: avoid generating large array by `ordinalScale.getTicks()`. + var tickValue = startTick; + for (; tickValue <= ordinalExtent[1]; tickValue += step) { + addItem(tickValue); + } + + if (showMinMax.max && tickValue !== ordinalExtent[1]) { + addItem(ordinalExtent[1]); + } + + function addItem(tVal) { + result.push(onlyTick + ? tVal + : { + formattedLabel: labelFormatter(tVal), + rawLabel: ordinalScale.getLabel(tVal), + tickValue: tVal + } + ); + } + + return result; +} + +// When interval is function, the result `false` means ignore the tick. +// It is time consuming for large category data. +function makeLabelsByCustomizedCategoryInterval(axis, categoryInterval, onlyTick) { + var ordinalScale = axis.scale; + var labelFormatter = makeLabelFormatter(axis); + var result = []; + + each$1(ordinalScale.getTicks(), function (tickValue) { + var rawLabel = ordinalScale.getLabel(tickValue); + if (categoryInterval(tickValue, rawLabel)) { + result.push(onlyTick + ? tickValue + : { + formattedLabel: labelFormatter(tickValue), + rawLabel: rawLabel, + tickValue: tickValue + } + ); + } + }); + + return result; +} + +// Can be null|'auto'|number|function +function getOptionCategoryInterval(model) { + var interval = model.get('interval'); + return interval == null ? 'auto' : interval; +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var NORMALIZED_EXTENT = [0, 1]; + +/** + * Base class of Axis. + * @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 () { + return this._extent.slice(); + }, + + /** + * Get precision used for formatting + * @param {Array.} [dataExtent] + * @return {number} + */ + getPixelPrecision: function (dataExtent) { + return 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 an 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, NORMALIZED_EXTENT, extent, clamp); + }, + + /** + * Convert coord to data. Data is the rank if it has an 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, NORMALIZED_EXTENT, clamp); + + return this.scale.scale(t); + }, + + /** + * Convert pixel point to data in axis + * @param {Array.} point + * @param {boolean} clamp + * @return {number} data + */ + pointToData: function (point, clamp) { + // Should be implemented in derived class if necessary. + }, + + /** + * Different from `zrUtil.map(axis.getTicks(), axis.dataToCoord, axis)`, + * `axis.getTicksCoords` considers `onBand`, which is used by + * `boundaryGap:true` of category axis and splitLine and splitArea. + * @param {Object} [opt] + * @param {number} [opt.tickModel=axis.model.getModel('axisTick')] + * @param {boolean} [opt.clamp] If `true`, the first and the last + * tick must be at the axis end points. Otherwise, clip ticks + * that outside the axis extent. + * @return {Array.} [{ + * coord: ..., + * tickValue: ... + * }, ...] + */ + getTicksCoords: function (opt) { + opt = opt || {}; + + var tickModel = opt.tickModel || this.getTickModel(); + + var result = createAxisTicks(this, tickModel); + var ticks = result.ticks; + + var ticksCoords = map(ticks, function (tickValue) { + return { + coord: this.dataToCoord(tickValue), + tickValue: tickValue + }; + }, this); + + var alignWithLabel = tickModel.get('alignWithLabel'); + fixOnBandTicksCoords( + this, ticksCoords, result.tickCategoryInterval, alignWithLabel, opt.clamp + ); + + return ticksCoords; + }, + + /** + * @return {Array.} [{ + * formattedLabel: string, + * rawLabel: axis.scale.getLabel(tickValue) + * tickValue: number + * }, ...] + */ + getViewLabels: function () { + return createAxisLabels(this).labels; + }, + + /** + * @return {module:echarts/coord/model/Model} + */ + getLabelModel: function () { + return this.model.getModel('axisLabel'); + }, + + /** + * Notice here we only get the default tick model. For splitLine + * or splitArea, we should pass the splitLineModel or splitAreaModel + * manually when calling `getTicksCoords`. + * In GL, this method may be overrided to: + * `axisModel.getModel('axisTick', grid3DModel.getModel('axisTick'));` + * @return {module:echarts/coord/model/Model} + */ + getTickModel: function () { + return this.model.getModel('axisTick'); + }, + + /** + * 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; + }, + + /** + * @abstract + * @return {boolean} Is horizontal + */ + isHorizontal: null, + + /** + * @abstract + * @return {number} Get axis rotate, by degree. + */ + getRotate: null, + + /** + * Only be called in category axis. + * Can be overrided, consider other axes like in 3D. + * @return {number} Auto interval for cateogry axis tick and label + */ + calculateCategoryInterval: function () { + return calculateCategoryInterval(this); + } + +}; + +function fixExtentWithBands(extent, nTick) { + var size = extent[1] - extent[0]; + var len = nTick; + var margin = size / len / 2; + extent[0] += margin; + extent[1] -= margin; +} + +// If axis has labels [1, 2, 3, 4]. Bands on the axis are +// |---1---|---2---|---3---|---4---|. +// So the displayed ticks and splitLine/splitArea should between +// each data item, otherwise cause misleading (e.g., split tow bars +// of a single data item when there are two bar series). +// Also consider if tickCategoryInterval > 0 and onBand, ticks and +// splitLine/spliteArea should layout appropriately corresponding +// to displayed labels. (So we should not use `getBandWidth` in this +// case). +function fixOnBandTicksCoords(axis, ticksCoords, tickCategoryInterval, alignWithLabel, clamp) { + var ticksLen = ticksCoords.length; + + if (!axis.onBand || alignWithLabel || !ticksLen) { + return; + } + + var axisExtent = axis.getExtent(); + var last; + if (ticksLen === 1) { + ticksCoords[0].coord = axisExtent[0]; + last = ticksCoords[1] = {coord: axisExtent[0]}; + } + else { + var shift = (ticksCoords[1].coord - ticksCoords[0].coord); + each$1(ticksCoords, function (ticksItem) { + ticksItem.coord -= shift / 2; + var tickCategoryInterval = tickCategoryInterval || 0; + // Avoid split a single data item when odd interval. + if (tickCategoryInterval % 2 > 0) { + ticksItem.coord -= shift / ((tickCategoryInterval + 1) * 2); + } + }); + last = {coord: ticksCoords[ticksLen - 1].coord + shift}; + ticksCoords.push(last); + } + + var inverse = axisExtent[0] > axisExtent[1]; + + if (littleThan(ticksCoords[0].coord, axisExtent[0])) { + clamp ? (ticksCoords[0].coord = axisExtent[0]) : ticksCoords.shift(); + } + if (clamp && littleThan(axisExtent[0], ticksCoords[0].coord)) { + ticksCoords.unshift({coord: axisExtent[0]}); + } + if (littleThan(axisExtent[1], last.coord)) { + clamp ? (last.coord = axisExtent[1]) : ticksCoords.pop(); + } + if (clamp && littleThan(last.coord, axisExtent[1])) { + ticksCoords.push({coord: axisExtent[1]}); + } + + function littleThan(a, b) { + return inverse ? a > b : a < b; + } +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * Do not mount those modules on 'src/echarts' for better tree shaking. + */ + +var parseGeoJson = parseGeoJson$1; + +var ecUtil = {}; +each$1([ + 'map', 'each', 'filter', 'indexOf', 'inherits', 'reduce', 'filter', + 'bind', 'curry', 'isArray', 'isString', 'isObject', 'isFunction', + 'extend', 'defaults', 'clone', 'merge' + ], + function (name) { + ecUtil[name] = zrUtil[name]; + } +); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +SeriesModel.extend({ + + type: 'series.line', + + dependencies: ['grid', 'polar'], + + getInitialData: function (option, ecModel) { + if (__DEV__) { + var coordSys = option.coordinateSystem; + if (coordSys !== 'polar' && coordSys !== 'cartesian2d') { + throw new Error('Line not support coordinateSystem besides cartesian and polar'); + } + } + return createListFromArray(this.getSource(), this); + }, + + 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, + // cursor: null, + + label: { + position: 'top' + }, + // itemStyle: { + // }, + + lineStyle: { + width: 2, + type: 'solid' + }, + // areaStyle: { + // origin of areaStyle. Valid values: + // `'auto'/null/undefined`: from axisLine to data + // `'start'`: from min to data + // `'end'`: from data to max + // origin: 'auto' + // }, + // false, 'start', 'end', 'middle' + step: false, + + // Disabled if step is true + smooth: false, + smoothMonotone: null, + symbol: 'emptyCircle', + symbolSize: 4, + symbolRotate: null, + + showSymbol: true, + // `false`: follow the label interval strategy. + // `true`: show all symbols. + // `'auto'`: If possible, show all symbols, otherwise + // follow the label interval strategy. + showAllSymbol: 'auto', + + // Whether to connect break point. + connectNulls: false, + + // Sampling for large data. Can be: 'average', 'max', 'min', 'sum'. + sampling: 'none', + + animationEasing: 'linear', + + // Disable progressive + progressive: 0, + hoverLayerThreshold: Infinity + } +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * @param {module:echarts/data/List} data + * @param {number} dataIndex + * @return {string} label string. Not null/undefined + */ +function getDefaultLabel(data, dataIndex) { + var labelDims = data.mapDimension('defaultedLabel', true); + var len = labelDims.length; + + // Simple optimization (in lots of cases, label dims length is 1) + if (len === 1) { + return retrieveRawValue(data, dataIndex, labelDims[0]); + } + else if (len) { + var vals = []; + for (var i = 0; i < labelDims.length; i++) { + var val = retrieveRawValue(data, dataIndex, labelDims[i]); + vals.push(val); + } + return vals.join(' '); + } +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * @module echarts/chart/helper/Symbol + */ + +/** + * @constructor + * @alias {module:echarts/chart/helper/Symbol} + * @param {module:echarts/data/List} data + * @param {number} idx + * @extends {module:zrender/graphic/Group} + */ +function SymbolClz$1(data, idx, seriesScope) { + Group.call(this); + this.updateData(data, idx, seriesScope); +} + +var symbolProto = SymbolClz$1.prototype; + +/** + * @public + * @static + * @param {module:echarts/data/List} data + * @param {number} dataIndex + * @return {Array.} [width, height] + */ +var getSymbolSize = SymbolClz$1.getSymbolSize = function (data, idx) { + var symbolSize = data.getItemVisual(idx, 'symbolSize'); + return symbolSize instanceof Array + ? symbolSize.slice() + : [+symbolSize, +symbolSize]; +}; + +function getScale(symbolSize) { + return [symbolSize[0] / 2, symbolSize[1] / 2]; +} + +function driftSymbol(dx, dy) { + this.parent.drift(dx, dy); +} + +symbolProto._createSymbol = function ( + symbolType, + data, + idx, + symbolSize, + keepAspect +) { + // Remove paths created before + this.removeAll(); + + var color = data.getItemVisual(idx, 'color'); + + // var symbolPath = createSymbol( + // symbolType, -0.5, -0.5, 1, 1, color + // ); + // If width/height are set too small (e.g., set to 1) on ios10 + // and macOS Sierra, a circle stroke become a rect, no matter what + // the scale is set. So we set width/height as 2. See #4150. + var symbolPath = createSymbol( + symbolType, -1, -1, 2, 2, color, keepAspect + ); + + symbolPath.attr({ + z2: 100, + culling: true, + scale: getScale(symbolSize) + }); + // Rewrite drift method + symbolPath.drift = driftSymbol; + + this._symbolType = symbolType; + + this.add(symbolPath); +}; + +/** + * Stop animation + * @param {boolean} toLastFrame + */ +symbolProto.stopSymbolAnimation = function (toLastFrame) { + this.childAt(0).stopAnimation(toLastFrame); +}; + +/** + * FIXME: + * Caution: This method breaks the encapsulation of this module, + * but it indeed brings convenience. So do not use the method + * unless you detailedly know all the implements of `Symbol`, + * especially animation. + * + * 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 + * @param {Object} [seriesScope] + * @param {Object} [seriesScope.itemStyle] + * @param {Object} [seriesScope.hoverItemStyle] + * @param {Object} [seriesScope.symbolRotate] + * @param {Object} [seriesScope.symbolOffset] + * @param {module:echarts/model/Model} [seriesScope.labelModel] + * @param {module:echarts/model/Model} [seriesScope.hoverLabelModel] + * @param {boolean} [seriesScope.hoverAnimation] + * @param {Object} [seriesScope.cursorStyle] + * @param {module:echarts/model/Model} [seriesScope.itemModel] + * @param {string} [seriesScope.symbolInnerColor] + * @param {Object} [seriesScope.fadeIn=false] + */ +symbolProto.updateData = function (data, idx, seriesScope) { + this.silent = false; + + var symbolType = data.getItemVisual(idx, 'symbol') || 'circle'; + var seriesModel = data.hostModel; + var symbolSize = getSymbolSize(data, idx); + var isInit = symbolType !== this._symbolType; + + if (isInit) { + var keepAspect = data.getItemVisual(idx, 'symbolKeepAspect'); + this._createSymbol(symbolType, data, idx, symbolSize, keepAspect); + } + else { + var symbolPath = this.childAt(0); + symbolPath.silent = false; + updateProps(symbolPath, { + scale: getScale(symbolSize) + }, seriesModel, idx); + } + + this._updateCommon(data, idx, symbolSize, seriesScope); + + if (isInit) { + var symbolPath = this.childAt(0); + var fadeIn = seriesScope && seriesScope.fadeIn; + + var target = {scale: symbolPath.scale.slice()}; + fadeIn && (target.style = {opacity: symbolPath.style.opacity}); + + symbolPath.scale = [0, 0]; + fadeIn && (symbolPath.style.opacity = 0); + + initProps(symbolPath, target, seriesModel, idx); + } + + this._seriesModel = seriesModel; +}; + +// Update common properties +var normalStyleAccessPath = ['itemStyle']; +var emphasisStyleAccessPath = ['emphasis', 'itemStyle']; +var normalLabelAccessPath = ['label']; +var emphasisLabelAccessPath = ['emphasis', 'label']; + +/** + * @param {module:echarts/data/List} data + * @param {number} idx + * @param {Array.} symbolSize + * @param {Object} [seriesScope] + */ +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 + }); + } + + 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; + var cursorStyle = seriesScope && seriesScope.cursorStyle; + + if (!seriesScope || data.hasItemOption) { + var itemModel = (seriesScope && seriesScope.itemModel) + ? seriesScope.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'); + cursorStyle = itemModel.getShallow('cursor'); + } + else { + hoverItemStyle = extend({}, hoverItemStyle); + } + + var elStyle = symbolPath.style; + + symbolPath.attr('rotation', (symbolRotate || 0) * Math.PI / 180 || 0); + + if (symbolOffset) { + symbolPath.attr('position', [ + parsePercent$1(symbolOffset[0], symbolSize[0]), + parsePercent$1(symbolOffset[1], symbolSize[1]) + ]); + } + + cursorStyle && symbolPath.attr('cursor', cursorStyle); + + // PENDING setColor before setStyle!!! + symbolPath.setColor(color, seriesScope && seriesScope.symbolInnerColor); + + symbolPath.setStyle(itemStyle); + + var opacity = data.getItemVisual(idx, 'opacity'); + if (opacity != null) { + elStyle.opacity = opacity; + } + + var liftZ = data.getItemVisual(idx, 'liftZ'); + var z2Origin = symbolPath.__z2Origin; + if (liftZ != null) { + if (z2Origin == null) { + symbolPath.__z2Origin = symbolPath.z2; + symbolPath.z2 += liftZ; + } + } + else if (z2Origin != null) { + symbolPath.z2 = z2Origin; + symbolPath.__z2Origin = null; + } + + var useNameLabel = seriesScope && seriesScope.useNameLabel; + + setLabelStyle( + elStyle, hoverItemStyle, labelModel, hoverLabelModel, + { + labelFetcher: seriesModel, + labelDataIndex: idx, + defaultText: getLabelDefaultText, + isRectText: true, + autoColor: color + } + ); + + // Do not execute util needed. + function getLabelDefaultText(idx, opt) { + return useNameLabel ? data.getName(idx) : getDefaultLabel(data, idx); + } + + symbolPath.off('mouseover') + .off('mouseout') + .off('emphasis') + .off('normal'); + + symbolPath.hoverStyle = hoverItemStyle; + + // FIXME + // Do not use symbol.trigger('emphasis'), but use symbol.highlight() instead. + setHoverStyle(symbolPath); + + var scale = getScale(symbolSize); + + if (hoverAnimation && seriesModel.isAnimationEnabled()) { + var onEmphasis = function() { + // Do not support this hover animation util some scenario required. + // Animation can only be supported in hover layer when using `el.incremetal`. + if (this.incremental) { + return; + } + var ratio = scale[1] / scale[0]; + this.animateTo({ + scale: [ + Math.max(scale[0] * 1.1, scale[0] + 3), + Math.max(scale[1] * 1.1, scale[1] + 3 * ratio) + ] + }, 400, 'elasticOut'); + }; + var onNormal = function() { + if (this.incremental) { + return; + } + this.animateTo({ + scale: scale + }, 400, 'elasticOut'); + }; + symbolPath.on('mouseover', onEmphasis) + .on('mouseout', onNormal) + .on('emphasis', onEmphasis) + .on('normal', onNormal); + } +}; + +/** + * @param {Function} cb + * @param {Object} [opt] + * @param {Object} [opt.keepLabel=true] + */ +symbolProto.fadeOut = function (cb, opt) { + var symbolPath = this.childAt(0); + // Avoid mistaken hover when fading out + this.silent = symbolPath.silent = true; + // Not show text when animating + !(opt && opt.keepLabel) && (symbolPath.style.text = null); + + updateProps( + symbolPath, + { + style: {opacity: 0}, + scale: [0, 0] + }, + this._seriesModel, + this.dataIndex, + cb + ); +}; + +inherits(SymbolClz$1, Group); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * @module echarts/chart/helper/SymbolDraw + */ + +/** + * @constructor + * @alias module:echarts/chart/helper/SymbolDraw + * @param {module:zrender/graphic/Group} [symbolCtor] + */ +function SymbolDraw(symbolCtor) { + this.group = new Group(); + + this._symbolCtor = symbolCtor || SymbolClz$1; +} + +var symbolDrawProto = SymbolDraw.prototype; + +function symbolNeedsDraw(data, point, idx, opt) { + return point && !isNaN(point[0]) && !isNaN(point[1]) + && !(opt.isIgnore && opt.isIgnore(idx)) + // We do not set clipShape on group, because it will cut part of + // the symbol element shape. We use the same clip shape here as + // the line clip. + && !(opt.clipShape && !opt.clipShape.contain(point[0], point[1])) + && data.getItemVisual(idx, 'symbol') !== 'none'; +} + +/** + * Update symbols draw by new data + * @param {module:echarts/data/List} data + * @param {Object} [opt] Or isIgnore + * @param {Function} [opt.isIgnore] + * @param {Object} [opt.clipShape] + */ +symbolDrawProto.updateData = function (data, opt) { + opt = normalizeUpdateOpt(opt); + + var group = this.group; + var seriesModel = data.hostModel; + var oldData = this._data; + var SymbolCtor = this._symbolCtor; + + var seriesScope = makeSeriesScope(data); + + // There is no oldLineData only when first rendering or switching from + // stream mode to normal mode, where previous elements should be removed. + if (!oldData) { + group.removeAll(); + } + + data.diff(oldData) + .add(function (newIdx) { + var point = data.getItemLayout(newIdx); + if (symbolNeedsDraw(data, point, newIdx, opt)) { + 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, point, newIdx, opt)) { + group.remove(symbolEl); + return; + } + if (!symbolEl) { + symbolEl = new SymbolCtor(data, newIdx); + symbolEl.attr('position', point); + } + else { + symbolEl.updateData(data, newIdx, seriesScope); + 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.isPersistent = function () { + return true; +}; + +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.incrementalPrepareUpdate = function (data) { + this._seriesScope = makeSeriesScope(data); + this._data = null; + this.group.removeAll(); +}; + +/** + * Update symbols draw by new data + * @param {module:echarts/data/List} data + * @param {Object} [opt] Or isIgnore + * @param {Function} [opt.isIgnore] + * @param {Object} [opt.clipShape] + */ +symbolDrawProto.incrementalUpdate = function (taskParams, data, opt) { + opt = normalizeUpdateOpt(opt); + + function updateIncrementalAndHover(el) { + if (!el.isGroup) { + el.incremental = el.useHoverLayer = true; + } + } + for (var idx = taskParams.start; idx < taskParams.end; idx++) { + var point = data.getItemLayout(idx); + if (symbolNeedsDraw(data, point, idx, opt)) { + var el = new this._symbolCtor(data, idx, this._seriesScope); + el.traverse(updateIncrementalAndHover); + el.attr('position', point); + this.group.add(el); + data.setItemGraphicEl(idx, el); + } + } +}; + +function normalizeUpdateOpt(opt) { + if (opt != null && !isObject$1(opt)) { + opt = {isIgnore: opt}; + } + return opt || {}; +} + +symbolDrawProto.remove = function (enableAnimation) { + var group = this.group; + var data = this._data; + // Incremental model do not have this._data. + if (data && enableAnimation) { + data.eachItemGraphicEl(function (el) { + el.fadeOut(function () { + group.remove(el); + }); + }); + } + else { + group.removeAll(); + } +}; + +function makeSeriesScope(data) { + var seriesModel = data.hostModel; + return { + itemStyle: seriesModel.getModel('itemStyle').getItemStyle(['color']), + hoverItemStyle: seriesModel.getModel('emphasis.itemStyle').getItemStyle(), + symbolRotate: seriesModel.get('symbolRotate'), + symbolOffset: seriesModel.get('symbolOffset'), + hoverAnimation: seriesModel.get('hoverAnimation'), + labelModel: seriesModel.getModel('label'), + hoverLabelModel: seriesModel.getModel('emphasis.label'), + cursorStyle: seriesModel.get('cursor') + }; +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * @param {Object} coordSys + * @param {module:echarts/data/List} data + * @param {string} valueOrigin lineSeries.option.areaStyle.origin + */ +function prepareDataCoordInfo(coordSys, data, valueOrigin) { + var baseAxis = coordSys.getBaseAxis(); + var valueAxis = coordSys.getOtherAxis(baseAxis); + var valueStart = getValueStart(valueAxis, valueOrigin); + + var baseAxisDim = baseAxis.dim; + var valueAxisDim = valueAxis.dim; + var valueDim = data.mapDimension(valueAxisDim); + var baseDim = data.mapDimension(baseAxisDim); + var baseDataOffset = valueAxisDim === 'x' || valueAxisDim === 'radius' ? 1 : 0; + + var dims = map(coordSys.dimensions, function (coordDim) { + return data.mapDimension(coordDim); + }); + + var stacked; + var stackResultDim = data.getCalculationInfo('stackResultDimension'); + if (stacked |= isDimensionStacked(data, dims[0] /*, dims[1]*/)) { // jshint ignore:line + dims[0] = stackResultDim; + } + if (stacked |= isDimensionStacked(data, dims[1] /*, dims[0]*/)) { // jshint ignore:line + dims[1] = stackResultDim; + } + + return { + dataDimsForPoint: dims, + valueStart: valueStart, + valueAxisDim: valueAxisDim, + baseAxisDim: baseAxisDim, + stacked: !!stacked, + valueDim: valueDim, + baseDim: baseDim, + baseDataOffset: baseDataOffset, + stackedOverDimension: data.getCalculationInfo('stackedOverDimension') + }; +} + +function getValueStart(valueAxis, valueOrigin) { + var valueStart = 0; + var extent = valueAxis.scale.getExtent(); + + if (valueOrigin === 'start') { + valueStart = extent[0]; + } + else if (valueOrigin === 'end') { + valueStart = extent[1]; + } + // auto + else { + // Both positive + if (extent[0] > 0) { + valueStart = extent[0]; + } + // Both negative + else if (extent[1] < 0) { + valueStart = extent[1]; + } + // If is one positive, and one negative, onZero shall be true + } + + return valueStart; +} + +function getStackedOnPoint(dataCoordInfo, coordSys, data, idx) { + var value = NaN; + if (dataCoordInfo.stacked) { + value = data.get(data.getCalculationInfo('stackedOverDimension'), idx); + } + if (isNaN(value)) { + value = dataCoordInfo.valueStart; + } + + var baseDataOffset = dataCoordInfo.baseDataOffset; + var stackedData = []; + stackedData[baseDataOffset] = data.get(dataCoordInfo.baseDim, idx); + stackedData[1 - baseDataOffset] = value; + + return coordSys.dataToPoint(stackedData); +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +// var arrayDiff = require('zrender/src/core/arrayDiff'); +// 'zrender/src/core/arrayDiff' has been used before, but it did +// not do well in performance when roam with fixed dataZoom window. + +// 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; +} + +var lineAnimationDiff = function ( + oldData, newData, + oldStackedOnPoints, newStackedOnPoints, + oldCoordSys, newCoordSys, + oldValueOrigin, newValueOrigin +) { + 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 newDataOldCoordInfo = prepareDataCoordInfo(oldCoordSys, newData, oldValueOrigin); + var oldDataNewCoordInfo = prepareDataCoordInfo(newCoordSys, oldData, newValueOrigin); + + 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(newDataOldCoordInfo.dataDimsForPoint[0], idx), + newData.get(newDataOldCoordInfo.dataDimsForPoint[1], idx) + ]) + ); + + nextPoints.push(newData.getItemLayout(idx).slice()); + + currStackedPoints.push( + getStackedOnPoint(newDataOldCoordInfo, 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(oldDataNewCoordInfo.dataDimsForPoint[0], idx), + oldData.get(oldDataNewCoordInfo.dataDimsForPoint[1], idx) + ])); + + currStackedPoints.push(oldStackedOnPoints[idx]); + nextStackedPoints.push( + getStackedOnPoint(oldDataNewCoordInfo, 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 + }; +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +// Poly path support NaN point + +var vec2Min = min; +var vec2Max = max; + +var scaleAndAdd$1 = scaleAndAdd; +var v2Copy = 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 +) { + // if (smoothMonotone == null) { + // if (isMono(points, 'x')) { + // return drawMono(ctx, points, start, segLen, allLen, + // dir, smoothMin, smoothMax, smooth, 'x', connectNulls); + // } + // else if (isMono(points, 'y')) { + // return drawMono(ctx, points, start, segLen, allLen, + // dir, smoothMin, smoothMax, smooth, 'y', connectNulls); + // } + // else { + // return drawNonMono.apply(this, arguments); + // } + // } + // else if (smoothMonotone !== 'none' && isMono(points, smoothMonotone)) { + // return drawMono.apply(this, arguments); + // } + // else { + // return drawNonMono.apply(this, arguments); + // } + if (smoothMonotone === 'none' || !smoothMonotone) { + return drawNonMono.apply(this, arguments); + } + else { + return drawMono.apply(this, arguments); + } +} + +/** + * Check if points is in monotone. + * + * @param {number[][]} points Array of points which is in [x, y] form + * @param {string} smoothMonotone 'x', 'y', or 'none', stating for which + * dimension that is checking. + * If is 'none', `drawNonMono` should be + * called. + * If is undefined, either being monotone + * in 'x' or 'y' will call `drawMono`. + */ +// function isMono(points, smoothMonotone) { +// if (points.length <= 1) { +// return true; +// } + +// var dim = smoothMonotone === 'x' ? 0 : 1; +// var last = points[0][dim]; +// var lastDiff = 0; +// for (var i = 1; i < points.length; ++i) { +// var diff = points[i][dim] - last; +// if (!isNaN(diff) && !isNaN(lastDiff) +// && diff !== 0 && lastDiff !== 0 +// && ((diff >= 0) !== (lastDiff >= 0)) +// ) { +// return false; +// } +// if (!isNaN(diff) && diff !== 0) { +// lastDiff = diff; +// last = points[i][dim]; +// } +// } +// return true; +// } + +/** + * Draw smoothed line in monotone, in which only vertical or horizontal bezier + * control points will be used. This should be used when points are monotone + * either in x or y dimension. + */ +function drawMono( + 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]); + } + else { + if (smooth > 0) { + var prevP = points[prevIdx]; + var dim = smoothMonotone === 'y' ? 1 : 0; + + // Length of control point to p, either in x or y, but not both + var ctrlLen = (p[dim] - prevP[dim]) * smooth; + + v2Copy(cp0, prevP); + cp0[dim] = prevP[dim] + ctrlLen; + + v2Copy(cp1, p); + cp1[dim] = p[dim] - ctrlLen; + + ctx.bezierCurveTo( + cp0[0], cp0[1], + cp1[0], cp1[1], + p[0], p[1] + ); + } + else { + ctx.lineTo(p[0], p[1]); + } + } + + prevIdx = idx; + idx += dir; + } + + return k; +} + +/** + * Draw smoothed line in non-monotone, in may cause undesired curve in extreme + * situations. This should be used when points are non-monotone neither in x or + * y dimension. + */ +function drawNonMono( + 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; + } + + 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 = dist(p, prevP); + lenNextSeg = dist(p, nextP); + } + + // Use ratio of seg length + ratioNextSeg = lenNextSeg / (lenNextSeg + lenPrevSeg); + + scaleAndAdd$1(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$1(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 + }; +} + +var Polyline$1 = Path.extend({ + + type: 'ec-polyline', + + shape: { + points: [], + + smooth: 0, + + smoothConstraint: true, + + smoothMonotone: null, + + connectNulls: false + }, + + style: { + fill: null, + + stroke: '#000' + }, + + brush: fixClipWithShadow(Path.prototype.brush), + + buildPath: function (ctx, shape) { + var points = shape.points; + + var i = 0; + var len$$1 = 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$$1 > 0; len$$1--) { + if (!isPointNull(points[len$$1 - 1])) { + break; + } + } + for (; i < len$$1; i++) { + if (!isPointNull(points[i])) { + break; + } + } + } + while (i < len$$1) { + i += drawSegment( + ctx, points, i, len$$1, len$$1, + 1, result.min, result.max, shape.smooth, + shape.smoothMonotone, shape.connectNulls + ) + 1; + } + } +}); + +var Polygon$1 = 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 + }, + + brush: fixClipWithShadow(Path.prototype.brush), + + buildPath: function (ctx, shape) { + var points = shape.points; + var stackedOnPoints = shape.stackedOnPoints; + + var i = 0; + var len$$1 = 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$$1 > 0; len$$1--) { + if (!isPointNull(points[len$$1 - 1])) { + break; + } + } + for (; i < len$$1; i++) { + if (!isPointNull(points[i])) { + break; + } + } + } + while (i < len$$1) { + var k = drawSegment( + ctx, points, i, len$$1, len$$1, + 1, bbox.min, bbox.max, shape.smooth, + smoothMonotone, shape.connectNulls + ); + drawSegment( + ctx, stackedOnPoints, i + k - 1, k, len$$1, + -1, stackedOnBBox.min, stackedOnBBox.max, shape.stackedOnSmooth, + smoothMonotone, shape.connectNulls + ); + i += k + 1; + + ctx.closePath(); + } + } +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +// FIXME step not support polar + +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.5 : 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; +} + +/** + * @param {module:echarts/coord/cartesian/Cartesian2D|module:echarts/coord/polar/Polar} coordSys + * @param {module:echarts/data/List} data + * @param {Object} dataCoordInfo + * @param {Array.>} points + */ +function getStackedOnPoints(coordSys, data, dataCoordInfo) { + if (!dataCoordInfo.valueDim) { + return []; + } + + var points = []; + for (var idx = 0, len = data.count(); idx < len; idx++) { + points.push(getStackedOnPoint(dataCoordInfo, coordSys, data, idx)); + } + + return points; +} + +function createGridClipShape(cartesian, hasAnimation, forSymbol, 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; + + // Avoid float number rounding error for symbol on the edge of axis extent. + // See #7913 and `test/dataZoom-clip.html`. + if (forSymbol) { + x -= 0.5; + width += 0.5; + y -= 0.5; + height += 0.5; + } + else { + var lineWidth = seriesModel.get('lineStyle.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 Rect({ + shape: { + x: x, + y: y, + width: width, + height: height + } + }); + + if (hasAnimation) { + clipPath.shape[isHorizontal ? 'width' : 'height'] = 0; + initProps(clipPath, { + shape: { + width: width, + height: height + } + }, seriesModel); + } + + return clipPath; +} + +function createPolarClipShape(polar, hasAnimation, forSymbol, seriesModel) { + var angleAxis = polar.getAngleAxis(); + var radiusAxis = polar.getRadiusAxis(); + + var radiusExtent = radiusAxis.getExtent().slice(); + radiusExtent[0] > radiusExtent[1] && radiusExtent.reverse(); + var angleExtent = angleAxis.getExtent(); + + var RADIAN = Math.PI / 180; + + // Avoid float number rounding error for symbol on the edge of axis extent. + if (forSymbol) { + radiusExtent[0] -= 0.5; + radiusExtent[1] += 0.5; + } + + var clipPath = new Sector({ + shape: { + cx: round$1(polar.cx, 1), + cy: round$1(polar.cy, 1), + r0: round$1(radiusExtent[0], 1), + r: round$1(radiusExtent[1], 1), + startAngle: -angleExtent[0] * RADIAN, + endAngle: -angleExtent[1] * RADIAN, + clockwise: angleAxis.inverse + } + }); + + if (hasAnimation) { + clipPath.shape.endAngle = -angleExtent[0] * RADIAN; + initProps(clipPath, { + shape: { + endAngle: -angleExtent[1] * RADIAN + } + }, seriesModel); + } + + return clipPath; +} + +function createClipShape(coordSys, hasAnimation, forSymbol, seriesModel) { + return coordSys.type === 'polar' + ? createPolarClipShape(coordSys, hasAnimation, forSymbol, seriesModel) + : createGridClipShape(coordSys, hasAnimation, forSymbol, 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 getVisualGradient(data, coordSys) { + var visualMetaList = data.getVisual('visualMeta'); + if (!visualMetaList || !visualMetaList.length || !data.count()) { + // When data.count() is 0, gradient range can not be calculated. + return; + } + + if (coordSys.type !== 'cartesian2d') { + if (__DEV__) { + console.warn('Visual map on line style is only supported on cartesian2d.'); + } + return; + } + + var coordDim; + var visualMeta; + + for (var i = visualMetaList.length - 1; i >= 0; i--) { + var dimIndex = visualMetaList[i].dimension; + var dimName = data.dimensions[dimIndex]; + var dimInfo = data.getDimensionInfo(dimName); + coordDim = dimInfo && dimInfo.coordDim; + // Can only be x or y + if (coordDim === 'x' || coordDim === 'y') { + visualMeta = visualMetaList[i]; + break; + } + } + + if (!visualMeta) { + if (__DEV__) { + console.warn('Visual map on line style only support x or y dimension.'); + } + return; + } + + // If the area to be rendered is bigger than area defined by LinearGradient, + // the canvas spec prescribes that the color of the first stop and the last + // stop should be used. But if two stops are added at offset 0, in effect + // browsers use the color of the second stop to render area outside + // LinearGradient. So we can only infinitesimally extend area defined in + // LinearGradient to render `outerColors`. + + var axis = coordSys.getAxis(coordDim); + + // dataToCoor mapping may not be linear, but must be monotonic. + var colorStops = map(visualMeta.stops, function (stop) { + return { + coord: axis.toGlobalCoord(axis.dataToCoord(stop.value)), + color: stop.color + }; + }); + var stopLen = colorStops.length; + var outerColors = visualMeta.outerColors.slice(); + + if (stopLen && colorStops[0].coord > colorStops[stopLen - 1].coord) { + colorStops.reverse(); + outerColors.reverse(); + } + + var tinyExtent = 10; // Arbitrary value: 10px + var minCoord = colorStops[0].coord - tinyExtent; + var maxCoord = colorStops[stopLen - 1].coord + tinyExtent; + var coordSpan = maxCoord - minCoord; + + if (coordSpan < 1e-3) { + return 'transparent'; + } + + each$1(colorStops, function (stop) { + stop.offset = (stop.coord - minCoord) / coordSpan; + }); + colorStops.push({ + offset: stopLen ? colorStops[stopLen - 1].offset : 0.5, + color: outerColors[1] || 'transparent' + }); + colorStops.unshift({ // notice colorStops.length have been changed. + offset: stopLen ? colorStops[0].offset : 0.5, + color: outerColors[0] || 'transparent' + }); + + // 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); + // }); + + var gradient = new LinearGradient(0, 0, 0, 0, colorStops, true); + gradient[coordDim] = minCoord; + gradient[coordDim + '2'] = maxCoord; + + return gradient; +} + +function getIsIgnoreFunc(seriesModel, data, coordSys) { + var showAllSymbol = seriesModel.get('showAllSymbol'); + var isAuto = showAllSymbol === 'auto'; + + if (showAllSymbol && !isAuto) { + return; + } + + var categoryAxis = coordSys.getAxesByScale('ordinal')[0]; + if (!categoryAxis) { + return; + } + + // Note that category label interval strategy might bring some weird effect + // in some scenario: users may wonder why some of the symbols are not + // displayed. So we show all symbols as possible as we can. + if (isAuto + // Simplify the logic, do not determine label overlap here. + && canShowAllSymbolForCategory(categoryAxis, data) + ) { + return; + } + + // Otherwise follow the label interval strategy on category axis. + var categoryDataDim = data.mapDimension(categoryAxis.dim); + var labelMap = {}; + + each$1(categoryAxis.getViewLabels(), function (labelItem) { + labelMap[labelItem.tickValue] = 1; + }); + + return function (dataIndex) { + return !labelMap.hasOwnProperty(data.get(categoryDataDim, dataIndex)); + }; +} + +function canShowAllSymbolForCategory(categoryAxis, data) { + // In mose cases, line is monotonous on category axis, and the label size + // is close with each other. So we check the symbol size and some of the + // label size alone with the category axis to estimate whether all symbol + // can be shown without overlap. + var axisExtent = categoryAxis.getExtent(); + var availSize = Math.abs(axisExtent[1] - axisExtent[0]) / categoryAxis.scale.count(); + isNaN(availSize) && (availSize = 0); // 0/0 is NaN. + + // Sampling some points, max 5. + var dataLen = data.count(); + var step = Math.max(1, Math.round(dataLen / 5)); + for (var dataIndex = 0; dataIndex < dataLen; dataIndex += step) { + if (SymbolClz$1.getSymbolSize( + data, dataIndex + // Only for cartesian, where `isHorizontal` exists. + )[categoryAxis.isHorizontal() ? 1 : 0] + // Empirical number + * 1.5 > availSize + ) { + return false; + } + } + + return true; +} + +Chart.extend({ + + type: 'line', + + init: function () { + var lineGroup = new 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'); + var areaStyleModel = seriesModel.getModel('areaStyle'); + + var points = data.mapArray(data.getItemLayout); + + 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 valueOrigin = areaStyleModel.get('origin'); + var dataCoordInfo = prepareDataCoordInfo(coordSys, data, valueOrigin); + + var stackedOnPoints = getStackedOnPoints(coordSys, data, dataCoordInfo); + + var showSymbol = seriesModel.get('showSymbol'); + + var isIgnoreFunc = showSymbol && !isCoordSysPolar + && getIsIgnoreFunc(seriesModel, 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, { + isIgnore: isIgnoreFunc, + clipShape: createClipShape(coordSys, false, true, seriesModel) + }); + + 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, false, 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, false, seriesModel)); + + // Always update, or it is wrong in the case turning on legend + // because points are not changed + showSymbol && symbolDraw.updateData(data, { + isIgnore: isIgnoreFunc, + clipShape: createClipShape(coordSys, false, true, seriesModel) + }); + + // 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, valueOrigin + ); + } + 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(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 stackedOnSeries = data.getCalculationInfo('stackedOnSeries'); + var stackedOnSmooth = 0; + + polygon.useStyle(defaults( + areaStyleModel.getAreaStyle(), + { + fill: visualColor, + opacity: 0.7, + lineJoin: 'bevel' + } + )); + + if (stackedOnSeries) { + 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; + this._valueOrigin = valueOrigin; + }, + + dispose: function () {}, + + 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); + if (!pt) { + // Null data + return; + } + symbol = new SymbolClz$1(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 + Chart.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 { + // FIXME + // can not downplay completely. + // Downplay whole series + Chart.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 Polyline$1({ + 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 Polygon$1({ + shape: { + points: points, + stackedOnPoints: stackedOnPoints + }, + silent: true + }); + + this._lineGroup.add(polygon); + + this._polygon = polygon; + return polygon; + }, + + /** + * @private + */ + // FIXME Two value axis + _updateAnimation: function (data, stackedOnPoints, coordSys, api, step, valueOrigin) { + var polyline = this._polyline; + var polygon = this._polygon; + var seriesModel = data.hostModel; + + var diff = lineAnimationDiff( + this._data, data, + this._stackedOnPoints, stackedOnPoints, + this._coordSys, coordSys, + this._valueOrigin, valueOrigin + ); + + 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); + } + // `diff.current` is subset of `current` (which should be ensured by + // turnPointsIntoStep), so points in `__points` can be updated when + // points in `current` are update during animation. + polyline.shape.__points = diff.current; + polyline.shape.points = current; + + updateProps(polyline, { + shape: { + points: next + } + }, seriesModel); + + if (polygon) { + polygon.setShape({ + points: current, + stackedOnPoints: stackedOnCurrent + }); + updateProps(polygon, { + shape: { + points: next, + stackedOnPoints: stackedOnNext + } + }, 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; + } +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + + +var visualSymbol = function (seriesType, defaultSymbolType, legendSymbol) { + // Encoding visual for all series include which is filtered for legend drawing + return { + seriesType: seriesType, + + // For legend. + performRawSeries: true, + + reset: function (seriesModel, ecModel, api) { + var data = seriesModel.getData(); + + var symbolType = seriesModel.get('symbol') || defaultSymbolType; + var symbolSize = seriesModel.get('symbolSize'); + var keepAspect = seriesModel.get('symbolKeepAspect'); + + data.setVisual({ + legendSymbol: legendSymbol || symbolType, + symbol: symbolType, + symbolSize: symbolSize, + symbolKeepAspect: keepAspect + }); + + // Only visible series has each data be visual encoded + if (ecModel.isSeriesFiltered(seriesModel)) { + return; + } + + var hasCallback = typeof symbolSize === 'function'; + + function dataEach(data, idx) { + if (typeof symbolSize === 'function') { + var rawValue = seriesModel.getRawValue(idx); + // FIXME + var params = seriesModel.getDataParams(idx); + data.setItemVisual(idx, 'symbolSize', symbolSize(rawValue, params)); + } + + if (data.hasItemOption) { + var itemModel = data.getItemModel(idx); + var itemSymbolType = itemModel.getShallow('symbol', true); + var itemSymbolSize = itemModel.getShallow('symbolSize', + true); + var itemSymbolKeepAspect = + itemModel.getShallow('symbolKeepAspect',true); + + // If has item symbol + if (itemSymbolType != null) { + data.setItemVisual(idx, 'symbol', itemSymbolType); + } + if (itemSymbolSize != null) { + // PENDING Transform symbolSize ? + data.setItemVisual(idx, 'symbolSize', itemSymbolSize); + } + if (itemSymbolKeepAspect != null) { + data.setItemVisual(idx, 'symbolKeepAspect', + itemSymbolKeepAspect); + } + } + } + + return { dataEach: (data.hasItemOption || hasCallback) ? dataEach : null }; + } + }; +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var pointsLayout = function (seriesType) { + return { + seriesType: seriesType, + + plan: createRenderPlanner(), + + reset: function (seriesModel) { + var data = seriesModel.getData(); + var coordSys = seriesModel.coordinateSystem; + var pipelineContext = seriesModel.pipelineContext; + var isLargeRender = pipelineContext.large; + + if (!coordSys) { + return; + } + + var dims = map(coordSys.dimensions, function (dim) { + return data.mapDimension(dim); + }).slice(0, 2); + var dimLen = dims.length; + + var stackResultDim = data.getCalculationInfo('stackResultDimension'); + if (isDimensionStacked(data, dims[0] /*, dims[1]*/)) { + dims[0] = stackResultDim; + } + if (isDimensionStacked(data, dims[1] /*, dims[0]*/)) { + dims[1] = stackResultDim; + } + + function progress(params, data) { + var segCount = params.end - params.start; + var points = isLargeRender && new Float32Array(segCount * dimLen); + + for (var i = params.start, offset = 0, tmpIn = [], tmpOut = []; i < params.end; i++) { + var point; + + if (dimLen === 1) { + var x = data.get(dims[0], i); + point = !isNaN(x) && coordSys.dataToPoint(x, null, tmpOut); + } + else { + var x = tmpIn[0] = data.get(dims[0], i); + var y = tmpIn[1] = data.get(dims[1], i); + // Also {Array.}, not undefined to avoid if...else... statement + point = !isNaN(x) && !isNaN(y) && coordSys.dataToPoint(tmpIn, null, tmpOut); + } + + if (isLargeRender) { + points[offset++] = point ? point[0] : NaN; + points[offset++] = point ? point[1] : NaN; + } + else { + data.setItemLayout(i, (point && point.slice()) || [NaN, NaN]); + } + } + + isLargeRender && data.setLayout('symbolPoints', points); + } + + return dimLen && {progress: progress}; + } + }; +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + + +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]); + } + // NaN will cause illegal axis extent. + return isFinite(max) ? max : NaN; + }, + min: function (frame) { + var min = Infinity; + for (var i = 0; i < frame.length; i++) { + frame[i] < min && (min = frame[i]); + } + // NaN will cause illegal axis extent. + return isFinite(min) ? min : NaN; + }, + // TODO + // Median + nearest: function (frame) { + return frame[0]; + } +}; + +var indexSampler = function (frame, value) { + return Math.round(frame.length / 2); +}; + +var dataSample = function (seriesType) { + return { + + seriesType: seriesType, + + modifyOutputEnd: true, + + reset: function (seriesModel, ecModel, api) { + 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) { + // Only support sample the first dim mapped from value axis. + seriesModel.setData(data.downSample( + data.mapDimension(valueAxis.dim), 1 / rate, sampler, indexSampler + )); + } + } + } + } + }; +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * Cartesian coordinate system + * @module echarts/coord/Cartesian + * + */ + +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 map(this._dimList, dimAxisMapper, this); + }, + + /** + * Get axes list by given scale type + */ + getAxesByScale: function (scaleType) { + scaleType = scaleType.toLowerCase(); + return 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; + } +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + + +function 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]); + }, + + /** + * @param {Array.} data + * @param {Array.} out + * @return {Array.} + */ + dataToPoint: function (data, reserved, out) { + var xAxis = this.getAxis('x'); + var yAxis = this.getAxis('y'); + out = out || []; + out[0] = xAxis.toGlobalCoord(xAxis.dataToCoord(data[0])); + out[1] = yAxis.toGlobalCoord(yAxis.dataToCoord(data[1])); + return out; + }, + + /** + * @param {Array.} data + * @param {Array.} out + * @return {Array.} + */ + clampData: function (data, out) { + var xScale = this.getAxis('x').scale; + var yScale = this.getAxis('y').scale; + var xAxisExtent = xScale.getExtent(); + var yAxisExtent = yScale.getExtent(); + var x = xScale.parse(data[0]); + var y = yScale.parse(data[1]); + out = out || []; + out[0] = Math.min( + Math.max(Math.min(xAxisExtent[0], xAxisExtent[1]), x), + Math.max(xAxisExtent[0], xAxisExtent[1]) + ); + out[1] = Math.min( + Math.max(Math.min(yAxisExtent[0], yAxisExtent[1]), y), + Math.max(yAxisExtent[0], yAxisExtent[1]) + ); + + return out; + }, + + /** + * @param {Array.} point + * @param {Array.} out + * @return {Array.} + */ + pointToData: function (point, out) { + var xAxis = this.getAxis('x'); + var yAxis = this.getAxis('y'); + out = out || []; + out[0] = xAxis.coordToData(xAxis.toLocalCoord(point[0])); + out[1] = yAxis.coordToData(yAxis.toLocalCoord(point[1])); + return out; + }, + + /** + * Get other axis + * @param {module:echarts/coord/cartesian/Axis2D} axis + */ + getOtherAxis: function (axis) { + return this.getAxis(axis.dim === 'x' ? 'y' : 'x'); + } + +}; + +inherits(Cartesian2D, Cartesian); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * 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, + + /** + * Implemented in . + * @return {Array.} + * If not on zero of other axis, return null/undefined. + * If no axes, return an empty array. + */ + getAxesOnZeroOf: null, + + /** + * Axis model + * @param {module:echarts/coord/cartesian/AxisModel} + */ + model: null, + + isHorizontal: function () { + var position = this.position; + return position === 'top' || position === 'bottom'; + }, + + /** + * Each item cooresponds to this.getExtent(), which + * means globalExtent[0] may greater than globalExtent[1], + * unless `asc` is input. + * + * @param {boolean} [asc] + * @return {Array.} + */ + getGlobalExtent: function (asc) { + var ret = this.getExtent(); + ret[0] = this.toGlobalCoord(ret[0]); + ret[1] = this.toGlobalCoord(ret[1]); + asc && ret[0] > ret[1] && ret.reverse(); + return ret; + }, + + getOtherAxis: function () { + this.grid.getOtherAxis(); + }, + + /** + * @override + */ + pointToData: function (point, clamp) { + return this.coordToData(this.toLocalCoord(point[this.dim === 'x' ? 0 : 1]), clamp); + }, + + /** + * 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 + +}; + +inherits(Axis2D, Axis); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var defaultOption = { + show: true, + zlevel: 0, + z: 0, + // Inverse the axis. + inverse: false, + + // Axis name displayed. + name: '', + // 'start' | 'middle' | 'end' + nameLocation: 'end', + // By degree. By defualt auto rotate by nameLocation. + nameRotate: null, + nameTruncate: { + maxWidth: null, + ellipsis: '...', + placeholder: '.' + }, + // Use global text style by default. + nameTextStyle: {}, + // The gap between axisName and axisLine. + nameGap: 15, + + // Default `false` to support tooltip. + silent: false, + // Default `false` to avoid legacy user event listener fail. + triggerEvent: false, + + tooltip: { + show: false + }, + + axisPointer: {}, + + axisLine: { + show: true, + onZero: true, + onZeroAxisIndex: null, + lineStyle: { + color: '#333', + width: 1, + type: 'solid' + }, + // The arrow at both ends the the axis. + symbol: ['none', 'none'], + symbolSize: [10, 15] + }, + axisTick: { + show: true, + // Whether axisTick is inside the grid or outside the grid. + inside: false, + // The length of axisTick. + length: 5, + lineStyle: { + width: 1 + } + }, + axisLabel: { + show: true, + // Whether axisLabel is inside the grid or outside the grid. + inside: false, + rotate: 0, + // true | false | null/undefined (auto) + showMinLabel: null, + // true | false | null/undefined (auto) + showMaxLabel: null, + margin: 8, + // formatter: null, + fontSize: 12 + }, + splitLine: { + show: true, + lineStyle: { + color: ['#ccc'], + width: 1, + type: 'solid' + } + }, + splitArea: { + show: false, + areaStyle: { + color: ['rgba(250,250,250,0.3)','rgba(200,200,200,0.3)'] + } + } +}; + +var axisDefault = {}; + +axisDefault.categoryAxis = merge({ + // The gap at both ends of the axis. For categoryAxis, boolean. + boundaryGap: true, + // Set false to faster category collection. + // Only usefull in the case like: category is + // ['2012-01-01', '2012-01-02', ...], where the input + // data has been ensured not duplicate and is large data. + // null means "auto": + // if axis.data provided, do not deduplication, + // else do deduplication. + deduplication: null, + // splitArea: { + // show: false + // }, + splitLine: { + show: false + }, + axisTick: { + // If tick is align with label when boundaryGap is true + alignWithLabel: false, + interval: 'auto' + }, + axisLabel: { + interval: 'auto' + } +}, defaultOption); + +axisDefault.valueAxis = merge({ + // The gap at both ends of the axis. For value axis, [GAP, GAP], where + // `GAP` can be an absolute pixel number (like `35`), or percent (like `'30%'`) + boundaryGap: [0, 0], + + // TODO + // min/max: [30, datamin, 60] or [20, datamin] or [datamin, 60] + + // Min value of the axis. can be: + // + a number + // + 'dataMin': use the min value in data. + // + null/undefined: auto decide min value (consider pretty look and boundaryGap). + // min: null, + + // Max value of the axis. can be: + // + a number + // + 'dataMax': use the max value in data. + // + null/undefined: auto decide max value (consider pretty look and boundaryGap). + // 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 + + // Optional value can be: + // + `false`: always include value 0. + // + `true`: the extent do not consider value 0. + // scale: false, + + // AxisTick and axisLabel and splitLine are caculated based on splitNumber. + splitNumber: 5 + + // Interval specifies the span of the ticks is mandatorily. + // interval: null + + // Specify min interval when auto calculate tick interval. + // minInterval: null + + // Specify max interval when auto calculate tick interval. + // maxInterval: null + +}, defaultOption); + +axisDefault.timeAxis = defaults({ + scale: true, + min: 'dataMin', + max: 'dataMax' +}, axisDefault.valueAxis); + +axisDefault.logAxis = defaults({ + scale: true, + logBase: 10 +}, axisDefault.valueAxis); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +// 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] + */ +var axisModelCreator = function (axisName, BaseAxisModelClass, axisTypeDefaulter, extraDefaultOption) { + + each$1(AXIS_TYPES, function (axisType) { + + BaseAxisModelClass.extend({ + + /** + * @readOnly + */ + type: axisName + 'Axis.' + axisType, + + mergeDefaultAndTheme: function (option, ecModel) { + var layoutMode = this.layoutMode; + var inputPositionParams = layoutMode + ? getLayoutParams(option) : {}; + + var themeModel = ecModel.getTheme(); + merge(option, themeModel.get(axisType + 'Axis')); + merge(option, this.getDefaultOption()); + + option.type = axisTypeDefaulter(axisName, option); + + if (layoutMode) { + mergeLayoutParam(option, inputPositionParams, layoutMode); + } + }, + + /** + * @override + */ + optionUpdated: function () { + var thisOption = this.option; + if (thisOption.type === 'category') { + this.__ordinalMeta = OrdinalMeta.createByAxisModel(this); + } + }, + + /** + * Should not be called before all of 'getInitailData' finished. + * Because categories are collected during initializing data. + */ + getCategories: function (rawData) { + var option = this.option; + // FIXME + // warning if called before all of 'getInitailData' finished. + if (option.type === 'category') { + if (rawData) { + return option.data; + } + return this.__ordinalMeta.categories; + } + }, + + getOrdinalMeta: function () { + return this.__ordinalMeta; + }, + + defaultOption: mergeAll( + [ + {}, + axisDefault[axisType + 'Axis'], + extraDefaultOption + ], + true + ) + }); + }); + + ComponentModel.registerSubTypeDefaulter( + axisName + 'Axis', + curry(axisTypeDefaulter, axisName) + ); +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +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(); + }, + + /** + * @override + * @return {module:echarts/model/Component} + */ + getCoordSysModel: function () { + return this.ecModel.queryComponents({ + mainType: 'grid', + index: this.option.gridIndex, + id: this.option.gridId + })[0]; + } + +}); + +function getAxisType(axisDim, option) { + // Default axis with data is category axis + return option.type || (option.data ? 'category' : 'value'); +} + +merge(AxisModel.prototype, axisModelCommonMixin); + +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); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +// Grid 是在有直角坐标系的时候必须要存在的 +// 所以这里也要被 Cartesian2D 依赖 + +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' + } +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * Grid is a region which contains at most 4 cartesian systems + * + * TODO Default cartesian + */ + +// Depends on GridModel, AxisModel, which performs preprocess. +/** + * Check if the axis is used in the specified grid + * @inner + */ +function isAxisUsedInTheGrid(axisModel, gridModel, ecModel) { + return axisModel.getCoordSysModel() === gridModel; +} + +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.axisPointerEnabled = true; + +gridProto.getRect = function () { + return this._rect; +}; + +gridProto.update = function (ecModel, api) { + + var axesMap = this._axesMap; + + this._updateScale(ecModel, this.model); + + each$1(axesMap.x, function (xAxis) { + niceScaleExtent(xAxis.scale, xAxis.model); + }); + each$1(axesMap.y, function (yAxis) { + niceScaleExtent(yAxis.scale, yAxis.model); + }); + each$1(axesMap.x, function (xAxis) { + fixAxisOnZero(axesMap, 'y', xAxis); + }); + each$1(axesMap.y, function (yAxis) { + fixAxisOnZero(axesMap, 'x', yAxis); + }); + + // Resize again if containLabel is enabled + // FIXME It may cause getting wrong grid size in data processing stage + this.resize(this.model, api); +}; + +function fixAxisOnZero(axesMap, otherAxisDim, axis) { + + axis.getAxesOnZeroOf = function () { + // TODO: onZero of multiple axes. + return otherAxis ? [otherAxis] : []; + }; + + // onZero can not be enabled in these two situations: + // 1. When any other axis is a category axis. + // 2. When no axis is cross 0 point. + var otherAxes = axesMap[otherAxisDim]; + + var otherAxis; + var axisModel = axis.model; + var onZero = axisModel.get('axisLine.onZero'); + var onZeroAxisIndex = axisModel.get('axisLine.onZeroAxisIndex'); + + if (!onZero) { + return; + } + + // If target axis is specified. + if (onZeroAxisIndex != null) { + if (canOnZeroToAxis(otherAxes[onZeroAxisIndex])) { + otherAxis = otherAxes[onZeroAxisIndex]; + } + return; + } + + // Find the first available other axis. + for (var idx in otherAxes) { + if (otherAxes.hasOwnProperty(idx) && canOnZeroToAxis(otherAxes[idx])) { + otherAxis = otherAxes[idx]; + break; + } + } +} + +function canOnZeroToAxis(axis) { + return axis && axis.type !== 'category' && axis.type !== 'time' && ifAxisCrossZero(axis); +} + +/** + * Resize the grid + * @param {module:echarts/coord/cartesian/GridModel} gridModel + * @param {module:echarts/ExtensionAPI} api + */ +gridProto.resize = function (gridModel, api, ignoreContainLabel) { + + var gridRect = getLayoutRect( + gridModel.getBoxLayoutParams(), { + width: api.getWidth(), + height: api.getHeight() + }); + + this._rect = gridRect; + + var axesList = this._axesList; + + adjustAxes(); + + // Minus label size + if (!ignoreContainLabel && gridModel.get('containLabel')) { + each$1(axesList, function (axis) { + if (!axis.model.get('axisLabel.inside')) { + var labelUnionRect = estimateLabelUnionRect(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$1(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]); + updateAxisTransform(axis, isHorizontal ? gridRect.x : gridRect.y); + }); + } +}; + +/** + * @param {string} axisType + * @param {number} [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) { + if (axesMapOnDim.hasOwnProperty(name)) { + return axesMapOnDim[name]; + } + } + } + return axesMapOnDim[axisIndex]; + } +}; + +/** + * @return {Array.} + */ +gridProto.getAxes = function () { + return this._axesList.slice(); +}; + +/** + * Usage: + * grid.getCartesian(xAxisIndex, yAxisIndex); + * grid.getCartesian(xAxisIndex); + * grid.getCartesian(null, yAxisIndex); + * grid.getCartesian({xAxisIndex: ..., yAxisIndex: ...}); + * + * @param {number|Object} [xAxisIndex] + * @param {number} [yAxisIndex] + */ +gridProto.getCartesian = function (xAxisIndex, yAxisIndex) { + if (xAxisIndex != null && yAxisIndex != null) { + var key = 'x' + xAxisIndex + 'y' + yAxisIndex; + return this._coordsMap[key]; + } + + if (isObject$1(xAxisIndex)) { + yAxisIndex = xAxisIndex.yAxisIndex; + xAxisIndex = xAxisIndex.xAxisIndex; + } + // 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]; + } + } +}; + +gridProto.getCartesians = function () { + return this._coordsList.slice(); +}; + +/** + * @implements + * see {module:echarts/CoodinateSystem} + */ +gridProto.convertToPixel = function (ecModel, finder, value) { + var target = this._findConvertTarget(ecModel, finder); + + return target.cartesian + ? target.cartesian.dataToPoint(value) + : target.axis + ? target.axis.toGlobalCoord(target.axis.dataToCoord(value)) + : null; +}; + +/** + * @implements + * see {module:echarts/CoodinateSystem} + */ +gridProto.convertFromPixel = function (ecModel, finder, value) { + var target = this._findConvertTarget(ecModel, finder); + + return target.cartesian + ? target.cartesian.pointToData(value) + : target.axis + ? target.axis.coordToData(target.axis.toLocalCoord(value)) + : null; +}; + +/** + * @inner + */ +gridProto._findConvertTarget = function (ecModel, finder) { + var seriesModel = finder.seriesModel; + var xAxisModel = finder.xAxisModel + || (seriesModel && seriesModel.getReferringComponents('xAxis')[0]); + var yAxisModel = finder.yAxisModel + || (seriesModel && seriesModel.getReferringComponents('yAxis')[0]); + var gridModel = finder.gridModel; + var coordsList = this._coordsList; + var cartesian; + var axis; + + if (seriesModel) { + cartesian = seriesModel.coordinateSystem; + indexOf(coordsList, cartesian) < 0 && (cartesian = null); + } + else if (xAxisModel && yAxisModel) { + cartesian = this.getCartesian(xAxisModel.componentIndex, yAxisModel.componentIndex); + } + else if (xAxisModel) { + axis = this.getAxis('x', xAxisModel.componentIndex); + } + else if (yAxisModel) { + axis = this.getAxis('y', yAxisModel.componentIndex); + } + // Lowest priority. + else if (gridModel) { + var grid = gridModel.coordinateSystem; + if (grid === this) { + cartesian = this._coordsList[0]; + } + } + + return {cartesian: cartesian, axis: axis}; +}; + +/** + * @implements + * see {module:echarts/CoodinateSystem} + */ +gridProto.containPoint = function (point) { + var coord = this._coordsList[0]; + if (coord) { + return coord.containPoint(point); + } +}; + +/** + * 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$1(axesMap.x, function (xAxis, xAxisIndex) { + each$1(axesMap.y, function (yAxis, yAxisIndex) { + var key = 'x' + xAxisIndex + 'y' + yAxisIndex; + var cartesian = new Cartesian2D(key); + + cartesian.grid = this; + cartesian.model = gridModel; + + 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, createScaleByModel(axisModel), + [0, 0], + axisModel.get('type'), + axisPosition + ); + + 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; + + // 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 + each$1(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$1(data.mapDimension(axis.dim, true), function (dim) { + axis.scale.unionExtentFromData( + // For example, the extent of the orginal dimension + // is [0.1, 0.5], the extent of the `stackResultDimension` + // is [7, 9], the final extent should not include [0.1, 0.5]. + data, getStackedDimension(data, dim) + ); + }); + } +}; + +/** + * @param {string} [dim] 'x' or 'y' or 'auto' or null/undefined + * @return {Object} {baseAxes: [], otherAxes: []} + */ +gridProto.getTooltipAxes = function (dim) { + var baseAxes = []; + var otherAxes = []; + + each$1(this.getCartesians(), function (cartesian) { + var baseAxis = (dim != null && dim !== 'auto') + ? cartesian.getAxis(dim) : cartesian.getBaseAxis(); + var otherAxis = cartesian.getOtherAxis(baseAxis); + indexOf(baseAxes, baseAxis) < 0 && baseAxes.push(baseAxis); + indexOf(otherAxes, otherAxis) < 0 && otherAxes.push(otherAxis); + }); + + return {baseAxes: baseAxes, otherAxes: otherAxes}; +}; + +/** + * @inner + */ +function updateAxisTransform(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 map(axesTypes, function (axisType) { + var axisModel = seriesModel.getReferringComponents(axisType)[0]; + + if (__DEV__) { + if (!axisModel) { + throw new Error(axisType + ' "' + 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; + // dataSampling requires axis extent, so resize + // should be performed in create stage. + grid.resize(gridModel, api, true); + + 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.getCoordSysModel(); + + if (__DEV__) { + if (!gridModel) { + throw new Error( + 'Grid "' + retrieve( + xAxisModel.get('gridIndex'), + xAxisModel.get('gridId'), + 0 + ) + '" not found' + ); + } + if (xAxisModel.getCoordSysModel() !== yAxisModel.getCoordSysModel()) { + 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 = Grid.prototype.dimensions = Cartesian2D.prototype.dimensions; + +CoordinateSystemManager.register('cartesian2d', Grid); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var PI$2 = 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' or 'center'. + * @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.labelRotate] by degree, default get from axisModel. + * @param {number} [opt.strokeContainThreshold] Default label interval when label + * @param {number} [opt.nameTruncateMaxWidth] + */ +var AxisBuilder = function (axisModel, opt) { + + /** + * @readOnly + */ + this.opt = opt; + + /** + * @readOnly + */ + this.axisModel = axisModel; + + // Default value + defaults( + opt, + { + labelOffset: 0, + nameDirection: 1, + tickDirection: 1, + labelDirection: 1, + silent: true + } + ); + + /** + * @readOnly + */ + this.group = new Group(); + + // FIXME Not use a seperate text group? + var dumbGroup = new 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) { + applyTransform(pt1, pt1, matrix); + applyTransform(pt2, pt2, matrix); + } + + var lineStyle = extend( + { + lineCap: 'round' + }, + axisModel.getModel('axisLine.lineStyle').getLineStyle() + ); + + this.group.add(new Line(subPixelOptimizeLine({ + // Id for animation + anid: 'line', + + shape: { + x1: pt1[0], + y1: pt1[1], + x2: pt2[0], + y2: pt2[1] + }, + style: lineStyle, + strokeContainThreshold: opt.strokeContainThreshold || 5, + silent: true, + z2: 1 + }))); + + var arrows = axisModel.get('axisLine.symbol'); + var arrowSize = axisModel.get('axisLine.symbolSize'); + + var arrowOffset = axisModel.get('axisLine.symbolOffset') || 0; + if (typeof arrowOffset === 'number') { + arrowOffset = [arrowOffset, arrowOffset]; + } + + if (arrows != null) { + if (typeof arrows === 'string') { + // Use the same arrow for start and end point + arrows = [arrows, arrows]; + } + if (typeof arrowSize === 'string' + || typeof arrowSize === 'number' + ) { + // Use the same size for width and height + arrowSize = [arrowSize, arrowSize]; + } + + var symbolWidth = arrowSize[0]; + var symbolHeight = arrowSize[1]; + + each$1([{ + rotate: opt.rotation + Math.PI / 2, + offset: arrowOffset[0], + r: 0 + }, { + rotate: opt.rotation - Math.PI / 2, + offset: arrowOffset[1], + r: Math.sqrt((pt1[0] - pt2[0]) * (pt1[0] - pt2[0]) + + (pt1[1] - pt2[1]) * (pt1[1] - pt2[1])) + }], function (point, index) { + if (arrows[index] !== 'none' && arrows[index] != null) { + var symbol = createSymbol( + arrows[index], + -symbolWidth / 2, + -symbolHeight / 2, + symbolWidth, + symbolHeight, + lineStyle.stroke, + true + ); + + // Calculate arrow position with offset + var r = point.r + point.offset; + var pos = [ + pt1[0] + r * Math.cos(opt.rotation), + pt1[1] - r * Math.sin(opt.rotation) + ]; + + symbol.attr({ + rotation: point.rotate, + position: pos, + silent: true + }); + this.group.add(symbol); + } + }, this); + } + }, + + /** + * @private + */ + axisTickLabel: function () { + var axisModel = this.axisModel; + var opt = this.opt; + + var tickEls = buildAxisTick(this, axisModel, opt); + var labelEls = buildAxisLabel(this, axisModel, opt); + + fixMinMaxLabelShow(axisModel, labelEls, tickEls); + }, + + /** + * @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. + isNameLocationCenter(nameLocation) ? opt.labelOffset + nameDirection * gap : 0 + ]; + + var labelLayout; + + var nameRotation = axisModel.get('nameRotate'); + if (nameRotation != null) { + nameRotation = nameRotation * PI$2 / 180; // To radian. + } + + var axisNameAvailableWidth; + + if (isNameLocationCenter(nameLocation)) { + labelLayout = innerTextLayout( + opt.rotation, + 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( + opt.nameTruncateMaxWidth, truncateOpt.maxWidth, axisNameAvailableWidth + ); + // FIXME + // truncate rich text? (consider performance) + var truncatedText = (ellipsis != null && maxWidth != null) + ? truncateText$1( + 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 Text({ + // Id for animation + anid: 'name', + + __fullText: name, + __truncatedText: truncatedText, + + position: pos, + rotation: labelLayout.rotation, + silent: isSilent(axisModel), + z2: 1, + tooltip: (tooltipOpt && tooltipOpt.show) + ? extend({ + content: name, + formatter: function () { + return name; + }, + formatterParams: formatterParams + }, tooltipOpt) + : null + }); + + setTextStyle(textEl.style, textStyleModel, { + text: truncatedText, + textFont: textFont, + textFill: textStyleModel.getTextColor() + || axisModel.get('axisLine.lineStyle.color'), + textAlign: labelLayout.textAlign, + textVerticalAlign: labelLayout.textVerticalAlign + }); + + 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(); + } + +}; + +/** + * @public + * @static + * @param {Object} opt + * @param {number} axisRotation in radian + * @param {number} textRotation in radian + * @param {number} direction + * @return {Object} { + * rotation, // according to axis + * textAlign, + * textVerticalAlign + * } + */ +var innerTextLayout = AxisBuilder.innerTextLayout = function (axisRotation, textRotation, direction) { + var rotationDiff = remRadian(textRotation - axisRotation); + var textAlign; + var textVerticalAlign; + + if (isRadianAroundZero(rotationDiff)) { // Label is parallel with axis line. + textVerticalAlign = direction > 0 ? 'top' : 'bottom'; + textAlign = 'center'; + } + else if (isRadianAroundZero(rotationDiff - PI$2)) { // Label is inverse parallel with axis line. + textVerticalAlign = direction > 0 ? 'bottom' : 'top'; + textAlign = 'center'; + } + else { + textVerticalAlign = 'middle'; + + if (rotationDiff > 0 && rotationDiff < PI$2) { + textAlign = direction > 0 ? 'right' : 'left'; + } + else { + textAlign = direction > 0 ? 'left' : 'right'; + } + } + + return { + rotation: rotationDiff, + textAlign: textAlign, + textVerticalAlign: textVerticalAlign + }; +}; + +function endTextLayout(opt, textPosition, textRotate, extent) { + var rotationDiff = remRadian(textRotate - opt.rotation); + var textAlign; + var textVerticalAlign; + var inverse = extent[0] > extent[1]; + var onLeft = (textPosition === 'start' && !inverse) + || (textPosition !== 'start' && inverse); + + if (isRadianAroundZero(rotationDiff - PI$2 / 2)) { + textVerticalAlign = onLeft ? 'bottom' : 'top'; + textAlign = 'center'; + } + else if (isRadianAroundZero(rotationDiff - PI$2 * 1.5)) { + textVerticalAlign = onLeft ? 'top' : 'bottom'; + textAlign = 'center'; + } + else { + textVerticalAlign = 'middle'; + if (rotationDiff < PI$2 * 1.5 && rotationDiff > PI$2 / 2) { + textAlign = onLeft ? 'left' : 'right'; + } + else { + textAlign = onLeft ? 'right' : 'left'; + } + } + + return { + rotation: rotationDiff, + textAlign: textAlign, + textVerticalAlign: textVerticalAlign + }; +} + +function isSilent(axisModel) { + var tooltipOpt = axisModel.get('tooltip'); + return axisModel.get('silent') + // Consider mouse cursor, add these restrictions. + || !( + axisModel.get('triggerEvent') || (tooltipOpt && tooltipOpt.show) + ); +} + +function fixMinMaxLabelShow(axisModel, labelEls, tickEls) { + // 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 + var showMinLabel = axisModel.get('axisLabel.showMinLabel'); + var showMaxLabel = axisModel.get('axisLabel.showMaxLabel'); + + // FIXME + // Have not consider onBand yet, where tick els is more than label els. + + labelEls = labelEls || []; + tickEls = tickEls || []; + + var firstLabel = labelEls[0]; + var nextLabel = labelEls[1]; + var lastLabel = labelEls[labelEls.length - 1]; + var prevLabel = labelEls[labelEls.length - 2]; + + var firstTick = tickEls[0]; + var nextTick = tickEls[1]; + var lastTick = tickEls[tickEls.length - 1]; + var prevTick = tickEls[tickEls.length - 2]; + + if (showMinLabel === false) { + ignoreEl(firstLabel); + ignoreEl(firstTick); + } + else if (isTwoLabelOverlapped(firstLabel, nextLabel)) { + if (showMinLabel) { + ignoreEl(nextLabel); + ignoreEl(nextTick); + } + else { + ignoreEl(firstLabel); + ignoreEl(firstTick); + } + } + + if (showMaxLabel === false) { + ignoreEl(lastLabel); + ignoreEl(lastTick); + } + else if (isTwoLabelOverlapped(prevLabel, lastLabel)) { + if (showMaxLabel) { + ignoreEl(prevLabel); + ignoreEl(prevTick); + } + else { + ignoreEl(lastLabel); + ignoreEl(lastTick); + } + } +} + +function ignoreEl(el) { + el && (el.ignore = true); +} + +function isTwoLabelOverlapped(current, next, labelLayout) { + // current and next has the same rotation. + var firstRect = current && current.getBoundingRect().clone(); + var nextRect = next && next.getBoundingRect().clone(); + + if (!firstRect || !nextRect) { + return; + } + + // When checking intersect of two rotated labels, we use mRotationBack + // to avoid that boundingRect is enlarge when using `boundingRect.applyTransform`. + var mRotationBack = identity([]); + rotate(mRotationBack, mRotationBack, -current.rotation); + + firstRect.applyTransform(mul$1([], mRotationBack, current.getLocalTransform())); + nextRect.applyTransform(mul$1([], mRotationBack, next.getLocalTransform())); + + return firstRect.intersect(nextRect); +} + +function isNameLocationCenter(nameLocation) { + return nameLocation === 'middle' || nameLocation === 'center'; +} + +function buildAxisTick(axisBuilder, axisModel, opt) { + var axis = axisModel.axis; + + if (!axisModel.get('axisTick.show') || axis.scale.isBlank()) { + return; + } + + var tickModel = axisModel.getModel('axisTick'); + + var lineStyleModel = tickModel.getModel('lineStyle'); + var tickLen = tickModel.get('length'); + + var ticksCoords = axis.getTicksCoords(); + + var pt1 = []; + var pt2 = []; + var matrix = axisBuilder._transform; + + var tickEls = []; + + for (var i = 0; i < ticksCoords.length; i++) { + var tickCoord = ticksCoords[i].coord; + + pt1[0] = tickCoord; + pt1[1] = 0; + pt2[0] = tickCoord; + pt2[1] = opt.tickDirection * tickLen; + + if (matrix) { + applyTransform(pt1, pt1, matrix); + applyTransform(pt2, pt2, matrix); + } + // Tick line, Not use group transform to have better line draw + var tickEl = new Line(subPixelOptimizeLine({ + // Id for animation + anid: 'tick_' + ticksCoords[i].tickValue, + + shape: { + x1: pt1[0], + y1: pt1[1], + x2: pt2[0], + y2: pt2[1] + }, + style: defaults( + lineStyleModel.getLineStyle(), + { + stroke: axisModel.get('axisLine.lineStyle.color') + } + ), + z2: 2, + silent: true + })); + axisBuilder.group.add(tickEl); + tickEls.push(tickEl); + } + + return tickEls; +} + +function buildAxisLabel(axisBuilder, axisModel, opt) { + var axis = axisModel.axis; + var show = retrieve(opt.axisLabelShow, axisModel.get('axisLabel.show')); + + if (!show || axis.scale.isBlank()) { + return; + } + + var labelModel = axisModel.getModel('axisLabel'); + var labelMargin = labelModel.get('margin'); + var labels = axis.getViewLabels(); + + // Special label rotate. + var labelRotation = ( + retrieve(opt.labelRotate, labelModel.get('rotate')) || 0 + ) * PI$2 / 180; + + var labelLayout = innerTextLayout(opt.rotation, labelRotation, opt.labelDirection); + var rawCategoryData = axisModel.getCategories(true); + + var labelEls = []; + var silent = isSilent(axisModel); + var triggerEvent = axisModel.get('triggerEvent'); + + each$1(labels, function (labelItem, index) { + var tickValue = labelItem.tickValue; + var formattedLabel = labelItem.formattedLabel; + var rawLabel = labelItem.rawLabel; + + var itemLabelModel = labelModel; + if (rawCategoryData && rawCategoryData[tickValue] && rawCategoryData[tickValue].textStyle) { + itemLabelModel = new Model( + rawCategoryData[tickValue].textStyle, labelModel, axisModel.ecModel + ); + } + + var textColor = itemLabelModel.getTextColor() + || axisModel.get('axisLine.lineStyle.color'); + + var tickCoord = axis.dataToCoord(tickValue); + var pos = [ + tickCoord, + opt.labelOffset + opt.labelDirection * labelMargin + ]; + + var textEl = new Text({ + // Id for animation + anid: 'label_' + tickValue, + position: pos, + rotation: labelLayout.rotation, + silent: silent, + z2: 10 + }); + + setTextStyle(textEl.style, itemLabelModel, { + text: formattedLabel, + textAlign: itemLabelModel.getShallow('align', true) + || labelLayout.textAlign, + textVerticalAlign: itemLabelModel.getShallow('verticalAlign', true) + || itemLabelModel.getShallow('baseline', true) + || labelLayout.textVerticalAlign, + textFill: typeof textColor === 'function' + ? textColor( + // (1) In category axis with data zoom, tick is not the original + // index of axis.data. So tick should not be exposed to user + // in category axis. + // (2) Compatible with previous version, which always use formatted label as + // input. But in interval scale the formatted label is like '223,445', which + // maked user repalce ','. So we modify it to return original val but remain + // it as 'string' to avoid error in replacing. + axis.type === 'category' + ? rawLabel + : axis.type === 'value' + ? tickValue + '' + : tickValue, + index + ) + : textColor + }); + + // Pack data for mouse event + if (triggerEvent) { + textEl.eventData = makeAxisEventDataBase(axisModel); + textEl.eventData.targetType = 'axisLabel'; + textEl.eventData.value = rawLabel; + } + + // FIXME + axisBuilder._dumbGroup.add(textEl); + textEl.updateTransform(); + + labelEls.push(textEl); + axisBuilder.group.add(textEl); + + textEl.decomposeTransform(); + + }); + + return labelEls; +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var each$6 = each$1; +var curry$1 = curry; + +// Build axisPointerModel, mergin tooltip.axisPointer model for each axis. +// allAxesInfo should be updated when setOption performed. +function collect(ecModel, api) { + var result = { + /** + * key: makeKey(axis.model) + * value: { + * axis, + * coordSys, + * axisPointerModel, + * triggerTooltip, + * involveSeries, + * snap, + * seriesModels, + * seriesDataCount + * } + */ + axesInfo: {}, + seriesInvolved: false, + /** + * key: makeKey(coordSys.model) + * value: Object: key makeKey(axis.model), value: axisInfo + */ + coordSysAxesInfo: {}, + coordSysMap: {} + }; + + collectAxesInfo(result, ecModel, api); + + // Check seriesInvolved for performance, in case too many series in some chart. + result.seriesInvolved && collectSeriesInfo(result, ecModel); + + return result; +} + +function collectAxesInfo(result, ecModel, api) { + var globalTooltipModel = ecModel.getComponent('tooltip'); + var globalAxisPointerModel = ecModel.getComponent('axisPointer'); + // links can only be set on global. + var linksOption = globalAxisPointerModel.get('link', true) || []; + var linkGroups = []; + + // Collect axes info. + each$6(api.getCoordinateSystems(), function (coordSys) { + // Some coordinate system do not support axes, like geo. + if (!coordSys.axisPointerEnabled) { + return; + } + + var coordSysKey = makeKey(coordSys.model); + var axesInfoInCoordSys = result.coordSysAxesInfo[coordSysKey] = {}; + result.coordSysMap[coordSysKey] = coordSys; + + // Set tooltip (like 'cross') is a convienent way to show axisPointer + // for user. So we enable seting tooltip on coordSys model. + var coordSysModel = coordSys.model; + var baseTooltipModel = coordSysModel.getModel('tooltip', globalTooltipModel); + + each$6(coordSys.getAxes(), curry$1(saveTooltipAxisInfo, false, null)); + + // If axis tooltip used, choose tooltip axis for each coordSys. + // Notice this case: coordSys is `grid` but not `cartesian2D` here. + if (coordSys.getTooltipAxes + && globalTooltipModel + // If tooltip.showContent is set as false, tooltip will not + // show but axisPointer will show as normal. + && baseTooltipModel.get('show') + ) { + // Compatible with previous logic. But series.tooltip.trigger: 'axis' + // or series.data[n].tooltip.trigger: 'axis' are not support any more. + var triggerAxis = baseTooltipModel.get('trigger') === 'axis'; + var cross = baseTooltipModel.get('axisPointer.type') === 'cross'; + var tooltipAxes = coordSys.getTooltipAxes(baseTooltipModel.get('axisPointer.axis')); + if (triggerAxis || cross) { + each$6(tooltipAxes.baseAxes, curry$1( + saveTooltipAxisInfo, cross ? 'cross' : true, triggerAxis + )); + } + if (cross) { + each$6(tooltipAxes.otherAxes, curry$1(saveTooltipAxisInfo, 'cross', false)); + } + } + + // fromTooltip: true | false | 'cross' + // triggerTooltip: true | false | null + function saveTooltipAxisInfo(fromTooltip, triggerTooltip, axis) { + var axisPointerModel = axis.model.getModel('axisPointer', globalAxisPointerModel); + + var axisPointerShow = axisPointerModel.get('show'); + if (!axisPointerShow || ( + axisPointerShow === 'auto' + && !fromTooltip + && !isHandleTrigger(axisPointerModel) + )) { + return; + } + + if (triggerTooltip == null) { + triggerTooltip = axisPointerModel.get('triggerTooltip'); + } + + axisPointerModel = fromTooltip + ? makeAxisPointerModel( + axis, baseTooltipModel, globalAxisPointerModel, ecModel, + fromTooltip, triggerTooltip + ) + : axisPointerModel; + + var snap = axisPointerModel.get('snap'); + var key = makeKey(axis.model); + var involveSeries = triggerTooltip || snap || axis.type === 'category'; + + // If result.axesInfo[key] exist, override it (tooltip has higher priority). + var axisInfo = result.axesInfo[key] = { + key: key, + axis: axis, + coordSys: coordSys, + axisPointerModel: axisPointerModel, + triggerTooltip: triggerTooltip, + involveSeries: involveSeries, + snap: snap, + useHandle: isHandleTrigger(axisPointerModel), + seriesModels: [] + }; + axesInfoInCoordSys[key] = axisInfo; + result.seriesInvolved |= involveSeries; + + var groupIndex = getLinkGroupIndex(linksOption, axis); + if (groupIndex != null) { + var linkGroup = linkGroups[groupIndex] || (linkGroups[groupIndex] = {axesInfo: {}}); + linkGroup.axesInfo[key] = axisInfo; + linkGroup.mapper = linksOption[groupIndex].mapper; + axisInfo.linkGroup = linkGroup; + } + } + }); +} + +function makeAxisPointerModel( + axis, baseTooltipModel, globalAxisPointerModel, ecModel, fromTooltip, triggerTooltip +) { + var tooltipAxisPointerModel = baseTooltipModel.getModel('axisPointer'); + var volatileOption = {}; + + each$6( + [ + 'type', 'snap', 'lineStyle', 'shadowStyle', 'label', + 'animation', 'animationDurationUpdate', 'animationEasingUpdate', 'z' + ], + function (field) { + volatileOption[field] = clone(tooltipAxisPointerModel.get(field)); + } + ); + + // category axis do not auto snap, otherwise some tick that do not + // has value can not be hovered. value/time/log axis default snap if + // triggered from tooltip and trigger tooltip. + volatileOption.snap = axis.type !== 'category' && !!triggerTooltip; + + // Compatibel with previous behavior, tooltip axis do not show label by default. + // Only these properties can be overrided from tooltip to axisPointer. + if (tooltipAxisPointerModel.get('type') === 'cross') { + volatileOption.type = 'line'; + } + var labelOption = volatileOption.label || (volatileOption.label = {}); + // Follow the convention, do not show label when triggered by tooltip by default. + labelOption.show == null && (labelOption.show = false); + + if (fromTooltip === 'cross') { + // When 'cross', both axes show labels. + var tooltipAxisPointerLabelShow = tooltipAxisPointerModel.get('label.show'); + labelOption.show = tooltipAxisPointerLabelShow != null ? tooltipAxisPointerLabelShow : true; + // If triggerTooltip, this is a base axis, which should better not use cross style + // (cross style is dashed by default) + if (!triggerTooltip) { + var crossStyle = volatileOption.lineStyle = tooltipAxisPointerModel.get('crossStyle'); + crossStyle && defaults(labelOption, crossStyle.textStyle); + } + } + + return axis.model.getModel( + 'axisPointer', + new Model(volatileOption, globalAxisPointerModel, ecModel) + ); +} + +function collectSeriesInfo(result, ecModel) { + // Prepare data for axis trigger + ecModel.eachSeries(function (seriesModel) { + + // Notice this case: this coordSys is `cartesian2D` but not `grid`. + var coordSys = seriesModel.coordinateSystem; + var seriesTooltipTrigger = seriesModel.get('tooltip.trigger', true); + var seriesTooltipShow = seriesModel.get('tooltip.show', true); + if (!coordSys + || seriesTooltipTrigger === 'none' + || seriesTooltipTrigger === false + || seriesTooltipTrigger === 'item' + || seriesTooltipShow === false + || seriesModel.get('axisPointer.show', true) === false + ) { + return; + } + + each$6(result.coordSysAxesInfo[makeKey(coordSys.model)], function (axisInfo) { + var axis = axisInfo.axis; + if (coordSys.getAxis(axis.dim) === axis) { + axisInfo.seriesModels.push(seriesModel); + axisInfo.seriesDataCount == null && (axisInfo.seriesDataCount = 0); + axisInfo.seriesDataCount += seriesModel.getData().count(); + } + }); + + }, this); +} + +/** + * For example: + * { + * axisPointer: { + * links: [{ + * xAxisIndex: [2, 4], + * yAxisIndex: 'all' + * }, { + * xAxisId: ['a5', 'a7'], + * xAxisName: 'xxx' + * }] + * } + * } + */ +function getLinkGroupIndex(linksOption, axis) { + var axisModel = axis.model; + var dim = axis.dim; + for (var i = 0; i < linksOption.length; i++) { + var linkOption = linksOption[i] || {}; + if (checkPropInLink(linkOption[dim + 'AxisId'], axisModel.id) + || checkPropInLink(linkOption[dim + 'AxisIndex'], axisModel.componentIndex) + || checkPropInLink(linkOption[dim + 'AxisName'], axisModel.name) + ) { + return i; + } + } +} + +function checkPropInLink(linkPropValue, axisPropValue) { + return linkPropValue === 'all' + || (isArray(linkPropValue) && indexOf(linkPropValue, axisPropValue) >= 0) + || linkPropValue === axisPropValue; +} + +function fixValue(axisModel) { + var axisInfo = getAxisInfo(axisModel); + if (!axisInfo) { + return; + } + + var axisPointerModel = axisInfo.axisPointerModel; + var scale = axisInfo.axis.scale; + var option = axisPointerModel.option; + var status = axisPointerModel.get('status'); + var value = axisPointerModel.get('value'); + + // Parse init value for category and time axis. + if (value != null) { + value = scale.parse(value); + } + + var useHandle = isHandleTrigger(axisPointerModel); + // If `handle` used, `axisPointer` will always be displayed, so value + // and status should be initialized. + if (status == null) { + option.status = useHandle ? 'show' : 'hide'; + } + + var extent = scale.getExtent().slice(); + extent[0] > extent[1] && extent.reverse(); + + if (// Pick a value on axis when initializing. + value == null + // If both `handle` and `dataZoom` are used, value may be out of axis extent, + // where we should re-pick a value to keep `handle` displaying normally. + || value > extent[1] + ) { + // Make handle displayed on the end of the axis when init, which looks better. + value = extent[1]; + } + if (value < extent[0]) { + value = extent[0]; + } + + option.value = value; + + if (useHandle) { + option.status = axisInfo.axis.scale.isBlank() ? 'hide' : 'show'; + } +} + +function getAxisInfo(axisModel) { + var coordSysAxesInfo = (axisModel.ecModel.getComponent('axisPointer') || {}).coordSysAxesInfo; + return coordSysAxesInfo && coordSysAxesInfo.axesInfo[makeKey(axisModel)]; +} + +function getAxisPointerModel(axisModel) { + var axisInfo = getAxisInfo(axisModel); + return axisInfo && axisInfo.axisPointerModel; +} + +function isHandleTrigger(axisPointerModel) { + return !!axisPointerModel.get('handle.show'); +} + +/** + * @param {module:echarts/model/Model} model + * @return {string} unique key + */ +function makeKey(model) { + return model.type + '||' + model.id; +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * Base class of AxisView. + */ +var AxisView = extendComponentView({ + + type: 'axis', + + /** + * @private + */ + _axisPointer: null, + + /** + * @protected + * @type {string} + */ + axisPointerClass: null, + + /** + * @override + */ + render: function (axisModel, ecModel, api, payload) { + // FIXME + // This process should proformed after coordinate systems updated + // (axis scale updated), and should be performed each time update. + // So put it here temporarily, although it is not appropriate to + // put a model-writing procedure in `view`. + this.axisPointerClass && fixValue(axisModel); + + AxisView.superApply(this, 'render', arguments); + + updateAxisPointer(this, axisModel, ecModel, api, payload, true); + }, + + /** + * Action handler. + * @public + * @param {module:echarts/coord/cartesian/AxisModel} axisModel + * @param {module:echarts/model/Global} ecModel + * @param {module:echarts/ExtensionAPI} api + * @param {Object} payload + */ + updateAxisPointer: function (axisModel, ecModel, api, payload, force) { + updateAxisPointer(this, axisModel, ecModel, api, payload, false); + }, + + /** + * @override + */ + remove: function (ecModel, api) { + var axisPointer = this._axisPointer; + axisPointer && axisPointer.remove(api); + AxisView.superApply(this, 'remove', arguments); + }, + + /** + * @override + */ + dispose: function (ecModel, api) { + disposeAxisPointer(this, api); + AxisView.superApply(this, 'dispose', arguments); + } + +}); + +function updateAxisPointer(axisView, axisModel, ecModel, api, payload, forceRender) { + var Clazz = AxisView.getAxisPointerClass(axisView.axisPointerClass); + if (!Clazz) { + return; + } + var axisPointerModel = getAxisPointerModel(axisModel); + axisPointerModel + ? (axisView._axisPointer || (axisView._axisPointer = new Clazz())) + .render(axisModel, axisPointerModel, api, forceRender) + : disposeAxisPointer(axisView, api); +} + +function disposeAxisPointer(axisView, ecModel, api) { + var axisPointer = axisView._axisPointer; + axisPointer && axisPointer.dispose(ecModel, api); + axisView._axisPointer = null; +} + +var axisPointerClazz = []; + +AxisView.registerAxisPointerClass = function (type, clazz) { + if (__DEV__) { + if (axisPointerClazz[type]) { + throw new Error('axisPointer ' + type + ' exists'); + } + } + axisPointerClazz[type] = clazz; +}; + +AxisView.getAxisPointerClass = function (type) { + return type && axisPointerClazz[type]; +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * Can only be called after coordinate system creation stage. + * (Can be called before coordinate system update stage). + * + * @param {Object} opt {labelInside} + * @return {Object} { + * position, rotation, labelDirection, labelOffset, + * tickDirection, labelRotate, z2 + * } + */ +function layout$1(gridModel, axisModel, opt) { + opt = opt || {}; + var grid = gridModel.coordinateSystem; + var axis = axisModel.axis; + var layout = {}; + var otherAxisOnZeroOf = axis.getAxesOnZeroOf()[0]; + + var rawAxisPosition = axis.position; + var axisPosition = otherAxisOnZeroOf ? 'onZero' : rawAxisPosition; + var axisDim = axis.dim; + + var rect = grid.getRect(); + var rectBound = [rect.x, rect.x + rect.width, rect.y, rect.y + rect.height]; + var idx = {left: 0, right: 1, top: 0, bottom: 1, onZero: 2}; + var axisOffset = axisModel.get('offset') || 0; + + var posBound = axisDim === 'x' + ? [rectBound[2] - axisOffset, rectBound[3] + axisOffset] + : [rectBound[0] - axisOffset, rectBound[1] + axisOffset]; + + if (otherAxisOnZeroOf) { + var onZeroCoord = otherAxisOnZeroOf.toGlobalCoord(otherAxisOnZeroOf.dataToCoord(0)); + posBound[idx['onZero']] = Math.max(Math.min(onZeroCoord, posBound[1]), posBound[0]); + } + + // Axis position + layout.position = [ + axisDim === 'y' ? posBound[idx[axisPosition]] : rectBound[0], + axisDim === 'x' ? posBound[idx[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]; + layout.labelOffset = otherAxisOnZeroOf ? posBound[idx[rawAxisPosition]] - posBound[idx['onZero']] : 0; + + if (axisModel.get('axisTick.inside')) { + layout.tickDirection = -layout.tickDirection; + } + if (retrieve(opt.labelInside, axisModel.get('axisLabel.inside'))) { + layout.labelDirection = -layout.labelDirection; + } + + // Special label rotation + var labelRotate = axisModel.get('axisLabel.rotate'); + layout.labelRotate = axisPosition === 'top' ? -labelRotate : labelRotate; + + // Over splitLine and splitArea + layout.z2 = 1; + + return layout; +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var axisBuilderAttrs = [ + 'axisLine', 'axisTickLabel', '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 CartesianAxisView = AxisView.extend({ + + type: 'cartesianAxis', + + axisPointerClass: 'CartesianAxisPointer', + + /** + * @override + */ + render: function (axisModel, ecModel, api, payload) { + + this.group.removeAll(); + + var oldAxisGroup = this._axisGroup; + this._axisGroup = new Group(); + + this.group.add(this._axisGroup); + + if (!axisModel.get('show')) { + return; + } + + var gridModel = axisModel.getCoordSysModel(); + + var layout = layout$1(gridModel, axisModel); + + var axisBuilder = new AxisBuilder(axisModel, layout); + + each$1(axisBuilderAttrs, axisBuilder.add, axisBuilder); + + this._axisGroup.add(axisBuilder.getGroup()); + + each$1(selfBuilderAttrs, function (name) { + if (axisModel.get(name + '.show')) { + this['_' + name](axisModel, gridModel); + } + }, this); + + groupTransition(oldAxisGroup, this._axisGroup, axisModel); + + CartesianAxisView.superCall(this, 'render', axisModel, ecModel, api, payload); + }, + + remove: function () { + this._splitAreaColors = null; + }, + + /** + * @param {module:echarts/coord/cartesian/AxisModel} axisModel + * @param {module:echarts/coord/cartesian/GridModel} gridModel + * @private + */ + _splitLine: function (axisModel, gridModel) { + var axis = axisModel.axis; + + if (axis.scale.isBlank()) { + return; + } + + var splitLineModel = axisModel.getModel('splitLine'); + var lineStyleModel = splitLineModel.getModel('lineStyle'); + var lineColors = lineStyleModel.get('color'); + + lineColors = isArray(lineColors) ? lineColors : [lineColors]; + + var gridRect = gridModel.coordinateSystem.getRect(); + var isHorizontal = axis.isHorizontal(); + + var lineCount = 0; + + var ticksCoords = axis.getTicksCoords({ + tickModel: splitLineModel + }); + + 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++) { + var tickCoord = axis.toGlobalCoord(ticksCoords[i].coord); + + 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; + var tickValue = ticksCoords[i].tickValue; + this._axisGroup.add(new Line(subPixelOptimizeLine({ + anid: tickValue != null ? 'line_' + ticksCoords[i].tickValue : null, + shape: { + x1: p1[0], + y1: p1[1], + x2: p2[0], + y2: p2[1] + }, + style: defaults({ + stroke: lineColors[colorIndex] + }, lineStyle), + silent: true + }))); + } + }, + + /** + * @param {module:echarts/coord/cartesian/AxisModel} axisModel + * @param {module:echarts/coord/cartesian/GridModel} gridModel + * @private + */ + _splitArea: function (axisModel, gridModel) { + var axis = axisModel.axis; + + if (axis.scale.isBlank()) { + return; + } + + var splitAreaModel = axisModel.getModel('splitArea'); + var areaStyleModel = splitAreaModel.getModel('areaStyle'); + var areaColors = areaStyleModel.get('color'); + + var gridRect = gridModel.coordinateSystem.getRect(); + + var ticksCoords = axis.getTicksCoords({ + tickModel: splitAreaModel, + clamp: true + }); + + if (!ticksCoords.length) { + return; + } + + // For Making appropriate splitArea animation, the color and anid + // should be corresponding to previous one if possible. + var areaColorsLen = areaColors.length; + var lastSplitAreaColors = this._splitAreaColors; + var newSplitAreaColors = createHashMap(); + var colorIndex = 0; + if (lastSplitAreaColors) { + for (var i = 0; i < ticksCoords.length; i++) { + var cIndex = lastSplitAreaColors.get(ticksCoords[i].tickValue); + if (cIndex != null) { + colorIndex = (cIndex + (areaColorsLen - 1) * i) % areaColorsLen; + break; + } + } + } + + var prev = axis.toGlobalCoord(ticksCoords[0].coord); + + var areaStyle = areaStyleModel.getAreaStyle(); + areaColors = isArray(areaColors) ? areaColors : [areaColors]; + + for (var i = 1; i < ticksCoords.length; i++) { + var tickCoord = axis.toGlobalCoord(ticksCoords[i].coord); + + var x; + var y; + var width; + var height; + if (axis.isHorizontal()) { + x = prev; + y = gridRect.y; + width = tickCoord - x; + height = gridRect.height; + prev = x + width; + } + else { + x = gridRect.x; + y = prev; + width = gridRect.width; + height = tickCoord - y; + prev = y + height; + } + + var tickValue = ticksCoords[i - 1].tickValue; + tickValue != null && newSplitAreaColors.set(tickValue, colorIndex); + + this._axisGroup.add(new Rect({ + anid: tickValue != null ? 'area_' + tickValue : null, + shape: { + x: x, + y: y, + width: width, + height: height + }, + style: defaults({ + fill: areaColors[colorIndex] + }, areaStyle), + silent: true + })); + + colorIndex = (colorIndex + 1) % areaColorsLen; + } + + this._splitAreaColors = newSplitAreaColors; + } +}); + +CartesianAxisView.extend({ + type: 'xAxis' +}); +CartesianAxisView.extend({ + type: 'yAxis' +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +// Grid view +extendComponentView({ + + type: 'grid', + + render: function (gridModel, ecModel) { + this.group.removeAll(); + if (gridModel.get('show')) { + this.group.add(new Rect({ + shape: gridModel.coordinateSystem.getRect(), + style: defaults({ + fill: gridModel.get('backgroundColor') + }, gridModel.getItemStyle()), + silent: true, + z2: -1 + })); + } + } + +}); + +registerPreprocessor(function (option) { + // Only create grid when need + if (option.xAxis && option.yAxis && !option.grid) { + option.grid = {}; + } +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +// In case developer forget to include grid component +registerVisual(visualSymbol('line', 'circle', 'line')); +registerLayout(pointsLayout('line')); + +// Down sample after filter +registerProcessor( + PRIORITY.PROCESSOR.STATISTIC, + dataSample('line') +); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var BaseBarSeries = SeriesModel.extend({ + + type: 'series.__base_bar__', + + getInitialData: function (option, ecModel) { + return createListFromArray(this.getSource(), this); + }, + + getMarkerPosition: function (value) { + var coordSys = this.coordinateSystem; + if (coordSys) { + // PENDING if clamp ? + var pt = coordSys.dataToPoint(coordSys.clampData(value)); + 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]; + }, + + defaultOption: { + zlevel: 0, // 一级层叠 + z: 2, // 二级层叠 + coordinateSystem: 'cartesian2d', + legendHoverLink: true, + // stack: null + + // Cartesian coordinate system + // xAxisIndex: 0, + // yAxisIndex: 0, + + // 最小高度改为0 + barMinHeight: 0, + // 最小角度为0,仅对极坐标系下的柱状图有效 + barMinAngle: 0, + // cursor: null, + + large: false, + largeThreshold: 400, + progressive: 3e3, + progressiveChunkMode: 'mod', + + // barMaxWidth: null, + // 默认自适应 + // barWidth: null, + // 柱间距离,默认为柱形宽度的30%,可设固定值 + // barGap: '30%', + // 类目间柱形距离,默认为类目间距的20%,可设固定值 + // barCategoryGap: '20%', + // label: { + // show: false + // }, + itemStyle: {}, + emphasis: {} + } +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +BaseBarSeries.extend({ + + type: 'series.bar', + + dependencies: ['grid', 'polar'], + + brushSelector: 'rect', + + /** + * @override + */ + getProgressive: function () { + // Do not support progressive in normal mode. + return this.get('large') + ? this.get('progressive') + : false; + }, + + /** + * @override + */ + getProgressiveThreshold: function () { + // Do not support progressive in normal mode. + var progressiveThreshold = this.get('progressiveThreshold'); + var largeThreshold = this.get('largeThreshold'); + if (largeThreshold > progressiveThreshold) { + progressiveThreshold = largeThreshold; + } + return progressiveThreshold; + } + +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +function setLabel( + normalStyle, hoverStyle, itemModel, color, seriesModel, dataIndex, labelPositionOutside +) { + var labelModel = itemModel.getModel('label'); + var hoverLabelModel = itemModel.getModel('emphasis.label'); + + setLabelStyle( + normalStyle, hoverStyle, labelModel, hoverLabelModel, + { + labelFetcher: seriesModel, + labelDataIndex: dataIndex, + defaultText: getDefaultLabel(seriesModel.getData(), dataIndex), + isRectText: true, + autoColor: color + } + ); + + fixPosition(normalStyle); + fixPosition(hoverStyle); +} + +function fixPosition(style, labelPositionOutside) { + if (style.textPosition === 'outside') { + style.textPosition = labelPositionOutside; + } +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var getBarItemStyle = makeStyleMapper( + [ + ['fill', 'color'], + ['stroke', 'borderColor'], + ['lineWidth', 'borderWidth'], + // Compatitable with 2 + ['stroke', 'barBorderColor'], + ['lineWidth', 'barBorderWidth'], + ['opacity'], + ['shadowBlur'], + ['shadowOffsetX'], + ['shadowOffsetY'], + ['shadowColor'] + ] +); + +var barItemStyle = { + getBarItemStyle: function (excludes) { + var style = getBarItemStyle(this, excludes); + if (this.getBorderLineDash) { + var lineDash = this.getBorderLineDash(); + lineDash && (style.lineDash = lineDash); + } + return style; + } +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var BAR_BORDER_WIDTH_QUERY = ['itemStyle', 'barBorderWidth']; + +// FIXME +// Just for compatible with ec2. +extend(Model.prototype, barItemStyle); + +extendChartView({ + + type: 'bar', + + render: function (seriesModel, ecModel, api) { + this._updateDrawMode(seriesModel); + + var coordinateSystemType = seriesModel.get('coordinateSystem'); + + if (coordinateSystemType === 'cartesian2d' + || coordinateSystemType === 'polar' + ) { + this._isLargeDraw + ? this._renderLarge(seriesModel, ecModel, api) + : this._renderNormal(seriesModel, ecModel, api); + } + else if (__DEV__) { + console.warn('Only cartesian2d and polar supported for bar.'); + } + + return this.group; + }, + + incrementalPrepareRender: function (seriesModel, ecModel, api) { + this._clear(); + this._updateDrawMode(seriesModel); + }, + + incrementalRender: function (params, seriesModel, ecModel, api) { + // Do not support progressive in normal mode. + this._incrementalRenderLarge(params, seriesModel); + }, + + _updateDrawMode: function (seriesModel) { + var isLargeDraw = seriesModel.pipelineContext.large; + if (this._isLargeDraw == null || isLargeDraw ^ this._isLargeDraw) { + this._isLargeDraw = isLargeDraw; + this._clear(); + } + }, + + _renderNormal: function (seriesModel, ecModel, api) { + var group = this.group; + var data = seriesModel.getData(); + var oldData = this._data; + + var coord = seriesModel.coordinateSystem; + var baseAxis = coord.getBaseAxis(); + var isHorizontalOrRadial; + + if (coord.type === 'cartesian2d') { + isHorizontalOrRadial = baseAxis.isHorizontal(); + } + else if (coord.type === 'polar') { + isHorizontalOrRadial = baseAxis.dim === 'angle'; + } + + var animationModel = seriesModel.isAnimationEnabled() ? seriesModel : null; + + data.diff(oldData) + .add(function (dataIndex) { + if (!data.hasValue(dataIndex)) { + return; + } + + var itemModel = data.getItemModel(dataIndex); + var layout = getLayout[coord.type](data, dataIndex, itemModel); + var el = elementCreator[coord.type]( + data, dataIndex, itemModel, layout, isHorizontalOrRadial, animationModel + ); + data.setItemGraphicEl(dataIndex, el); + group.add(el); + + updateStyle( + el, data, dataIndex, itemModel, layout, + seriesModel, isHorizontalOrRadial, coord.type === 'polar' + ); + }) + .update(function (newIndex, oldIndex) { + var el = oldData.getItemGraphicEl(oldIndex); + + if (!data.hasValue(newIndex)) { + group.remove(el); + return; + } + + var itemModel = data.getItemModel(newIndex); + var layout = getLayout[coord.type](data, newIndex, itemModel); + + if (el) { + updateProps(el, {shape: layout}, animationModel, newIndex); + } + else { + el = elementCreator[coord.type]( + data, newIndex, itemModel, layout, isHorizontalOrRadial, animationModel, true + ); + } + + data.setItemGraphicEl(newIndex, el); + // Add back + group.add(el); + + updateStyle( + el, data, newIndex, itemModel, layout, + seriesModel, isHorizontalOrRadial, coord.type === 'polar' + ); + }) + .remove(function (dataIndex) { + var el = oldData.getItemGraphicEl(dataIndex); + if (coord.type === 'cartesian2d') { + el && removeRect(dataIndex, animationModel, el); + } + else { + el && removeSector(dataIndex, animationModel, el); + } + }) + .execute(); + + this._data = data; + }, + + _renderLarge: function (seriesModel, ecModel, api) { + this._clear(); + createLarge(seriesModel, this.group); + }, + + _incrementalRenderLarge: function (params, seriesModel) { + createLarge(seriesModel, this.group, true); + }, + + dispose: noop, + + remove: function (ecModel) { + this._clear(ecModel); + }, + + _clear: function (ecModel) { + var group = this.group; + var data = this._data; + if (ecModel && ecModel.get('animation') && data && !this._isLargeDraw) { + data.eachItemGraphicEl(function (el) { + if (el.type === 'sector') { + removeSector(el.dataIndex, ecModel, el); + } + else { + removeRect(el.dataIndex, ecModel, el); + } + }); + } + else { + group.removeAll(); + } + this._data = null; + } + +}); + +var elementCreator = { + + cartesian2d: function ( + data, dataIndex, itemModel, layout, isHorizontal, + animationModel, isUpdate + ) { + var rect = new Rect({shape: extend({}, layout)}); + + // Animation + if (animationModel) { + 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 + }, animationModel, dataIndex); + } + + return rect; + }, + + polar: function ( + data, dataIndex, itemModel, layout, isRadial, + animationModel, isUpdate + ) { + // Keep the same logic with bar in catesion: use end value to control + // direction. Notice that if clockwise is true (by default), the sector + // will always draw clockwisely, no matter whether endAngle is greater + // or less than startAngle. + var clockwise = layout.startAngle < layout.endAngle; + var sector = new Sector({ + shape: defaults({clockwise: clockwise}, layout) + }); + + // Animation + if (animationModel) { + var sectorShape = sector.shape; + var animateProperty = isRadial ? 'r' : 'endAngle'; + var animateTarget = {}; + sectorShape[animateProperty] = isRadial ? 0 : layout.startAngle; + animateTarget[animateProperty] = layout[animateProperty]; + graphic[isUpdate ? 'updateProps' : 'initProps'](sector, { + shape: animateTarget + }, animationModel, dataIndex); + } + + return sector; + } +}; + +function removeRect(dataIndex, animationModel, el) { + // Not show text when animating + el.style.text = null; + updateProps(el, { + shape: { + width: 0 + } + }, animationModel, dataIndex, function () { + el.parent && el.parent.remove(el); + }); +} + +function removeSector(dataIndex, animationModel, el) { + // Not show text when animating + el.style.text = null; + updateProps(el, { + shape: { + r: el.shape.r0 + } + }, animationModel, dataIndex, function () { + el.parent && el.parent.remove(el); + }); +} + +var getLayout = { + cartesian2d: function (data, dataIndex, itemModel) { + var layout = data.getItemLayout(dataIndex); + var fixedLineWidth = getLineWidth(itemModel, layout); + + // fix layout with lineWidth + var signX = layout.width > 0 ? 1 : -1; + var signY = layout.height > 0 ? 1 : -1; + return { + x: layout.x + signX * fixedLineWidth / 2, + y: layout.y + signY * fixedLineWidth / 2, + width: layout.width - signX * fixedLineWidth, + height: layout.height - signY * fixedLineWidth + }; + }, + + polar: function (data, dataIndex, itemModel) { + var layout = data.getItemLayout(dataIndex); + return { + cx: layout.cx, + cy: layout.cy, + r0: layout.r0, + r: layout.r, + startAngle: layout.startAngle, + endAngle: layout.endAngle + }; + } +}; + +function updateStyle( + el, data, dataIndex, itemModel, layout, seriesModel, isHorizontal, isPolar +) { + var color = data.getItemVisual(dataIndex, 'color'); + var opacity = data.getItemVisual(dataIndex, 'opacity'); + var itemStyleModel = itemModel.getModel('itemStyle'); + var hoverStyle = itemModel.getModel('emphasis.itemStyle').getBarItemStyle(); + + if (!isPolar) { + el.setShape('r', itemStyleModel.get('barBorderRadius') || 0); + } + + el.useStyle(defaults( + { + fill: color, + opacity: opacity + }, + itemStyleModel.getBarItemStyle() + )); + + var cursorStyle = itemModel.getShallow('cursor'); + cursorStyle && el.attr('cursor', cursorStyle); + + var labelPositionOutside = isHorizontal + ? (layout.height > 0 ? 'bottom' : 'top') + : (layout.width > 0 ? 'left' : 'right'); + + if (!isPolar) { + setLabel( + el.style, hoverStyle, itemModel, color, + seriesModel, dataIndex, labelPositionOutside + ); + } + + setHoverStyle(el, hoverStyle); +} + +// In case width or height are too small. +function getLineWidth(itemModel, rawLayout) { + var lineWidth = itemModel.get(BAR_BORDER_WIDTH_QUERY) || 0; + return Math.min(lineWidth, Math.abs(rawLayout.width), Math.abs(rawLayout.height)); +} + + +var LargePath = Path.extend({ + + type: 'largeBar', + + shape: {points: []}, + + buildPath: function (ctx, shape) { + // Drawing lines is more efficient than drawing + // a whole line or drawing rects. + var points = shape.points; + var startPoint = this.__startPoint; + var valueIdx = this.__valueIdx; + + for (var i = 0; i < points.length; i += 2) { + startPoint[this.__valueIdx] = points[i + valueIdx]; + ctx.moveTo(startPoint[0], startPoint[1]); + ctx.lineTo(points[i], points[i + 1]); + } + } +}); + +function createLarge(seriesModel, group, incremental) { + // TODO support polar + var data = seriesModel.getData(); + var startPoint = []; + var valueIdx = data.getLayout('valueAxisHorizontal') ? 1 : 0; + startPoint[1 - valueIdx] = data.getLayout('valueAxisStart'); + + var el = new LargePath({ + shape: {points: data.getLayout('largePoints')}, + incremental: !!incremental, + __startPoint: startPoint, + __valueIdx: valueIdx + }); + group.add(el); + setLargeStyle(el, seriesModel, data); +} + +function setLargeStyle(el, seriesModel, data) { + var borderColor = data.getVisual('borderColor') || data.getVisual('color'); + var itemStyle = seriesModel.getModel('itemStyle').getItemStyle(['color', 'borderColor']); + + el.useStyle(itemStyle); + el.style.fill = null; + el.style.stroke = borderColor; + el.style.lineWidth = data.getLayout('barWidth'); +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +// In case developer forget to include grid component +registerLayout(curry(layout, 'bar')); +// Should after normal bar layout, otherwise it is blocked by normal bar layout. +registerLayout(largeLayout); + +registerVisual({ + seriesType: 'bar', + reset: function (seriesModel) { + // Visual coding for legend + seriesModel.getData().setVisual('legendSymbol', 'roundRect'); + } +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + + +/** + * [Usage]: + * (1) + * createListSimply(seriesModel, ['value']); + * (2) + * createListSimply(seriesModel, { + * coordDimensions: ['value'], + * dimensionsCount: 5 + * }); + * + * @param {module:echarts/model/Series} seriesModel + * @param {Object|Array.} opt opt or coordDimensions + * The options in opt, see `echarts/data/helper/createDimensions` + * @param {Array.} [nameList] + * @return {module:echarts/data/List} + */ +var createListSimply = function (seriesModel, opt, nameList) { + opt = isArray(opt) && {coordDimensions: opt} || extend({}, opt); + + var source = seriesModel.getSource(); + + var dimensionsInfo = createDimensions(source, opt); + + var list = new List(dimensionsInfo, seriesModel); + list.initData(source, nameList); + + return list; +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * 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 + */ + +var selectableMixin = { + + /** + * @param {Array.} targetList [{name, value, selected}, ...] + * If targetList is an array, it should like [{name: ..., value: ...}, ...]. + * If targetList is a "List", it must have coordDim: 'value' dimension and name. + */ + updateSelectedMap: function (targetList) { + this._targetList = isArray(targetList) ? targetList.slice() : []; + + this._selectTargetMap = reduce(targetList || [], function (targetMap, target) { + targetMap.set(target.name, target); + return targetMap; + }, createHashMap()); + }, + + /** + * Either name or id should be passed as input here. + * If both of them are defined, id is used. + * + * @param {string|undefined} name name of data + * @param {number|undefined} id dataIndex of data + */ + // PENGING If selectedMode is null ? + select: function (name, id) { + var target = id != null + ? this._targetList[id] + : this._selectTargetMap.get(name); + var selectedMode = this.get('selectedMode'); + if (selectedMode === 'single') { + this._selectTargetMap.each(function (target) { + target.selected = false; + }); + } + target && (target.selected = true); + }, + + /** + * Either name or id should be passed as input here. + * If both of them are defined, id is used. + * + * @param {string|undefined} name name of data + * @param {number|undefined} id dataIndex of data + */ + unSelect: function (name, id) { + var target = id != null + ? this._targetList[id] + : this._selectTargetMap.get(name); + // var selectedMode = this.get('selectedMode'); + // selectedMode !== 'single' && target && (target.selected = false); + target && (target.selected = false); + }, + + /** + * Either name or id should be passed as input here. + * If both of them are defined, id is used. + * + * @param {string|undefined} name name of data + * @param {number|undefined} id dataIndex of data + */ + toggleSelected: function (name, id) { + var target = id != null + ? this._targetList[id] + : this._selectTargetMap.get(name); + if (target != null) { + this[target.selected ? 'unSelect' : 'select'](name, id); + return target.selected; + } + }, + + /** + * Either name or id should be passed as input here. + * If both of them are defined, id is used. + * + * @param {string|undefined} name name of data + * @param {number|undefined} id dataIndex of data + */ + isSelected: function (name, id) { + var target = id != null + ? this._targetList[id] + : this._selectTargetMap.get(name); + return target && target.selected; + } +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var PieSeries = 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.getRawData(); + }; + + this.updateSelectedMap(this._createSelectableList()); + + this._defaultLabelLine(option); + }, + + // Overwrite + mergeOption: function (newOption) { + PieSeries.superCall(this, 'mergeOption', newOption); + + this.updateSelectedMap(this._createSelectableList()); + }, + + getInitialData: function (option, ecModel) { + return createListSimply(this, ['value']); + }, + + _createSelectableList: function () { + var data = this.getRawData(); + var valueDim = data.mapDimension('value'); + var targetList = []; + for (var i = 0, len = data.count(); i < len; i++) { + targetList.push({ + name: data.getName(i), + value: data.get(valueDim, i), + selected: retrieveRawAttr(data, i, 'selected') + }); + } + return targetList; + }, + + // Overwrite + getDataParams: function (dataIndex) { + var data = this.getData(); + var params = PieSeries.superCall(this, 'getDataParams', dataIndex); + // FIXME toFixed? + + var valueList = []; + data.each(data.mapDimension('value'), function (value) { + valueList.push(value); + }); + + params.percent = getPercentWithPrecision( + valueList, + dataIndex, + data.hostModel.get('percentPrecision') + ); + + params.$vars.push('percent'); + return params; + }, + + _defaultLabelLine: function (option) { + // Extend labelLine emphasis + defaultEmphasis(option, 'labelLine', ['show']); + + var labelLineNormalOpt = option.labelLine; + var labelLineEmphasisOpt = option.emphasis.labelLine; + // Not show label line if `label.normal.show = false` + labelLineNormalOpt.show = labelLineNormalOpt.show + && option.label.show; + labelLineEmphasisOpt.show = labelLineEmphasisOpt.show + && option.emphasis.label.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, + // 高亮扇区偏移量 + hoverOffset: 10, + + // If use strategy to avoid label overlapping + avoidLabelOverlap: true, + // 选择模式,默认关闭,可选single,multiple + // selectedMode: false, + // 南丁格尔玫瑰图模式,'radius'(半径) | 'area'(面积) + // roseType: null, + + percentPrecision: 2, + + // If still show when all data zero. + stillShowZeroSum: true, + + // cursor: null, + + label: { + // If rotate around circle + rotate: false, + show: true, + // 'outer', 'inside', 'center' + position: 'outer' + // formatter: 标签文本格式器,同Tooltip.formatter,不支持异步回调 + // 默认使用全局文本样式,详见TEXTSTYLE + // distance: 当position为inner时有效,为label位置到圆心的距离与圆半径(环状图为内外半径和)的比例系数 + }, + // Enabled when label.normal.position is 'outer' + labelLine: { + show: true, + // 引导线两段中的第一段长度 + length: 15, + // 引导线两段中的第二段长度 + length2: 15, + smooth: false, + lineStyle: { + // color: 各异, + width: 1, + type: 'solid' + } + }, + itemStyle: { + borderWidth: 1 + }, + + // Animation type canbe expansion, scale + animationType: 'expansion', + + animationEasing: 'cubicOut' + } +}); + +mixin(PieSeries, selectableMixin); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * @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) { + + Group.call(this); + + var sector = new Sector({ + z2: 2 + }); + var polyline = new Polyline(); + var text = new 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; + +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 = extend({}, layout); + sectorShape.label = null; + + if (firstCreate) { + sector.setShape(sectorShape); + + var animationType = seriesModel.getShallow('animationType'); + if (animationType === 'scale') { + sector.shape.r = layout.r0; + initProps(sector, { + shape: { + r: layout.r + } + }, seriesModel, idx); + } + // Expansion + else { + sector.shape.endAngle = layout.startAngle; + updateProps(sector, { + shape: { + endAngle: layout.endAngle + } + }, seriesModel, idx); + } + + } + else { + updateProps(sector, { + shape: sectorShape + }, seriesModel, idx); + } + + // Update common style + var visualColor = data.getItemVisual(idx, 'color'); + + sector.useStyle( + defaults( + { + lineJoin: 'bevel', + fill: visualColor + }, + itemModel.getModel('itemStyle').getItemStyle() + ) + ); + sector.hoverStyle = itemModel.getModel('emphasis.itemStyle').getItemStyle(); + + var cursorStyle = itemModel.getShallow('cursor'); + cursorStyle && sector.attr('cursor', cursorStyle); + + // Toggle selected + toggleItemSelected( + this, + data.getItemLayout(idx), + seriesModel.isSelected(null, idx), + 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 + seriesModel.get('hoverOffset') + } + }, 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.isAnimationEnabled()) { + sector + .on('mouseover', onEmphasis) + .on('mouseout', onNormal) + .on('emphasis', onEmphasis) + .on('normal', onNormal); + } + + this._updateLabel(data, idx); + + 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'); + + updateProps(labelLine, { + shape: { + points: labelLayout.linePoints || [ + [labelLayout.x, labelLayout.y], [labelLayout.x, labelLayout.y], [labelLayout.x, labelLayout.y] + ] + } + }, seriesModel, idx); + + updateProps(labelText, { + style: { + x: labelLayout.x, + y: labelLayout.y + } + }, seriesModel, idx); + labelText.attr({ + rotation: labelLayout.rotation, + origin: [labelLayout.x, labelLayout.y], + z2: 10 + }); + + var labelModel = itemModel.getModel('label'); + var labelHoverModel = itemModel.getModel('emphasis.label'); + var labelLineModel = itemModel.getModel('labelLine'); + var labelLineHoverModel = itemModel.getModel('emphasis.labelLine'); + var visualColor = data.getItemVisual(idx, 'color'); + + setLabelStyle( + labelText.style, labelText.hoverStyle = {}, labelModel, labelHoverModel, + { + labelFetcher: data.hostModel, + labelDataIndex: idx, + defaultText: data.getName(idx), + autoColor: visualColor, + useInsideStyle: !!labelLayout.inside + }, + { + textAlign: labelLayout.textAlign, + textVerticalAlign: labelLayout.verticalAlign, + opacity: data.getItemVisual(idx, 'opacity') + } + ); + + 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()); + + labelLine.hoverStyle = labelLineHoverModel.getModel('lineStyle').getLineStyle(); + + var smooth = labelLineModel.get('smooth'); + if (smooth && smooth === true) { + smooth = 0.4; + } + labelLine.setShape({ + smooth: smooth + }); +}; + +inherits(PiePiece, Group); + + +// Pie view +var PieView = Chart.extend({ + + type: 'pie', + + init: function () { + var sectorGroup = new 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 animationType = seriesModel.get('animationType'); + + var onSectorClick = curry( + updateDataSelected, this.uid, seriesModel, hasAnimation, api + ); + + var selectedMode = seriesModel.get('selectedMode'); + + data.diff(oldData) + .add(function (idx) { + var piePiece = new PiePiece(data, idx); + // Default expansion animation + if (isFirstRender && animationType !== 'scale') { + 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 + // Default expansion animation + && animationType !== 'scale' + ) { + var shape = data.getItemLayout(0); + var r = Math.max(api.getWidth(), api.getHeight()) / 2; + + var removeClipPath = bind(group.removeClipPath, group); + group.setClipPath(this._createClipPath( + shape.cx, shape.cy, r, shape.startAngle, shape.clockwise, removeClipPath, seriesModel + )); + } + + this._data = data; + }, + + dispose: function () {}, + + _createClipPath: function ( + cx, cy, r, startAngle, clockwise, cb, seriesModel + ) { + var clipPath = new Sector({ + shape: { + cx: cx, + cy: cy, + r0: 0, + r: r, + startAngle: startAngle, + endAngle: startAngle, + clockwise: clockwise + } + }); + + initProps(clipPath, { + shape: { + endAngle: startAngle + (clockwise ? 1 : -1) * Math.PI * 2 + } + }, seriesModel, cb); + + return clipPath; + }, + + /** + * @implement + */ + containPoint: function (point, seriesModel) { + var data = seriesModel.getData(); + var itemLayout = data.getItemLayout(0); + if (itemLayout) { + var dx = point[0] - itemLayout.cx; + var dy = point[1] - itemLayout.cy; + var radius = Math.sqrt(dx * dx + dy * dy); + return radius <= itemLayout.r && radius >= itemLayout.r0; + } + } + +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var createDataSelectAction = function (seriesType, actionInfos) { + each$1(actionInfos, function (actionInfo) { + actionInfo.update = 'updateView'; + /** + * @payload + * @property {string} seriesName + * @property {string} name + */ + 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, + payload.dataIndex + ); + } + 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 + }; + }); + }); +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +// Pick color from palette for each data item. +// Applicable for charts that require applying color palette +// in data level (like pie, funnel, chord). +var dataColor = function (seriesType) { + return { + getTargetSeries: function (ecModel) { + // Pie and funnel may use diferrent scope + var paletteScope = {}; + var seiresModelMap = createHashMap(); + + ecModel.eachSeriesByType(seriesType, function (seriesModel) { + seriesModel.__paletteScope = paletteScope; + seiresModelMap.set(seriesModel.uid, seriesModel); + }); + + return seiresModelMap; + }, + reset: function (seriesModel, ecModel) { + var dataAll = seriesModel.getRawData(); + var idxMap = {}; + var data = seriesModel.getData(); + + data.each(function (idx) { + var rawIdx = data.getRawIndex(idx); + idxMap[rawIdx] = idx; + }); + + dataAll.each(function (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) { + // FIXME Performance + var itemModel = dataAll.getItemModel(rawIdx); + + var color = itemModel.get('itemStyle.color') + || seriesModel.getColorFromPalette( + dataAll.getName(rawIdx) || (rawIdx + ''), seriesModel.__paletteScope, + dataAll.count() + ); + // 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); + } + }); + } + }; +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +// FIXME emphasis label position is not same with normal label position + +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; + } + } +} + +var labelLayout = 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'); + // Use position in normal or emphasis + var labelPosition = labelModel.get('position') || itemModel.get('emphasis.label.position'); + + var labelLineModel = itemModel.getModel('labelLine'); + 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.getFont(); + + var labelRotate = labelModel.get('rotate') + ? (dx < 0 ? -midAngle + Math.PI : -midAngle) : 0; + var text = seriesModel.getFormattedLabel(idx, 'normal') + || data.getName(idx); + var textRect = 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', + rotation: labelRotate, + inside: isLabelInside + }; + + // Not layout the inside label + if (!isLabelInside) { + labelLayoutList.push(layout.label); + } + }); + if (!hasLabelRotate && seriesModel.get('avoidLabelOverlap')) { + avoidOverlap(labelLayoutList, cx, cy, r, viewWidth, viewHeight); + } +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + + +var PI2$4 = Math.PI * 2; +var RADIAN = Math.PI / 180; + +var pieLayout = function (seriesType, ecModel, api, payload) { + ecModel.eachSeriesByType(seriesType, function (seriesModel) { + var data = seriesModel.getData(); + var valueDim = data.mapDimension('value'); + + var center = seriesModel.get('center'); + var radius = seriesModel.get('radius'); + + if (!isArray(radius)) { + radius = [0, radius]; + } + if (!isArray(center)) { + center = [center, center]; + } + + var width = api.getWidth(); + var height = api.getHeight(); + var size = Math.min(width, height); + var cx = parsePercent$1(center[0], width); + var cy = parsePercent$1(center[1], height); + var r0 = parsePercent$1(radius[0], size / 2); + var r = parsePercent$1(radius[1], size / 2); + + var startAngle = -seriesModel.get('startAngle') * RADIAN; + + var minAngle = seriesModel.get('minAngle') * RADIAN; + + var validDataCount = 0; + data.each(valueDim, function (value) { + !isNaN(value) && validDataCount++; + }); + + var sum = data.getSum(valueDim); + // Sum may be 0 + var unitRadian = Math.PI / (sum || validDataCount) * 2; + + var clockwise = seriesModel.get('clockwise'); + + var roseType = seriesModel.get('roseType'); + var stillShowZeroSum = seriesModel.get('stillShowZeroSum'); + + // [0...max] + var extent = data.getDataExtent(valueDim); + extent[0] = 0; + + // In the case some sector angle is smaller than minAngle + var restAngle = PI2$4; + var valueSumLargerThanMinAngle = 0; + + var currentAngle = startAngle; + var dir = clockwise ? 1 : -1; + + data.each(valueDim, function (value, idx) { + var angle; + if (isNaN(value)) { + data.setItemLayout(idx, { + angle: NaN, + startAngle: NaN, + endAngle: NaN, + clockwise: clockwise, + cx: cx, + cy: cy, + r0: r0, + r: roseType + ? NaN + : r + }); + return; + } + + // FIXME 兼容 2.0 但是 roseType 是 area 的时候才是这样? + if (roseType !== 'area') { + angle = (sum === 0 && stillShowZeroSum) + ? unitRadian : (value * unitRadian); + } + else { + angle = PI2$4 / validDataCount; + } + + 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 + ? linearMap(value, extent, [r0, r]) + : r + }); + + currentAngle = endAngle; + }); + + // Some sector is constrained by minAngle + // Rest sectors needs recalculate angle + if (restAngle < PI2$4 && validDataCount) { + // Average the angle if rest angle is not enough after all angles is + // Constrained by minAngle + if (restAngle <= 1e-3) { + var angle = PI2$4 / validDataCount; + data.each(valueDim, function (value, idx) { + if (!isNaN(value)) { + var layout = data.getItemLayout(idx); + layout.angle = angle; + layout.startAngle = startAngle + dir * idx * angle; + layout.endAngle = startAngle + dir * (idx + 1) * angle; + } + }); + } + else { + unitRadian = restAngle / valueSumLargerThanMinAngle; + currentAngle = startAngle; + data.each(valueDim, function (value, idx) { + if (!isNaN(value)) { + var layout = data.getItemLayout(idx); + var angle = layout.angle === minAngle + ? minAngle : value * unitRadian; + layout.startAngle = currentAngle; + layout.endAngle = currentAngle + dir * angle; + currentAngle += dir * angle; + } + }); + } + } + + labelLayout(seriesModel, r, width, height); + }); +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var dataFilter = function (seriesType) { + return { + seriesType: seriesType, + reset: function (seriesModel, ecModel) { + var legendModels = ecModel.findComponents({ + mainType: 'legend' + }); + if (!legendModels || !legendModels.length) { + return; + } + var data = seriesModel.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; + }); + } + }; +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +createDataSelectAction('pie', [{ + type: 'pieToggleSelect', + event: 'pieselectchanged', + method: 'toggleSelected' +}, { + type: 'pieSelect', + event: 'pieselected', + method: 'select' +}, { + type: 'pieUnSelect', + event: 'pieunselected', + method: 'unSelect' +}]); + +registerVisual(dataColor('pie')); +registerLayout(curry(pieLayout, 'pie')); +registerProcessor(dataFilter('pie')); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +SeriesModel.extend({ + + type: 'series.scatter', + + dependencies: ['grid', 'polar', 'geo', 'singleAxis', 'calendar'], + + getInitialData: function (option, ecModel) { + return createListFromArray(this.getSource(), this); + }, + + brushSelector: 'point', + + getProgressive: function () { + var progressive = this.option.progressive; + if (progressive == null) { + // PENDING + return this.option.large ? 5e3 : this.get('progressive'); + } + return progressive; + }, + + getProgressiveThreshold: function () { + var progressiveThreshold = this.option.progressiveThreshold; + if (progressiveThreshold == null) { + // PENDING + return this.option.large ? 1e4 : this.get('progressiveThreshold'); + } + return progressiveThreshold; + }, + + 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, + // cursor: null, + + // label: { + // show: false + // distance: 5, + // formatter: 标签文本格式器,同Tooltip.formatter,不支持异步回调 + // position: 默认自适应,水平布局为'top',垂直布局为'right',可选为 + // 'inside'|'left'|'right'|'top'|'bottom' + // 默认使用全局文本样式,详见TEXTSTYLE + // }, + itemStyle: { + opacity: 0.8 + // color: 各异 + } + + // progressive: null + } + +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +// TODO Batch by color + +var BOOST_SIZE_THRESHOLD = 4; + +var LargeSymbolPath = extendShape({ + + shape: { + points: null + }, + + symbolProxy: null, + + buildPath: function (path, shape) { + var points = shape.points; + var size = shape.size; + + var symbolProxy = this.symbolProxy; + var symbolProxyShape = symbolProxy.shape; + var ctx = path.getContext ? path.getContext() : path; + var canBoost = ctx && size[0] < BOOST_SIZE_THRESHOLD; + + // Do draw in afterBrush. + if (canBoost) { + return; + } + + for (var i = 0; i < points.length;) { + var x = points[i++]; + var y = points[i++]; + + if (isNaN(x) || isNaN(y)) { + continue; + } + + symbolProxyShape.x = x - size[0] / 2; + symbolProxyShape.y = y - size[1] / 2; + symbolProxyShape.width = size[0]; + symbolProxyShape.height = size[1]; + + symbolProxy.buildPath(path, symbolProxyShape, true); + } + }, + + afterBrush: function (ctx) { + var shape = this.shape; + var points = shape.points; + var size = shape.size; + var canBoost = size[0] < BOOST_SIZE_THRESHOLD; + + if (!canBoost) { + return; + } + + this.setTransform(ctx); + // PENDING If style or other canvas status changed? + for (var i = 0; i < points.length;) { + var x = points[i++]; + var y = points[i++]; + if (isNaN(x) || isNaN(y)) { + continue; + } + // fillRect is faster than building a rect path and draw. + // And it support light globalCompositeOperation. + ctx.fillRect( + x - size[0] / 2, y - size[1] / 2, + size[0], size[1] + ); + } + + this.restoreTransform(ctx); + }, + + findDataIndex: function (x, y) { + // TODO ??? + // Consider transform + + var shape = this.shape; + var points = shape.points; + var size = shape.size; + + var w = Math.max(size[0], 4); + var h = Math.max(size[1], 4); + + // Not consider transform + // Treat each element as a rect + // top down traverse + for (var idx = points.length / 2 - 1; idx >= 0; idx--) { + var i = idx * 2; + var x0 = points[i] - w / 2; + var y0 = points[i + 1] - h / 2; + if (x >= x0 && y >= y0 && x <= x0 + w && y <= y0 + h) { + return idx; + } + } + + return -1; + } +}); + +function LargeSymbolDraw() { + this.group = new Group(); +} + +var largeSymbolProto = LargeSymbolDraw.prototype; + +largeSymbolProto.isPersistent = function () { + return !this._incremental; +}; + +/** + * Update symbols draw by new data + * @param {module:echarts/data/List} data + */ +largeSymbolProto.updateData = function (data) { + this.group.removeAll(); + var symbolEl = new LargeSymbolPath({ + rectHover: true, + cursor: 'default' + }); + + symbolEl.setShape({ + points: data.getLayout('symbolPoints') + }); + this._setCommon(symbolEl, data); + this.group.add(symbolEl); + + this._incremental = null; +}; + +largeSymbolProto.updateLayout = function (data) { + if (this._incremental) { + return; + } + + var points = data.getLayout('symbolPoints'); + this.group.eachChild(function (child) { + if (child.startIndex != null) { + var len = (child.endIndex - child.startIndex) * 2; + var byteOffset = child.startIndex * 4 * 2; + points = new Float32Array(points.buffer, byteOffset, len); + } + child.setShape('points', points); + }); +}; + +largeSymbolProto.incrementalPrepareUpdate = function (data) { + this.group.removeAll(); + + this._clearIncremental(); + // Only use incremental displayables when data amount is larger than 2 million. + // PENDING Incremental data? + if (data.count() > 2e6) { + if (!this._incremental) { + this._incremental = new IncrementalDisplayble({ + silent: true + }); + } + this.group.add(this._incremental); + } + else { + this._incremental = null; + } +}; + +largeSymbolProto.incrementalUpdate = function (taskParams, data) { + var symbolEl; + if (this._incremental) { + symbolEl = new LargeSymbolPath(); + this._incremental.addDisplayable(symbolEl, true); + } + else { + symbolEl = new LargeSymbolPath({ + rectHover: true, + cursor: 'default', + startIndex: taskParams.start, + endIndex: taskParams.end + }); + symbolEl.incremental = true; + this.group.add(symbolEl); + } + + symbolEl.setShape({ + points: data.getLayout('symbolPoints') + }); + this._setCommon(symbolEl, data, !!this._incremental); +}; + +largeSymbolProto._setCommon = function (symbolEl, data, isIncremental) { + var hostModel = data.hostModel; + + // TODO + // if (data.hasItemVisual.symbolSize) { + // // TODO typed array? + // symbolEl.setShape('sizes', data.mapArray( + // function (idx) { + // var size = data.getItemVisual(idx, 'symbolSize'); + // return (size instanceof Array) ? size : [size, size]; + // } + // )); + // } + // else { + var size = data.getVisual('symbolSize'); + symbolEl.setShape('size', (size instanceof Array) ? size : [size, size]); + // } + + // Create symbolProxy to build path for each data + symbolEl.symbolProxy = createSymbol( + data.getVisual('symbol'), 0, 0, 0, 0 + ); + // Use symbolProxy setColor method + symbolEl.setColor = symbolEl.symbolProxy.setColor; + + var extrudeShadow = symbolEl.shape.size[0] < BOOST_SIZE_THRESHOLD; + symbolEl.useStyle( + // Draw shadow when doing fillRect is extremely slow. + hostModel.getModel('itemStyle').getItemStyle(extrudeShadow ? ['color', 'shadowBlur', 'shadowColor'] : ['color']) + ); + + var visualColor = data.getVisual('color'); + if (visualColor) { + symbolEl.setColor(visualColor); + } + + if (!isIncremental) { + // Enable tooltip + // PENDING May have performance issue when path is extremely large + symbolEl.seriesIndex = hostModel.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 + (symbolEl.startIndex || 0); + } + }); + } +}; + +largeSymbolProto.remove = function () { + this._clearIncremental(); + this._incremental = null; + this.group.removeAll(); +}; + +largeSymbolProto._clearIncremental = function () { + var incremental = this._incremental; + if (incremental) { + incremental.clearDisplaybles(); + } +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +extendChartView({ + + type: 'scatter', + + render: function (seriesModel, ecModel, api) { + var data = seriesModel.getData(); + + var symbolDraw = this._updateSymbolDraw(data, seriesModel); + symbolDraw.updateData(data); + + this._finished = true; + }, + + incrementalPrepareRender: function (seriesModel, ecModel, api) { + var data = seriesModel.getData(); + var symbolDraw = this._updateSymbolDraw(data, seriesModel); + + symbolDraw.incrementalPrepareUpdate(data); + + this._finished = false; + }, + + incrementalRender: function (taskParams, seriesModel, ecModel) { + this._symbolDraw.incrementalUpdate(taskParams, seriesModel.getData()); + + this._finished = taskParams.end === seriesModel.getData().count(); + }, + + updateTransform: function (seriesModel, ecModel, api) { + var data = seriesModel.getData(); + // Must mark group dirty and make sure the incremental layer will be cleared + // PENDING + this.group.dirty(); + + if (!this._finished || data.count() > 1e4 || !this._symbolDraw.isPersistent()) { + return { + update: true + }; + } + else { + var res = pointsLayout().reset(seriesModel); + if (res.progress) { + res.progress({ start: 0, end: data.count() }, data); + } + + this._symbolDraw.updateLayout(data); + } + }, + + _updateSymbolDraw: function (data, seriesModel) { + var symbolDraw = this._symbolDraw; + var pipelineContext = seriesModel.pipelineContext; + var isLargeDraw = pipelineContext.large; + + if (!symbolDraw || isLargeDraw !== this._isLargeDraw) { + symbolDraw && symbolDraw.remove(); + symbolDraw = this._symbolDraw = isLargeDraw + ? new LargeSymbolDraw() + : new SymbolDraw(); + this._isLargeDraw = isLargeDraw; + this.group.removeAll(); + } + + this.group.add(symbolDraw.group); + + return symbolDraw; + }, + + remove: function (ecModel, api) { + this._symbolDraw && this._symbolDraw.remove(true); + this._symbolDraw = null; + }, + + dispose: function () {} +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +// import * as zrUtil from 'zrender/src/core/util'; + +// In case developer forget to include grid component +registerVisual(visualSymbol('scatter', 'circle')); +registerLayout(pointsLayout('scatter')); + +// echarts.registerProcessor(function (ecModel, api) { +// ecModel.eachSeriesByType('scatter', function (seriesModel) { +// var data = seriesModel.getData(); +// var coordSys = seriesModel.coordinateSystem; +// if (coordSys.type !== 'geo') { +// return; +// } +// var startPt = coordSys.pointToData([0, 0]); +// var endPt = coordSys.pointToData([api.getWidth(), api.getHeight()]); + +// var dims = zrUtil.map(coordSys.dimensions, function (dim) { +// return data.mapDimension(dim); +// }); +// var range = {}; +// range[dims[0]] = [Math.min(startPt[0], endPt[0]), Math.max(startPt[0], endPt[0])]; +// range[dims[1]] = [Math.min(startPt[1], endPt[1]), Math.max(startPt[1], endPt[1])]; + +// data.selectRange(range); +// }); +// }); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +function 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; +} + +inherits(IndicatorAxis, Axis); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +// TODO clockwise + +function Radar(radarModel, ecModel, api) { + + this._model = radarModel; + /** + * Radar dimensions + * @type {Array.} + */ + this.dimensions = []; + + this._indicatorAxes = 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 = parsePercent$1(center[0], viewWidth); + this.cy = parsePercent$1(center[1], viewHeight); + + this.startAngle = radarModel.get('startAngle') * Math.PI / 180; + + this.r = parsePercent$1(radarModel.get('radius'), viewSize); + + each$1(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; + each$1(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(); + each$1(indicatorAxes, function (indicatorAxis) { + indicatorAxis.scale.unionExtentFromData(data, data.mapDimension(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. + each$1(indicatorAxes, function (indicatorAxis, idx) { + var rawExtent = getScaleExtent(indicatorAxis.scale, indicatorAxis.model); + niceScaleExtent(indicatorAxis.scale, indicatorAxis.model); + + var axisModel = indicatorAxis.model; + var scale = indicatorAxis.scale; + var fixedMin = axisModel.getMin(); + var fixedMax = axisModel.getMax(); + var interval = scale.getInterval(); + + if (fixedMin != null && fixedMax != null) { + // User set min, max, divide to get new interval + scale.setExtent(+fixedMin, +fixedMax); + 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( + round$1(center - halfSplitNumber * interval), + round$1(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; +}; + +CoordinateSystemManager.register('radar', Radar); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var valueAxisDefault = axisDefault.valueAxis; + +function defaultsShow(opt, show) { + return defaults({ + show: show + }, opt); +} + +var RadarModel = 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'); + var showName = this.get('name.show'); + var nameFormatter = this.get('name.formatter'); + var nameGap = this.get('nameGap'); + var triggerEvent = this.get('triggerEvent'); + + var indicatorModels = 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; + } + var iNameTextStyle = nameTextStyle; + if(indicatorOpt.color != null) { + iNameTextStyle = defaults({color: indicatorOpt.color}, nameTextStyle); + } + // Use same configuration + indicatorOpt = merge(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: iNameTextStyle, + triggerEvent: triggerEvent + }, false); + if (!showName) { + indicatorOpt.name = ''; + } + if (typeof nameFormatter === 'string') { + var indName = indicatorOpt.name; + indicatorOpt.name = nameFormatter.replace('{value}', indName != null ? indName : ''); + } + else if (typeof nameFormatter === 'function') { + indicatorOpt.name = nameFormatter( + indicatorOpt.name, indicatorOpt + ); + } + var model = extend( + new Model(indicatorOpt, null, this.ecModel), + axisModelCommonMixin + ); + + // For triggerEvent. + model.mainType = 'radar'; + model.componentIndex = this.componentIndex; + + return model; + }, 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: 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: [] + } +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var axisBuilderAttrs$1 = [ + 'axisLine', 'axisTickLabel', 'axisName' +]; + +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 = 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; + }); + + each$1(axisBuilders, function (axisBuilder) { + each$1(axisBuilderAttrs$1, axisBuilder.add, axisBuilder); + this.group.add(axisBuilder.getGroup()); + }, this); + }, + + _buildSplitLineAndArea: function (radarModel) { + var radar = radarModel.coordinateSystem; + 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 = isArray(splitLineColors) ? splitLineColors : [splitLineColors]; + splitAreaColors = 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 Circle({ + shape: { + cx: cx, + cy: cy, + r: ticksRadius[i].coord + } + })); + } + if (showSplitArea && i < ticksRadius.length - 1) { + var colorIndex = getColorIndex(splitAreas, splitAreaColors, i); + splitAreas[colorIndex].push(new Ring({ + shape: { + cx: cx, + cy: cy, + r0: ticksRadius[i].coord, + r: ticksRadius[i + 1].coord + } + })); + } + } + } + // Polyyon + else { + var realSplitNumber; + var axesTicksPoints = map(indicatorAxes, function (indicatorAxis, idx) { + var ticksCoords = indicatorAxis.getTicksCoords(); + realSplitNumber = realSplitNumber == null + ? ticksCoords.length - 1 + : Math.min(ticksCoords.length - 1, realSplitNumber); + return map(ticksCoords, function (tickCoord) { + return radar.coordToPoint(tickCoord.coord, idx); + }); + }); + + var prevPoints = []; + for (var i = 0; i <= realSplitNumber; 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 (__DEV__) { + console.error('Can\'t draw value axis ' + i); + } + } + + if (showSplitLine) { + var colorIndex = getColorIndex(splitLines, splitLineColors, i); + splitLines[colorIndex].push(new Polyline({ + shape: { + points: points + } + })); + } + if (showSplitArea && prevPoints) { + var colorIndex = getColorIndex(splitAreas, splitAreaColors, i - 1); + splitAreas[colorIndex].push(new Polygon({ + shape: { + points: points.concat(prevPoints) + } + })); + } + prevPoints = points.slice().reverse(); + } + } + + var lineStyle = lineStyleModel.getLineStyle(); + var areaStyle = areaStyleModel.getAreaStyle(); + // Add splitArea before splitLine + each$1(splitAreas, function (splitAreas, idx) { + this.group.add(mergePath( + splitAreas, { + style: defaults({ + stroke: 'none', + fill: splitAreaColors[idx % splitAreaColors.length] + }, areaStyle), + silent: true + } + )); + }, this); + + each$1(splitLines, function (splitLines, idx) { + this.group.add(mergePath( + splitLines, { + style: defaults({ + fill: 'none', + stroke: splitLineColors[idx % splitLineColors.length] + }, lineStyle), + silent: true + } + )); + }, this); + + } +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +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.getRawData(); + }; + }, + + getInitialData: function (option, ecModel) { + return createListSimply(this, { + generateCoord: 'indicator_', + generateCoordCount: Infinity + }); + }, + + formatTooltip: function (dataIndex) { + var data = this.getData(); + var coordSys = this.coordinateSystem; + var indicatorAxes = coordSys.getIndicatorAxes(); + var name = this.getData().getName(dataIndex); + return encodeHTML(name === '' ? this.name : name) + '
' + + map(indicatorAxes, function (axis, idx) { + var val = data.get(data.mapDimension(axis.dim), dataIndex); + return encodeHTML(axis.name + ' : ' + val); + }).join('
'); + }, + + defaultOption: { + zlevel: 0, + z: 2, + coordinateSystem: 'radar', + legendHoverLink: true, + radarIndex: 0, + lineStyle: { + width: 2, + type: 'solid' + }, + label: { + position: 'top' + }, + // areaStyle: { + // }, + // itemStyle: {} + symbol: 'emptyCircle', + symbolSize: 4 + // symbolRotate: null + } +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +function normalizeSymbolSize(symbolSize) { + if (!isArray(symbolSize)) { + symbolSize = [+symbolSize, +symbolSize]; + } + return symbolSize; +} + +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$$1(data, idx) { + var symbolType = data.getItemVisual(idx, 'symbol') || 'circle'; + var color = data.getItemVisual(idx, 'color'); + if (symbolType === 'none') { + return; + } + var symbolSize = normalizeSymbolSize( + data.getItemVisual(idx, 'symbolSize') + ); + var symbolPath = createSymbol( + symbolType, -1, -1, 2, 2, color + ); + symbolPath.attr({ + style: { + strokeNoScale: true + }, + z2: 100, + scale: [symbolSize[0] / 2, symbolSize[1] / 2] + }); + 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$$1(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 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 Polygon(); + var polyline = new Polyline(); + var target = { + shape: { + points: points + } + }; + polygon.shape.points = getInitialPoints(points); + polyline.shape.points = getInitialPoints(points); + initProps(polygon, target, seriesModel, idx); + initProps(polyline, target, seriesModel, idx); + + var itemGroup = new Group(); + var symbolGroup = new 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 + ); + + updateProps(polyline, target, seriesModel); + 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( + defaults( + itemModel.getModel('lineStyle').getLineStyle(), + { + fill: 'none', + stroke: color + } + ) + ); + polyline.hoverStyle = itemModel.getModel('emphasis.lineStyle').getLineStyle(); + + var areaStyleModel = itemModel.getModel('areaStyle'); + var hoverAreaStyleModel = itemModel.getModel('emphasis.areaStyle'); + var polygonIgnore = areaStyleModel.isEmpty() && areaStyleModel.parentModel.isEmpty(); + var hoverPolygonIgnore = hoverAreaStyleModel.isEmpty() && hoverAreaStyleModel.parentModel.isEmpty(); + + hoverPolygonIgnore = hoverPolygonIgnore && polygonIgnore; + polygon.ignore = polygonIgnore; + + polygon.useStyle( + defaults( + areaStyleModel.getAreaStyle(), + { + fill: color, + opacity: 0.7 + } + ) + ); + polygon.hoverStyle = hoverAreaStyleModel.getAreaStyle(); + + var itemStyle = itemModel.getModel('itemStyle').getItemStyle(['color']); + var itemHoverStyle = itemModel.getModel('emphasis.itemStyle').getItemStyle(); + var labelModel = itemModel.getModel('label'); + var labelHoverModel = itemModel.getModel('emphasis.label'); + symbolGroup.eachChild(function (symbolPath) { + symbolPath.setStyle(itemStyle); + symbolPath.hoverStyle = clone(itemHoverStyle); + + setLabelStyle( + symbolPath.style, symbolPath.hoverStyle, labelModel, labelHoverModel, + { + labelFetcher: data.hostModel, + labelDataIndex: idx, + labelDimIndex: symbolPath.__dimIdx, + defaultText: data.get(data.dimensions[symbolPath.__dimIdx], idx), + autoColor: color, + isRectText: true + } + ); + }); + + 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); + + setHoverStyle(itemGroup); + }); + + this._data = data; + }, + + remove: function () { + this.group.removeAll(); + this._data = null; + }, + + dispose: function () {} +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + + +var radarLayout = 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); + } + var axes = coordSys.getIndicatorAxes(); + for (var i = 0; i < axes.length; i++) { + data.each(data.mapDimension(axes[i].dim), pointsConverter); + } + + data.each(function (idx) { + // Close polygon + points[idx][0] && points[idx].push(points[idx][0].slice()); + data.setItemLayout(idx, points[idx]); + }); + }); +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +// Backward compat for radar chart in 2 +var backwardCompat$1 = function (option) { + var polarOptArr = option.polar; + if (polarOptArr) { + if (!isArray(polarOptArr)) { + polarOptArr = [polarOptArr]; + } + var polarNotRadar = []; + each$1(polarOptArr, function (polarOpt, idx) { + if (polarOpt.indicator) { + if (polarOpt.type && !polarOpt.shape) { + polarOpt.shape = polarOpt.type; + } + option.radar = option.radar || []; + if (!isArray(option.radar)) { + option.radar = [option.radar]; + } + option.radar.push(polarOpt); + } + else { + polarNotRadar.push(polarOpt); + } + }); + option.polar = polarNotRadar; + } + each$1(option.series, function (seriesOpt) { + if (seriesOpt && seriesOpt.type === 'radar' && seriesOpt.polarIndex) { + seriesOpt.radarIndex = seriesOpt.polarIndex; + } + }); +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + + +// Must use radar component +registerVisual(dataColor('radar')); +registerVisual(visualSymbol('radar', 'circle')); +registerLayout(radarLayout); +registerProcessor(dataFilter('radar')); +registerPreprocessor(backwardCompat$1); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * Simple view coordinate system + * Mapping given x, y to transformd view x, y + */ + +var v2ApplyTransform$1 = applyTransform; + +// Dummy transform node +function TransformDummy() { + Transformable.call(this); +} +mixin(TransformDummy, Transformable); + +function View(name) { + /** + * @type {string} + */ + this.name = name; + + /** + * @type {Object} + */ + this.zoomLimit; + + Transformable.call(this); + + this._roamTransformable = new TransformDummy(); + + this._rawTransformable = 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) { + 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 rawTransform = this._rawTransformable; + + rawTransform.transform = rect.calculateTransform( + new BoundingRect(x, y, width, height) + ); + + rawTransform.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 + * @param {boolean} noRoam + * @param {Array.} [out] + * @return {Array.} + */ + dataToPoint: function (data, noRoam, out) { + var transform = noRoam ? this._rawTransform : this.transform; + out = out || []; + return transform + ? v2ApplyTransform$1(out, data, transform) + : copy(out, data); + }, + + /** + * Convert a (x, y) point to (lon, lat) data + * @param {Array.} point + * @return {Array.} + */ + pointToData: function (point) { + var invTransform = this.invTransform; + return invTransform + ? v2ApplyTransform$1([], point, invTransform) + : [point[0], point[1]]; + }, + + /** + * @implements + * see {module:echarts/CoodinateSystem} + */ + convertToPixel: curry(doConvert$1, 'dataToPoint'), + + /** + * @implements + * see {module:echarts/CoodinateSystem} + */ + convertFromPixel: curry(doConvert$1, 'pointToData'), + + /** + * @implements + * see {module:echarts/CoodinateSystem} + */ + containPoint: function (point) { + return this.getViewRectAfterRoam().contain(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; + // } +}; + +mixin(View, Transformable); + +function doConvert$1(methodName, ecModel, finder, value) { + var seriesModel = finder.seriesModel; + var coordSys = seriesModel ? seriesModel.coordinateSystem : null; // e.g., graph. + return coordSys === this ? coordSys[methodName](value) : null; +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +// Fix for 南海诸岛 + +var geoCoord = [126, 25]; + +var points$1 = [ + [[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$1 = 0; i$1 < points$1.length; i$1++) { + for (var k = 0; k < points$1[i$1].length; k++) { + points$1[i$1][k][0] /= 10.5; + points$1[i$1][k][1] /= -10.5 / 0.75; + + points$1[i$1][k][0] += geoCoord[0]; + points$1[i$1][k][1] += geoCoord[1]; + } +} + +var fixNanhai = function (geo) { + if (geo.map === 'china') { + geo.regions.push(new Region( + '南海诸岛', + map(points$1, function (exterior) { + return { + type: 'polygon', + exterior: exterior + }; + }), geoCoord + )); + } +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var coordsOffsetMap = { + '南海诸岛' : [32, 80], + // 全国 + '广东': [0, -10], + '香港': [10, 5], + '澳门': [-10, 10], + //'北京': [-10, 0], + '天津': [5, 5] +}; + +var fixTextCoord = function (geo) { + each$1(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); + } + }); +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var geoCoordMap = { + 'Russia': [100, 60], + 'United States': [-99, 38], + 'United States of America': [-99, 38] +}; + +var fixGeoCoord = function (geo) { + each$1(geo.regions, function (region) { + var geoCoord = geoCoordMap[region.name]; + if (geoCoord) { + var cp = region.center; + cp[0] = geoCoord[0]; + cp[1] = geoCoord[1]; + } + }); +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +// Fix for 钓鱼岛 + +// var Region = require('../Region'); +// var zrUtil = require('zrender/src/core/util'); + +// var geoCoord = [126, 25]; + +var points$2 = [ + [ + [123.45165252685547, 25.73527164402261], + [123.49731445312499, 25.73527164402261], + [123.49731445312499, 25.750734064600884], + [123.45165252685547, 25.750734064600884], + [123.45165252685547, 25.73527164402261] + ] +]; + +var fixDiaoyuIsland = function (geo) { + if (geo.map === 'china') { + for (var i = 0, len = geo.regions.length; i < len; ++i) { + if (geo.regions[i].name === '台湾') { + geo.regions[i].geometries.push({ + type: 'polygon', + exterior: points$2[0] + }); + } + } + } +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +// Geo fix functions +var geoFixFuncs = [ + fixNanhai, + fixTextCoord, + fixGeoCoord, + fixDiaoyuIsland +]; + +/** + * [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$$1, geoJson, specialAreas, nameMap) { + + View.call(this, name); + + /** + * Map type + * @type {string} + */ + this.map = map$$1; + + this._nameCoordMap = createHashMap(); + + 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$1(geoJson) : []; + } + catch (e) { + throw 'Invalid geoJson format\n' + e.message; + } + specialAreas = specialAreas || {}; + nameMap = nameMap || {}; + var regions = this.regions; + var regionsMap = createHashMap(); + for (var i = 0; i < regions.length; i++) { + var regionName = regions[i].name; + // Try use the alias in nameMap + regionName = nameMap.hasOwnProperty(regionName) ? nameMap[regionName] : regionName; + regions[i].name = regionName; + + regionsMap.set(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; + + each$1(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 rawTransformable = this._rawTransformable; + + rawTransformable.transform = rect.calculateTransform( + new BoundingRect(x, y, width, height) + ); + + rawTransformable.decomposeTransform(); + + var scale = rawTransformable.scale; + scale[1] = -scale[1]; + + rawTransformable.updateTransform(); + + this._updateTransform(); + }, + + /** + * @param {string} name + * @return {module:echarts/coord/geo/Region} + */ + getRegion: function (name) { + return this._regionsMap.get(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.set(name, geoCoord); + }, + + /** + * Get geoCoord by name + * @param {string} name + * @return {Array.} + */ + getGeoCoord: function (name) { + return this._nameCoordMap.get(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)); + }, + + /** + * @param {string|Array.} data + * @param {boolean} noRoam + * @param {Array.} [out] + * @return {Array.} + */ + dataToPoint: function (data, noRoam, out) { + if (typeof data === 'string') { + // Map area name to geoCoord + data = this.getGeoCoord(data); + } + if (data) { + return View.prototype.dataToPoint.call(this, data, noRoam, out); + } + }, + + /** + * @inheritDoc + */ + convertToPixel: curry(doConvert, 'dataToPoint'), + + /** + * @inheritDoc + */ + convertFromPixel: curry(doConvert, 'pointToData') + +}; + +mixin(Geo, View); + +function doConvert(methodName, ecModel, finder, value) { + var geoModel = finder.geoModel; + var seriesModel = finder.seriesModel; + + var coordSys = geoModel + ? geoModel.coordinateSystem + : seriesModel + ? ( + seriesModel.coordinateSystem // For map. + || (seriesModel.getReferringComponents('geo')[0] || {}).coordinateSystem + ) + : null; + + return coordSys === this ? coordSys[methodName](value) : null; +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * 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 boundingCoords = geoModel.get('boundingCoords'); + if (boundingCoords != null) { + var leftTop = boundingCoords[0]; + var rightBottom = boundingCoords[1]; + if (isNaN(leftTop[0]) || isNaN(leftTop[1]) || isNaN(rightBottom[0]) || isNaN(rightBottom[1])) { + if (__DEV__) { + console.error('Invalid boundingCoords'); + } + } + else { + this.setBoundingRect(leftTop[0], leftTop[1], rightBottom[0] - leftTop[0], rightBottom[1] - leftTop[1]); + } + } + + 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 = [ + parsePercent$1(center[0], viewWidth), + parsePercent$1(center[1], viewHeight) + ]; + size = parsePercent$1(size, Math.min(viewWidth, viewHeight)); + + if (!isNaN(center[0]) && !isNaN(center[1]) && !isNaN(size)) { + useCenterAndSize = true; + } + else { + if (__DEV__) { + 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 = 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) { + each$1(model.get('geoCoord'), function (geoCoord, name) { + geo.addGeoCoord(name, geoCoord); + }); +} + +if (__DEV__) { + 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 = getMap(name); + if (__DEV__) { + 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) { + if (!seriesModel.getHostGeoModel()) { + var mapType = seriesModel.getMapType(); + mapModelGroupBySeries[mapType] = mapModelGroupBySeries[mapType] || []; + mapModelGroupBySeries[mapType].push(seriesModel); + } + }); + + each$1(mapModelGroupBySeries, function (mapSeries, mapType) { + var mapData = getMap(mapType); + if (__DEV__) { + if (!mapData) { + mapNotExistsError(mapSeries[0].get('map')); + } + } + + var nameMapList = map(mapSeries, function (singleMapSeries) { + return singleMapSeries.get('nameMap'); + }); + var geo = new Geo( + mapType, mapType, + mapData && mapData.geoJson, mapData && mapData.specialAreas, + mergeAll(nameMapList) + ); + geo.zoomLimit = retrieve.apply(null, map(mapSeries, function (singleMapSeries) { + return singleMapSeries.get('scaleLimit'); + })); + geoList.push(geo); + + // Inject resize method + geo.resize = resizeGeo; + + geo.resize(mapSeries[0], api); + + each$1(mapSeries, function (singleMapSeries) { + singleMapSeries.coordinateSystem = geo; + + setGeoCoords(geo, singleMapSeries); + }); + }); + + return geoList; + }, + + /** + * Fill given regions array + * @param {Array.} originRegionArr + * @param {string} mapName + * @param {Object} [nameMap] + * @return {Array} + */ + getFilledRegions: function (originRegionArr, mapName, nameMap) { + // Not use the original + var regionsArr = (originRegionArr || []).slice(); + nameMap = nameMap || {}; + + var map$$1 = getMap(mapName); + var geoJson = map$$1 && map$$1.geoJson; + if (!geoJson) { + if (__DEV__) { + mapNotExistsError(mapName); + } + return originRegionArr; + } + + var dataNameMap = createHashMap(); + var features = geoJson.features; + for (var i = 0; i < regionsArr.length; i++) { + dataNameMap.set(regionsArr[i].name, regionsArr[i]); + } + + for (var i = 0; i < features.length; i++) { + var name = features[i].properties.name; + if (!dataNameMap.get(name)) { + if (nameMap.hasOwnProperty(name)) { + name = nameMap[name]; + } + regionsArr.push({ + name: name + }); + } + } + return regionsArr; + } +}; + +registerCoordinateSystem('geo', geoCreator); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var MapSeries = SeriesModel.extend({ + + type: 'series.map', + + dependencies: ['geo'], + + 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) { + + // this._fillOption(option, this.getMapType()); + // this.option = option; + + MapSeries.superApply(this, 'init', arguments); + + this.updateSelectedMap(this._createSelectableList()); + }, + + getInitialData: function (option) { + return createListSimply(this, ['value']); + }, + + mergeOption: function (newOption) { + // this._fillOption(newOption, this.getMapType()); + + MapSeries.superApply(this, 'mergeOption', arguments); + + this.updateSelectedMap(this._createSelectableList()); + }, + + _createSelectableList: function () { + var data = this.getRawData(); + var valueDim = data.mapDimension('value'); + var targetList = []; + for (var i = 0, len = data.count(); i < len; i++) { + targetList.push({ + name: data.getName(i), + value: data.get(valueDim, i), + selected: retrieveRawAttr(data, i, 'selected') + }); + } + + targetList = geoCreator.getFilledRegions(targetList, this.getMapType(), this.option.nameMap); + + return targetList; + }, + + /** + * If no host geo model, return null, which means using a + * inner exclusive geo model. + */ + getHostGeoModel: function () { + var geoIndex = this.option.geoIndex; + return geoIndex != null + ? this.dependentModels.geo[geoIndex] + : null; + }, + + getMapType: function () { + return (this.getHostGeoModel() || this).option.map; + }, + + _fillOption: function (option, mapName) { + // Shallow clone + // option = zrUtil.extend({}, option); + + // option.data = geoCreator.getFilledRegions(option.data, mapName, option.nameMap); + + // 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 ? + var data = this.getData(); + return data.get(data.mapDimension('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); + var valueDim = data.mapDimension('value'); + if (!isNaN(seriesGroup[i].originalData.get(valueDim, otherIndex))) { + seriesNames.push( + encodeHTML(seriesGroup[i].name) + ); + } + } + + return seriesNames.join(', ') + '
' + + encodeHTML(name + ' : ' + formattedValue); + }, + + /** + * @implement + */ + getTooltipPosition: function (dataIndex) { + if (dataIndex != null) { + var name = this.getData().getName(dataIndex); + var geo = this.coordinateSystem; + var region = geo.getRegion(name); + + return region && geo.dataToPoint(region.center); + } + }, + + setZoom: function (zoom) { + this.option.zoom = zoom; + }, + + setCenter: function (center) { + this.option.center = center; + }, + + defaultOption: { + // 一级层叠 + zlevel: 0, + // 二级层叠 + z: 2, + + coordinateSystem: 'geo', + + // map should be explicitly specified since ec3. + map: '', + + // If `geoIndex` is not specified, a exclusive geo will be + // created. Otherwise use the specified geo component, and + // `map` and `mapType` are ignored. + // geoIndex: 0, + + // '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, + + // Define left-top, right-bottom coords to control view + // For example, [ [180, 90], [-180, -90] ], + // higher priority than center and zoom + boundingCoords: null, + + // Default on center of map + center: null, + + zoom: 1, + + scaleLimit: null, + + label: { + show: false, + color: '#000' + }, + // scaleLimit: null, + itemStyle: { + borderWidth: 0.5, + borderColor: '#444', + areaColor: '#eee' + }, + + emphasis: { + label: { + show: true, + color: 'rgb(100,0,0)' + }, + itemStyle: { + areaColor: 'rgba(255,215,0,0.8)' + } + } + } + +}); + +mixin(MapSeries, selectableMixin); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var ATTR = '\0_ec_interaction_mutex'; + +function take(zr, resourceKey, userKey) { + var store = getStore(zr); + store[resourceKey] = userKey; +} + +function release(zr, resourceKey, userKey) { + var store = getStore(zr); + var uKey = store[resourceKey]; + + if (uKey === userKey) { + store[resourceKey] = null; + } +} + +function isTaken(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. + * } + */ +registerAction( + {type: 'takeGlobalCursor', event: 'globalCursorTaken', update: 'update'}, + function () {} +); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * @alias module:echarts/component/helper/RoamController + * @constructor + * @mixin {module:zrender/mixin/Eventful} + * + * @param {module:zrender/zrender~ZRender} zr + */ +function RoamController(zr) { + + /** + * @type {Function} + */ + this.pointerChecker; + + /** + * @type {module:zrender} + */ + this._zr = zr; + + /** + * @type {Object} + */ + this._opt = {}; + + // Avoid two roamController bind the same handler + var bind$$1 = bind; + var mousedownHandler = bind$$1(mousedown, this); + var mousemoveHandler = bind$$1(mousemove, this); + var mouseupHandler = bind$$1(mouseup, this); + var mousewheelHandler = bind$$1(mousewheel, this); + var pinchHandler = bind$$1(pinch, this); + + Eventful.call(this); + + /** + * @param {Function} pointerChecker + * input: x, y + * output: boolean + */ + this.setPointerChecker = function (pointerChecker) { + this.pointerChecker = pointerChecker; + }; + + /** + * 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' + * @param {Object} [opt] + * @param {Object} [opt.zoomOnMouseWheel=true] + * @param {Object} [opt.moveOnMouseMove=true] + * @param {Object} [opt.preventDefaultMouseMove=true] When pan. + */ + this.enable = function (controlType, opt) { + + // Disable previous first + this.disable(); + + this._opt = defaults(clone(opt) || {}, { + zoomOnMouseWheel: true, + moveOnMouseMove: true, + preventDefaultMouseMove: true + }); + + 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; + }; +} + +mixin(RoamController, Eventful); + + +function mousedown(e) { + if (notLeftMouse(e) + || (e.target && e.target.draggable) + ) { + return; + } + + var x = e.offsetX; + var y = e.offsetY; + + // Only check on mosedown, but not mousemove. + // Mouse can be out of target when mouse moving. + if (this.pointerChecker && this.pointerChecker(e, x, y)) { + this._x = x; + this._y = y; + this._dragging = true; + } +} + +function mousemove(e) { + if (notLeftMouse(e) + || !checkKeyBinding(this, 'moveOnMouseMove', e) + || !this._dragging + || e.gestureEvent === 'pinch' + || isTaken(this._zr, 'globalPan') + ) { + return; + } + + var x = e.offsetX; + var y = e.offsetY; + + var oldX = this._x; + var oldY = this._y; + + var dx = x - oldX; + var dy = y - oldY; + + this._x = x; + this._y = y; + + this._opt.preventDefaultMouseMove && stop(e.event); + + this.trigger('pan', dx, dy, oldX, oldY, x, y); +} + +function mouseup(e) { + if (!notLeftMouse(e)) { + this._dragging = false; + } +} + +function mousewheel(e) { + // wheelDelta maybe -0 in chrome mac. + if (!checkKeyBinding(this, 'zoomOnMouseWheel', e) || e.wheelDelta === 0) { + return; + } + + // 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 (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) { + if (this.pointerChecker && this.pointerChecker(e, zoomX, zoomY)) { + // When mouse is out of roamController rect, + // default befavoius should not be be disabled, otherwise + // page sliding is disabled, contrary to expectation. + stop(e.event); + + this.trigger('zoom', zoomDelta, zoomX, zoomY); + } +} + +function checkKeyBinding(roamController, prop, e) { + var setting = roamController._opt[prop]; + return setting + && (!isString(setting) || e.event[setting + 'Key']); +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + + +/** + * For geo and graph. + * + * @param {Object} controllerHost + * @param {module:zrender/Element} controllerHost.target + */ +function updateViewOnPan(controllerHost, dx, dy) { + var target = controllerHost.target; + var pos = target.position; + pos[0] += dx; + pos[1] += dy; + target.dirty(); +} + +/** + * For geo and graph. + * + * @param {Object} controllerHost + * @param {module:zrender/Element} controllerHost.target + * @param {number} controllerHost.zoom + * @param {number} controllerHost.zoomLimit like: {min: 1, max: 2} + */ +function updateViewOnZoom(controllerHost, zoomDelta, zoomX, zoomY) { + var target = controllerHost.target; + var zoomLimit = controllerHost.zoomLimit; + var pos = target.position; + var scale = target.scale; + + var newZoom = controllerHost.zoom = controllerHost.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 / controllerHost.zoom; + controllerHost.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(); +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + + +var IRRELEVANT_EXCLUDES = {'axisPointer': 1, 'tooltip': 1, 'brush': 1}; + +/** + * Avoid that: mouse click on a elements that is over geo or graph, + * but roam is triggered. + */ +function onIrrelevantElement(e, api, targetCoordSysModel) { + var model = api.getComponentByElement(e.topTarget); + // If model is axisModel, it works only if it is injected with coordinateSystem. + var coordSys = model && model.coordinateSystem; + return model + && model !== targetCoordSysModel + && !IRRELEVANT_EXCLUDES[model.mainType] + && (coordSys && coordSys.model !== targetCoordSysModel); +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +function getFixedItemStyle(model, scale) { + var itemStyle = model.getItemStyle(); + var areaColor = model.get('areaColor'); + + // If user want the color not to be changed when hover, + // they should both set areaColor and color to be null. + if (areaColor != null) { + itemStyle.fill = areaColor; + } + + return itemStyle; +} + +function updateMapSelectHandler(mapDraw, mapOrGeoModel, group, api, fromView) { + group.off('click'); + group.off('mousedown'); + + if (mapOrGeoModel.get('selectedMode')) { + + group.on('mousedown', function () { + mapDraw._mouseDownFlag = true; + }); + + group.on('click', function (e) { + if (!mapDraw._mouseDownFlag) { + return; + } + mapDraw._mouseDownFlag = false; + + var el = e.target; + while (!el.__regions) { + el = el.parent; + } + if (!el) { + return; + } + + var action = { + type: (mapOrGeoModel.mainType === 'geo' ? 'geo' : 'map') + 'ToggleSelect', + batch: map(el.__regions, function (region) { + return { + 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) { + each$1(otherRegionEl.__regions, function (region) { + otherRegionEl.trigger(mapOrGeoModel.isSelected(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 Group(); + + /** + * @type {module:echarts/component/helper/RoamController} + * @private + */ + this._controller = new RoamController(api.getZr()); + + /** + * @type {Object} {target, zoom, zoomLimit} + * @private + */ + this._controllerHost = {target: updateGroup ? group : null}; + + /** + * @type {module:zrender/container/Group} + * @readOnly + */ + this.group = group; + + /** + * @type {boolean} + * @private + */ + this._updateGroup = updateGroup; + + /** + * This flag is used to make sure that only one among + * `pan`, `zoom`, `click` can occurs, otherwise 'selected' + * action may be triggered when `pan`, which is unexpected. + * @type {booelan} + */ + this._mouseDownFlag; +} + +MapDraw.prototype = { + + constructor: MapDraw, + + draw: function (mapOrGeoModel, ecModel, api, fromView, payload) { + + var isGeo = mapOrGeoModel.mainType === 'geo'; + + // Map series has data. GEO model that controlled by map series + // will be assigned with map data. Other GEO model has no data. + var data = mapOrGeoModel.getData && mapOrGeoModel.getData(); + isGeo && ecModel.eachComponent({mainType: 'series', subType: 'map'}, function (mapSeries) { + if (!data && mapSeries.getHostGeoModel() === mapOrGeoModel) { + data = mapSeries.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 { + updateProps(group, groupNewProp, mapOrGeoModel); + } + + group.removeAll(); + + var itemStyleAccessPath = ['itemStyle']; + var hoverItemStyleAccessPath = ['emphasis', 'itemStyle']; + var labelAccessPath = ['label']; + var hoverLabelAccessPath = ['emphasis', 'label']; + var nameMap = createHashMap(); + + each$1(geo.regions, function (region) { + + // Consider in GeoJson properties.name may be duplicated, for example, + // there is multiple region named "United Kindom" or "France" (so many + // colonies). And it is not appropriate to merge them in geo, which + // will make them share the same label and bring trouble in label + // location calculation. + var regionGroup = nameMap.get(region.name) + || nameMap.set(region.name, new Group()); + + var compoundPath = new 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; + } + } + + each$1(region.geometries, function (geometry) { + if (geometry.type !== 'polygon') { + return; + } + compoundPath.shape.paths.push(new Polygon({ + shape: { + points: geometry.exterior + } + })); + + for (var i = 0; i < (geometry.interiors ? geometry.interiors.length : 0); i++) { + compoundPath.shape.paths.push(new Polygon({ + shape: { + points: geometry.interiors[i] + } + })); + } + }); + + 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(data.mapDimension('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 ( + (isGeo || isDataNaN && (showLabel || hoverShowLabel)) + || (itemLayout && itemLayout.showLabel) + ) { + var query = !isGeo ? dataIdx : region.name; + var labelFetcher; + + // Consider dataIdx not found. + if (!data || dataIdx >= 0) { + labelFetcher = mapOrGeoModel; + } + + var textEl = new Text({ + position: region.center.slice(), + scale: [1 / scale[0], 1 / scale[1]], + z2: 10, + silent: true + }); + + setLabelStyle( + textEl.style, textEl.hoverStyle = {}, labelModel, hoverLabelModel, + { + labelFetcher: labelFetcher, + labelDataIndex: query, + defaultText: region.name, + useInsideStyle: false + }, + { + textAlign: 'center', + textVerticalAlign: 'middle' + } + ); + + regionGroup.add(textEl); + } + + // 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) || {} + }; + } + + var groupRegions = regionGroup.__regions || (regionGroup.__regions = []); + groupRegions.push(region); + + setHoverStyle( + regionGroup, + hoverItemStyle, + {hoverSilentOnTouch: !!mapOrGeoModel.get('selectedMode')} + ); + + group.add(regionGroup); + }); + + this._updateController(mapOrGeoModel, ecModel, api); + + updateMapSelectHandler(this, mapOrGeoModel, group, api, fromView); + + updateMapSelected(mapOrGeoModel, group); + }, + + remove: function () { + this.group.removeAll(); + this._controller.dispose(); + this._controllerHost = {}; + }, + + _updateController: function (mapOrGeoModel, ecModel, api) { + var geo = mapOrGeoModel.coordinateSystem; + var controller = this._controller; + var controllerHost = this._controllerHost; + + controllerHost.zoomLimit = mapOrGeoModel.get('scaleLimit'); + controllerHost.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) { + this._mouseDownFlag = false; + + updateViewOnPan(controllerHost, dx, dy); + + api.dispatchAction(extend(makeActionBase(), { + dx: dx, + dy: dy + })); + }, this); + + controller.off('zoom').on('zoom', function (zoom, mouseX, mouseY) { + this._mouseDownFlag = false; + + updateViewOnZoom(controllerHost, zoom, mouseX, mouseY); + + api.dispatchAction(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.setPointerChecker(function (e, x, y) { + return geo.getViewRectAfterRoam().contain(x, y) + && !onIrrelevantElement(e, api, mapOrGeoModel); + }); + } +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +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(); + + if (mapModel.getHostGeoModel()) { + return; + } + + // 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(); + }, + + dispose: function () { + this._mapDraw && this._mapDraw.remove(); + this._mapDraw = null; + }, + + _renderSymbols: function (mapModel, ecModel, api) { + var originalData = mapModel.originalData; + var group = this.group; + + originalData.each(originalData.mapDimension('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 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, + // Do not overlap the first series, on which labels are displayed. + z2: !offset ? 10 : 8 + }); + + // First data on the same region + if (!offset) { + var fullData = mapModel.mainSeries.getData(); + var name = originalData.getName(idx); + + var fullIndex = fullData.indexOfName(name); + + var itemModel = originalData.getItemModel(idx); + var labelModel = itemModel.getModel('label'); + var hoverLabelModel = itemModel.getModel('emphasis.label'); + + var polygonGroups = fullData.getItemGraphicEl(fullIndex); + + var normalText = retrieve2( + mapModel.getFormattedLabel(idx, 'normal'), + name + ); + var emphasisText = retrieve2( + mapModel.getFormattedLabel(idx, 'emphasis'), + normalText + ); + + var onEmphasis = function () { + var hoverStyle = setTextStyle({}, hoverLabelModel, { + text: hoverLabelModel.get('show') ? emphasisText : null + }, {isRectText: true, useInsideStyle: false}, true); + circle.style.extendFrom(hoverStyle); + // Make label upper than others if overlaps. + circle.__mapOriginalZ2 = circle.z2; + circle.z2 += 1; + }; + + var onNormal = function () { + setTextStyle(circle.style, labelModel, { + text: labelModel.get('show') ? normalText : null, + textPosition: labelModel.getShallow('position') || 'bottom' + }, {isRectText: true, useInsideStyle: false}); + + if (circle.__mapOriginalZ2 != null) { + circle.z2 = circle.__mapOriginalZ2; + circle.__mapOriginalZ2 = null; + } + }; + + polygonGroups.on('mouseover', onEmphasis) + .on('mouseout', onNormal) + .on('emphasis', onEmphasis) + .on('normal', onNormal); + + onNormal(); + } + + group.add(circle); + }); + } +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * @param {module:echarts/coord/View} view + * @param {Object} payload + * @param {Object} [zoomLimit] + */ +function updateCenterAndZoom( + 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() + }; +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * @payload + * @property {string} [componentType=series] + * @property {number} [dx] + * @property {number} [dy] + * @property {number} [zoom] + * @property {number} [originX] + * @property {number} [originY] + */ +registerAction({ + type: 'geoRoam', + event: 'geoRoam', + update: 'updateTransform' +}, 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 = 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') { + each$1(componentModel.seriesGroup, function (seriesModel) { + seriesModel.setCenter(res.center); + seriesModel.setZoom(res.zoom); + }); + } + } + ); +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var mapSymbolLayout = function (ecModel) { + + var processedMapType = {}; + + ecModel.eachSeriesByType('map', function (mapSeries) { + var mapType = mapSeries.getMapType(); + if (mapSeries.getHostGeoModel() || processedMapType[mapType]) { + return; + } + + var mapSymbolOffsets = {}; + + each$1(mapSeries.seriesGroup, function (subMapSeries) { + var geo = subMapSeries.coordinateSystem; + var data = subMapSeries.originalData; + if (subMapSeries.get('showLegendSymbol') && ecModel.getComponent('legend')) { + data.each(data.mapDimension('value'), function (value, idx) { + var name = data.getName(idx); + var region = geo.getRegion(name); + + // If input series.data is [11, 22, '-'/null/undefined, 44], + // it will be filled with NaN: [11, 22, NaN, 44] and NaN will + // not be drawn. 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; + }); +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + + +var mapVisual = function (ecModel) { + ecModel.eachSeriesByType('map', function (seriesModel) { + var colorList = seriesModel.get('color'); + var itemStyleModel = seriesModel.getModel('itemStyle'); + + var areaColor = itemStyleModel.get('areaColor'); + var color = itemStyleModel.get('color') + || colorList[seriesModel.seriesIndex % colorList.length]; + + seriesModel.getData().setVisual({ + 'areaColor': areaColor, + 'color': color + }); + }); +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +// FIXME 公用? +/** + * @param {Array.} datas + * @param {string} statisticType 'average' 'sum' + * @inner + */ +function dataStatistics(datas, statisticType) { + var dataNameMap = {}; + + each$1(datas, function (data) { + data.each(data.mapDimension('value'), function (value, idx) { + // Add prefix to avoid conflict with Object.prototype. + var mapKey = 'ec-' + data.getName(idx); + dataNameMap[mapKey] = dataNameMap[mapKey] || []; + if (!isNaN(value)) { + dataNameMap[mapKey].push(value); + } + }); + }); + + return datas[0].map(datas[0].mapDimension('value'), function (value, idx) { + var mapKey = 'ec-' + datas[0].getName(idx); + var sum = 0; + var min = Infinity; + var max = -Infinity; + var len = dataNameMap[mapKey].length; + for (var i = 0; i < len; i++) { + min = Math.min(min, dataNameMap[mapKey][i]); + max = Math.max(max, dataNameMap[mapKey][i]); + sum += dataNameMap[mapKey][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; + }); +} + +var mapDataStatistic = function (ecModel) { + var seriesGroups = {}; + ecModel.eachSeriesByType('map', function (seriesModel) { + var hostGeoModel = seriesModel.getHostGeoModel(); + var key = hostGeoModel ? 'o' + hostGeoModel.id : 'i' + seriesModel.getMapType(); + (seriesGroups[key] = seriesGroups[key] || []).push(seriesModel); + }); + + each$1(seriesGroups, function (seriesList, key) { + var data = dataStatistics( + 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].getHostGeoModel(); + + seriesList[i].setData(data.cloneShallow()); + seriesList[i].mainSeries = seriesList[0]; + } + }); +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var backwardCompat$2 = function (option) { + // Save geoCoord + var mapSeries = []; + each$1(option.series, function (seriesOpt) { + if (seriesOpt && seriesOpt.type === 'map') { + mapSeries.push(seriesOpt); + seriesOpt.map = seriesOpt.map || seriesOpt.mapType; + // Put x, y, width, height, x2, y2 in the top level + defaults(seriesOpt, seriesOpt.mapLocation); + } + }); +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +registerLayout(mapSymbolLayout); +registerVisual(mapVisual); +registerProcessor(PRIORITY.PROCESSOR.STATISTIC, mapDataStatistic); +registerPreprocessor(backwardCompat$2); + +createDataSelectAction('map', [{ + type: 'mapToggleSelect', + event: 'mapselectchanged', + method: 'toggleSelected' +}, { + type: 'mapSelect', + event: 'mapselected', + method: 'select' +}, { + type: 'mapUnSelect', + event: 'mapunselected', + method: 'unSelect' +}]); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * Link lists and struct (graph or tree) + */ + +var each$7 = each$1; + +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$7(datas, function (data) { + each$7(mainData.TRANSFERABLE_METHODS, function (methodName) { + data.wrapMethod(methodName, curry(transferInjection, opt)); + }); + + }); + + // Beyond transfer, additional features should be added to `cloneShallow`. + mainData.wrapMethod('cloneShallow', curry(cloneShallowInjection, opt)); + + // Only mainData trigger change, because struct.update may trigger + // another changable methods, which may bring about dead lock. + each$7(mainData.CHANGABLE_METHODS, function (methodName) { + mainData.wrapMethod(methodName, curry(changeInjection, opt)); + }); + + // Make sure datas contains mainData. + assert$1(datas[mainData.dataType] === mainData); +} + +function transferInjection(opt, res) { + if (isMainData(this)) { + // Transfer datas to new main data. + var datas = 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$7(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$7(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; +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * Tree data structure + * + * @module echarts/data/Tree + */ + +/** + * @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 (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$$1) { + this.dataIndex >= 0 + && this.hostTree.data.setItemLayout(this.dataIndex, layout, merge$$1); + }, + + /** + * @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(); + var leavesModel; + if (!levelModel && (this.children.length === 0 || (this.children.length !== 0 && this.isExpand === false))) { + leavesModel = this.getLeavesModel(); + } + return itemModel.getModel(path, (levelModel || leavesModel || hostTree.hostModel).getModel(path)); + }, + + /** + * @return {module:echarts/model/Model} + */ + getLevelModel: function () { + return (this.hostTree.levelModels || [])[this.depth]; + }, + + /** + * @return {module:echarts/model/Model} + */ + getLeavesModel: function () { + return this.hostTree.leavesModel; + }, + + /** + * @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); + }, + + /** + * if this is an ancestor of another node + * + * @public + * @param {TreeNode} node another node + * @return {boolean} if is ancestor + */ + isAncestorOf: function (node) { + var parent = node.parentNode; + while (parent) { + if (parent === this) { + return true; + } + parent = parent.parentNode; + } + return false; + }, + + /** + * if this is an descendant of another node + * + * @public + * @param {TreeNode} node another node + * @return {boolean} if is descendant + */ + isDescendantOf: function (node) { + return node !== this && node.isAncestorOf(this); + } +}; + +/** + * @constructor + * @alias module:echarts/data/Tree + * @param {module:echarts/model/Model} hostModel + * @param {Array.} levelOptions + * @param {Object} leavesOption + */ +function Tree(hostModel, levelOptions, leavesOption) { + /** + * @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.} treeOptions.levels + * @param {Array.} treeOptions.leaves + * @return module:echarts/data/Tree + */ +Tree.createTree = function (dataRoot, hostModel, treeOptions) { + + var tree = new Tree(hostModel, treeOptions.levels, treeOptions.leaves); + var listData = []; + var dimMax = 1; + + buildHierarchy(dataRoot); + + function buildHierarchy(dataNode, parentNode) { + var value = dataNode.value; + dimMax = Math.max(dimMax, isArray(value) ? value.length : 1); + + 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 dimensionsInfo = createDimensions(listData, { + coordDimensions: ['value'], + dimensionsCount: dimMax + }); + + var list = new List(dimensionsInfo, 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; +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * @file Create data struct and define tree view's series model + */ + +SeriesModel.extend({ + + type: 'series.tree', + + layoutInfo: null, + + // can support the position parameters 'left', 'top','right','bottom', 'width', + // 'height' in the setOption() with 'merge' mode normal. + layoutMode: 'box', + + /** + * Init a tree 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) { + + //create an virtual root + var root = {name: option.name, children: option.data}; + + var leaves = option.leaves || {}; + + var treeOption = {}; + + treeOption.leaves = leaves; + + var tree = Tree.createTree(root, this, treeOption); + + var treeDepth = 0; + + tree.eachNode('preorder', function (node) { + if (node.depth > treeDepth) { + treeDepth = node.depth; + } + }); + + var expandAndCollapse = option.expandAndCollapse; + var expandTreeDepth = (expandAndCollapse && option.initialTreeDepth >= 0) + ? option.initialTreeDepth : treeDepth; + + tree.root.eachNode('preorder', function (node) { + var item = node.hostTree.data.getRawDataItem(node.dataIndex); + // add item.collapsed != null, because users can collapse node original in the series.data. + node.isExpand = (item && item.collapsed != null) + ? !item.collapsed + : node.depth <= expandTreeDepth; + }); + + return tree.data; + }, + + /** + * Make the configuration 'orient' backward compatibly, with 'horizontal = LR', 'vertical = TB'. + * @returns {string} orient + */ + getOrient: function () { + var orient = this.get('orient'); + if (orient === 'horizontal') { + orient = 'LR'; + } + else if (orient === 'vertical') { + orient = 'TB'; + } + return orient; + }, + + /** + * @override + * @param {number} dataIndex + */ + formatTooltip: function (dataIndex) { + var tree = this.getData().tree; + var realRoot = tree.root.children[0]; + var node = tree.getNodeByDataIndex(dataIndex); + var value = node.getValue(); + var name = node.name; + while (node && (node !== realRoot)) { + name = node.parentNode.name + '.' + name; + node = node.parentNode; + } + return encodeHTML(name + ( + (isNaN(value) || value == null) ? '' : ' : ' + value + )); + }, + + defaultOption: { + zlevel: 0, + z: 2, + + // the position of the whole view + left: '12%', + top: '12%', + right: '12%', + bottom: '12%', + + // the layout of the tree, two value can be selected, 'orthogonal' or 'radial' + layout: 'orthogonal', + + // The orient of orthoginal layout, can be setted to 'LR', 'TB', 'RL', 'BT'. + // and the backward compatibility configuration 'horizontal = LR', 'vertical = TB'. + orient: 'LR', + + symbol: 'emptyCircle', + + symbolSize: 7, + + expandAndCollapse: true, + + initialTreeDepth: 2, + + lineStyle: { + color: '#ccc', + width: 1.5, + curveness: 0.5 + }, + + itemStyle: { + color: 'lightsteelblue', + borderColor: '#c23531', + borderWidth: 1.5 + }, + + label: { + show: true, + color: '#555' + }, + + leaves: { + label: { + show: true + } + }, + + animationEasing: 'linear', + + animationDuration: 700, + + animationDurationUpdate: 1000 + } +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/* +* The tree layout implementation references to d3.js +* (https://github.com/d3/d3-hierarchy). The use of the source +* code of this file is also subject to the terms and consitions +* of its license (BSD-3Clause, see ). +*/ + +/** + * @file The layout algorithm of node-link tree diagrams. Here we using Reingold-Tilford algorithm to drawing + * the tree. + * @see https://github.com/d3/d3-hierarchy + */ + +/** + * Initialize all computational message for following algorithm + * @param {module:echarts/data/Tree~TreeNode} root The virtual root of the tree + */ +function init$2(root) { + root.hierNode = { + defaultAncestor: null, + ancestor: root, + prelim: 0, + modifier: 0, + change: 0, + shift: 0, + i: 0, + thread: null + }; + + var nodes = [root]; + var node; + var children; + + while (node = nodes.pop()) { // jshint ignore:line + children = node.children; + if (node.isExpand && children.length) { + var n = children.length; + for (var i = n - 1; i >= 0; i--) { + var child = children[i]; + child.hierNode = { + defaultAncestor: null, + ancestor: child, + prelim: 0, + modifier: 0, + change: 0, + shift: 0, + i: i, + thread: null + }; + nodes.push(child); + } + } + } +} + +/** + * Computes a preliminary x coordinate for node. Before that, this function is + * applied recursively to the children of node, as well as the function + * apportion(). After spacing out the children by calling executeShifts(), the + * node is placed to the midpoint of its outermost children. + * @param {module:echarts/data/Tree~TreeNode} node + * @param {Function} separation + */ +function firstWalk(node, separation) { + var children = node.isExpand ? node.children : []; + var siblings = node.parentNode.children; + var subtreeW = node.hierNode.i ? siblings[node.hierNode.i -1] : null; + if (children.length) { + executeShifts(node); + var midPoint = (children[0].hierNode.prelim + children[children.length - 1].hierNode.prelim) / 2; + if (subtreeW) { + node.hierNode.prelim = subtreeW.hierNode.prelim + separation(node, subtreeW); + node.hierNode.modifier = node.hierNode.prelim - midPoint; + } + else { + node.hierNode.prelim = midPoint; + } + } + else if (subtreeW) { + node.hierNode.prelim = subtreeW.hierNode.prelim + separation(node, subtreeW); + } + node.parentNode.hierNode.defaultAncestor = apportion(node, subtreeW, node.parentNode.hierNode.defaultAncestor || siblings[0], separation); +} + + +/** + * Computes all real x-coordinates by summing up the modifiers recursively. + * @param {module:echarts/data/Tree~TreeNode} node + */ +function secondWalk(node) { + var nodeX = node.hierNode.prelim + node.parentNode.hierNode.modifier; + node.setLayout({x: nodeX}, true); + node.hierNode.modifier += node.parentNode.hierNode.modifier; +} + + +function separation(cb) { + return arguments.length ? cb : defaultSeparation; +} + +/** + * Transform the common coordinate to radial coordinate + * @param {number} x + * @param {number} y + * @return {Object} + */ +function radialCoordinate(x, y) { + var radialCoor = {}; + x -= Math.PI / 2; + radialCoor.x = y * Math.cos(x); + radialCoor.y = y * Math.sin(x); + return radialCoor; +} + +/** + * 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 getLayoutRect( + seriesModel.getBoxLayoutParams(), { + width: api.getWidth(), + height: api.getHeight() + } + ); +} + +/** + * All other shifts, applied to the smaller subtrees between w- and w+, are + * performed by this function. + * @param {module:echarts/data/Tree~TreeNode} node + */ +function executeShifts(node) { + var children = node.children; + var n = children.length; + var shift = 0; + var change = 0; + while (--n >= 0) { + var child = children[n]; + child.hierNode.prelim += shift; + child.hierNode.modifier += shift; + change += child.hierNode.change; + shift += child.hierNode.shift + change; + } +} + +/** + * The core of the algorithm. Here, a new subtree is combined with the + * previous subtrees. Threads are used to traverse the inside and outside + * contours of the left and right subtree up to the highest common level. + * Whenever two nodes of the inside contours conflict, we compute the left + * one of the greatest uncommon ancestors using the function nextAncestor() + * and call moveSubtree() to shift the subtree and prepare the shifts of + * smaller subtrees. Finally, we add a new thread (if necessary). + * @param {module:echarts/data/Tree~TreeNode} subtreeV + * @param {module:echarts/data/Tree~TreeNode} subtreeW + * @param {module:echarts/data/Tree~TreeNode} ancestor + * @param {Function} separation + * @return {module:echarts/data/Tree~TreeNode} + */ +function apportion(subtreeV, subtreeW, ancestor, separation) { + + if (subtreeW) { + var nodeOutRight = subtreeV; + var nodeInRight = subtreeV; + var nodeOutLeft = nodeInRight.parentNode.children[0]; + var nodeInLeft = subtreeW; + + var sumOutRight = nodeOutRight.hierNode.modifier; + var sumInRight = nodeInRight.hierNode.modifier; + var sumOutLeft = nodeOutLeft.hierNode.modifier; + var sumInLeft = nodeInLeft.hierNode.modifier; + + while (nodeInLeft = nextRight(nodeInLeft), nodeInRight = nextLeft(nodeInRight), nodeInLeft && nodeInRight) { + nodeOutRight = nextRight(nodeOutRight); + nodeOutLeft = nextLeft(nodeOutLeft); + nodeOutRight.hierNode.ancestor = subtreeV; + var shift = nodeInLeft.hierNode.prelim + sumInLeft - nodeInRight.hierNode.prelim + - sumInRight + separation(nodeInLeft, nodeInRight); + if (shift > 0) { + moveSubtree(nextAncestor(nodeInLeft, subtreeV, ancestor), subtreeV, shift); + sumInRight += shift; + sumOutRight += shift; + } + sumInLeft += nodeInLeft.hierNode.modifier; + sumInRight += nodeInRight.hierNode.modifier; + sumOutRight += nodeOutRight.hierNode.modifier; + sumOutLeft += nodeOutLeft.hierNode.modifier; + } + if (nodeInLeft && !nextRight(nodeOutRight)) { + nodeOutRight.hierNode.thread = nodeInLeft; + nodeOutRight.hierNode.modifier += sumInLeft - sumOutRight; + + } + if (nodeInRight && !nextLeft(nodeOutLeft)) { + nodeOutLeft.hierNode.thread = nodeInRight; + nodeOutLeft.hierNode.modifier += sumInRight - sumOutLeft; + ancestor = subtreeV; + } + } + return ancestor; +} + +/** + * This function is used to traverse the right contour of a subtree. + * It returns the rightmost child of node or the thread of node. The function + * returns null if and only if node is on the highest depth of its subtree. + * @param {module:echarts/data/Tree~TreeNode} node + * @return {module:echarts/data/Tree~TreeNode} + */ +function nextRight(node) { + var children = node.children; + return children.length && node.isExpand ? children[children.length - 1] : node.hierNode.thread; +} + +/** + * This function is used to traverse the left contour of a subtree (or a subforest). + * It returns the leftmost child of node or the thread of node. The function + * returns null if and only if node is on the highest depth of its subtree. + * @param {module:echarts/data/Tree~TreeNode} node + * @return {module:echarts/data/Tree~TreeNode} + */ +function nextLeft(node) { + var children = node.children; + return children.length && node.isExpand ? children[0] : node.hierNode.thread; +} + +/** + * If nodeInLeft’s ancestor is a sibling of node, returns nodeInLeft’s ancestor. + * Otherwise, returns the specified ancestor. + * @param {module:echarts/data/Tree~TreeNode} nodeInLeft + * @param {module:echarts/data/Tree~TreeNode} node + * @param {module:echarts/data/Tree~TreeNode} ancestor + * @return {module:echarts/data/Tree~TreeNode} + */ +function nextAncestor(nodeInLeft, node, ancestor) { + return nodeInLeft.hierNode.ancestor.parentNode === node.parentNode + ? nodeInLeft.hierNode.ancestor : ancestor; +} + +/** + * Shifts the current subtree rooted at wr. This is done by increasing prelim(w+) and modifier(w+) by shift. + * @param {module:echarts/data/Tree~TreeNode} wl + * @param {module:echarts/data/Tree~TreeNode} wr + * @param {number} shift [description] + */ +function moveSubtree(wl, wr,shift) { + var change = shift / (wr.hierNode.i - wl.hierNode.i); + wr.hierNode.change -= change; + wr.hierNode.shift += shift; + wr.hierNode.modifier += shift; + wr.hierNode.prelim += shift; + wl.hierNode.change += change; +} + +function defaultSeparation(node1, node2) { + return node1.parentNode === node2.parentNode ? 1 : 2; +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * @file This file used to draw tree view + */ + +extendChartView({ + + type: 'tree', + + /** + * Init the chart + * @override + * @param {module:echarts/model/Global} ecModel + * @param {module:echarts/ExtensionAPI} api + */ + init: function (ecModel, api) { + + /** + * @private + * @type {module:echarts/data/Tree} + */ + this._oldTree; + + /** + * @private + * @type {module:zrender/container/Group} + */ + this._mainGroup = new Group(); + + this.group.add(this._mainGroup); + }, + + render: function (seriesModel, ecModel, api, payload) { + + var data = seriesModel.getData(); + + var layoutInfo = seriesModel.layoutInfo; + + var group = this._mainGroup; + + var layout = seriesModel.get('layout'); + + if (layout === 'radial') { + group.attr('position', [layoutInfo.x + layoutInfo.width / 2, layoutInfo.y + layoutInfo.height / 2]); + } + else { + group.attr('position', [layoutInfo.x, layoutInfo.y]); + } + + var oldData = this._data; + + var seriesScope = { + expandAndCollapse: seriesModel.get('expandAndCollapse'), + layout: layout, + orient: seriesModel.getOrient(), + curvature: seriesModel.get('lineStyle.curveness'), + symbolRotate: seriesModel.get('symbolRotate'), + symbolOffset: seriesModel.get('symbolOffset'), + hoverAnimation: seriesModel.get('hoverAnimation'), + useNameLabel: true, + fadeIn: true + }; + + data.diff(oldData) + .add(function (newIdx) { + if (symbolNeedsDraw$1(data, newIdx)) { + // create node and edge + updateNode(data, newIdx, null, group, seriesModel, seriesScope); + } + }) + .update(function (newIdx, oldIdx) { + var symbolEl = oldData.getItemGraphicEl(oldIdx); + if (!symbolNeedsDraw$1(data, newIdx)) { + symbolEl && removeNode(oldData, oldIdx, symbolEl, group, seriesModel, seriesScope); + return; + } + // update node and edge + updateNode(data, newIdx, symbolEl, group, seriesModel, seriesScope); + }) + .remove(function (oldIdx) { + var symbolEl = oldData.getItemGraphicEl(oldIdx); + // When remove a collapsed node of subtree, since the collapsed + // node haven't been initialized with a symbol element, + // you can't found it's symbol element through index. + // so if we want to remove the symbol element we should insure + // that the symbol element is not null. + if (symbolEl) { + removeNode(oldData, oldIdx, symbolEl, group, seriesModel, seriesScope); + } + }) + .execute(); + + if (seriesScope.expandAndCollapse === true) { + data.eachItemGraphicEl(function (el, dataIndex) { + el.off('click').on('click', function () { + api.dispatchAction({ + type: 'treeExpandAndCollapse', + seriesId: seriesModel.id, + dataIndex: dataIndex + }); + }); + }); + } + + this._data = data; + }, + + dispose: function () {}, + + remove: function () { + this._mainGroup.removeAll(); + this._data = null; + } + +}); + +function symbolNeedsDraw$1(data, dataIndex) { + var layout = data.getItemLayout(dataIndex); + + return layout + && !isNaN(layout.x) && !isNaN(layout.y) + && data.getItemVisual(dataIndex, 'symbol') !== 'none'; +} + +function getTreeNodeStyle(node, itemModel, seriesScope) { + seriesScope.itemModel = itemModel; + seriesScope.itemStyle = itemModel.getModel('itemStyle').getItemStyle(); + seriesScope.hoverItemStyle = itemModel.getModel('emphasis.itemStyle').getItemStyle(); + seriesScope.lineStyle = itemModel.getModel('lineStyle').getLineStyle(); + seriesScope.labelModel = itemModel.getModel('label'); + seriesScope.hoverLabelModel = itemModel.getModel('emphasis.label'); + + if (node.isExpand === false && node.children.length !== 0) { + seriesScope.symbolInnerColor = seriesScope.itemStyle.fill; + } + else { + seriesScope.symbolInnerColor = '#fff'; + } + + return seriesScope; +} + +function updateNode(data, dataIndex, symbolEl, group, seriesModel, seriesScope) { + var isInit = !symbolEl; + var node = data.tree.getNodeByDataIndex(dataIndex); + var itemModel = node.getModel(); + var seriesScope = getTreeNodeStyle(node, itemModel, seriesScope); + var virtualRoot = data.tree.root; + + var source = node.parentNode === virtualRoot ? node : node.parentNode || node; + var sourceSymbolEl = data.getItemGraphicEl(source.dataIndex); + var sourceLayout = source.getLayout(); + var sourceOldLayout = sourceSymbolEl + ? { + x: sourceSymbolEl.position[0], + y: sourceSymbolEl.position[1], + rawX: sourceSymbolEl.__radialOldRawX, + rawY: sourceSymbolEl.__radialOldRawY + } + : sourceLayout; + var targetLayout = node.getLayout(); + + if (isInit) { + symbolEl = new SymbolClz$1(data, dataIndex, seriesScope); + symbolEl.attr('position', [sourceOldLayout.x, sourceOldLayout.y]); + } + else { + symbolEl.updateData(data, dataIndex, seriesScope); + } + + symbolEl.__radialOldRawX = symbolEl.__radialRawX; + symbolEl.__radialOldRawY = symbolEl.__radialRawY; + symbolEl.__radialRawX = targetLayout.rawX; + symbolEl.__radialRawY = targetLayout.rawY; + + group.add(symbolEl); + data.setItemGraphicEl(dataIndex, symbolEl); + updateProps(symbolEl, { + position: [targetLayout.x, targetLayout.y] + }, seriesModel); + + var symbolPath = symbolEl.getSymbolPath(); + + if (seriesScope.layout === 'radial') { + var realRoot = virtualRoot.children[0]; + var rootLayout = realRoot.getLayout(); + var length = realRoot.children.length; + var rad; + var isLeft; + + if (targetLayout.x === rootLayout.x && node.isExpand === true) { + var center = {}; + center.x = (realRoot.children[0].getLayout().x + realRoot.children[length - 1].getLayout().x) / 2; + center.y = (realRoot.children[0].getLayout().y + realRoot.children[length - 1].getLayout().y) / 2; + rad = Math.atan2(center.y - rootLayout.y, center.x - rootLayout.x); + if (rad < 0) { + rad = Math.PI * 2 + rad; + } + isLeft = center.x < rootLayout.x; + if (isLeft) { + rad = rad - Math.PI; + } + } + else { + rad = Math.atan2(targetLayout.y - rootLayout.y, targetLayout.x - rootLayout.x); + if (rad < 0) { + rad = Math.PI * 2 + rad; + } + if (node.children.length === 0 || (node.children.length !== 0 && node.isExpand === false)) { + isLeft = targetLayout.x < rootLayout.x; + if (isLeft) { + rad = rad - Math.PI; + } + } + else { + isLeft = targetLayout.x > rootLayout.x; + if (!isLeft) { + rad = rad - Math.PI; + } + } + } + + var textPosition = isLeft ? 'left' : 'right'; + symbolPath.setStyle({ + textPosition: textPosition, + textRotation: -rad, + textOrigin: 'center', + verticalAlign: 'middle' + }); + } + + if (node.parentNode && node.parentNode !== virtualRoot) { + var edge = symbolEl.__edge; + if (!edge) { + edge = symbolEl.__edge = new BezierCurve({ + shape: getEdgeShape(seriesScope, sourceOldLayout, sourceOldLayout), + style: defaults({opacity: 0}, seriesScope.lineStyle) + }); + } + + updateProps(edge, { + shape: getEdgeShape(seriesScope, sourceLayout, targetLayout), + style: {opacity: 1} + }, seriesModel); + + group.add(edge); + } +} + +function removeNode(data, dataIndex, symbolEl, group, seriesModel, seriesScope) { + var node = data.tree.getNodeByDataIndex(dataIndex); + var virtualRoot = data.tree.root; + var itemModel = node.getModel(); + var seriesScope = getTreeNodeStyle(node, itemModel, seriesScope); + + var source = node.parentNode === virtualRoot ? node : node.parentNode || node; + var sourceLayout; + while (sourceLayout = source.getLayout(), sourceLayout == null) { + source = source.parentNode === virtualRoot ? source : source.parentNode || source; + } + + updateProps(symbolEl, { + position: [sourceLayout.x + 1, sourceLayout.y + 1] + }, seriesModel, function () { + group.remove(symbolEl); + data.setItemGraphicEl(dataIndex, null); + }); + + symbolEl.fadeOut(null, {keepLabel: true}); + + var edge = symbolEl.__edge; + if (edge) { + updateProps(edge, { + shape: getEdgeShape(seriesScope, sourceLayout, sourceLayout), + style: { + opacity: 0 + } + }, seriesModel, function () { + group.remove(edge); + }); + } +} + +function getEdgeShape(seriesScope, sourceLayout, targetLayout) { + var cpx1; + var cpy1; + var cpx2; + var cpy2; + var orient = seriesScope.orient; + + if (seriesScope.layout === 'radial') { + var x1 = sourceLayout.rawX; + var y1 = sourceLayout.rawY; + var x2 = targetLayout.rawX; + var y2 = targetLayout.rawY; + + var radialCoor1 = radialCoordinate(x1, y1); + var radialCoor2 = radialCoordinate(x1, y1 + (y2 - y1) * seriesScope.curvature); + var radialCoor3 = radialCoordinate(x2, y2 + (y1 - y2) * seriesScope.curvature); + var radialCoor4 = radialCoordinate(x2, y2); + + return { + x1: radialCoor1.x, + y1: radialCoor1.y, + x2: radialCoor4.x, + y2: radialCoor4.y, + cpx1: radialCoor2.x, + cpy1: radialCoor2.y, + cpx2: radialCoor3.x, + cpy2: radialCoor3.y + }; + } + else { + var x1 = sourceLayout.x; + var y1 = sourceLayout.y; + var x2 = targetLayout.x; + var y2 = targetLayout.y; + + if (orient === 'LR' || orient === 'RL') { + cpx1 = x1 + (x2 - x1) * seriesScope.curvature; + cpy1 = y1; + cpx2 = x2 + (x1 - x2) * seriesScope.curvature; + cpy2 = y2; + } + if (orient === 'TB' || orient === 'BT') { + cpx1 = x1; + cpy1 = y1 + (y2 - y1) * seriesScope.curvature; + cpx2 = x2; + cpy2 = y2 + (y1 - y2) * seriesScope.curvature; + } + return { + x1: x1, + y1: y1, + x2: x2, + y2: y2, + cpx1: cpx1, + cpy1: cpy1, + cpx2: cpx2, + cpy2: cpy2 + }; + } +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +registerAction({ + type: 'treeExpandAndCollapse', + event: 'treeExpandAndCollapse', + update: 'update' +}, function (payload, ecModel) { + ecModel.eachComponent({mainType: 'series', subType: 'tree', query: payload}, function (seriesModel) { + var dataIndex = payload.dataIndex; + var tree = seriesModel.getData().tree; + var node = tree.getNodeByDataIndex(dataIndex); + node.isExpand = !node.isExpand; + + }); +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + + +/** + * Traverse the tree from bottom to top and do something + * @param {module:echarts/data/Tree~TreeNode} root The real root of the tree + * @param {Function} callback + */ +function eachAfter (root, callback, separation) { + var nodes = [root]; + var next = []; + var node; + + while (node = nodes.pop()) { // jshint ignore:line + next.push(node); + if (node.isExpand) { + var children = node.children; + if (children.length) { + for (var i = 0; i < children.length; i++) { + nodes.push(children[i]); + } + } + } + } + + while (node = next.pop()) { // jshint ignore:line + callback(node, separation); + } +} + +/** + * Traverse the tree from top to bottom and do something + * @param {module:echarts/data/Tree~TreeNode} root The real root of the tree + * @param {Function} callback + */ +function eachBefore (root, callback) { + var nodes = [root]; + var node; + while (node = nodes.pop()) { // jshint ignore:line + callback(node); + if (node.isExpand) { + var children = node.children; + if (children.length) { + for (var i = children.length - 1; i >= 0; i--) { + nodes.push(children[i]); + } + } + } + } +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var treeLayout = function (ecModel, api) { + ecModel.eachSeriesByType('tree', function (seriesModel) { + commonLayout(seriesModel, api); + }); +}; + +function commonLayout(seriesModel, api) { + var layoutInfo = getViewRect(seriesModel, api); + seriesModel.layoutInfo = layoutInfo; + var layout = seriesModel.get('layout'); + var width = 0; + var height = 0; + var separation$$1 = null; + + if (layout === 'radial') { + width = 2 * Math.PI; + height = Math.min(layoutInfo.height, layoutInfo.width) / 2; + separation$$1 = separation(function (node1, node2) { + return (node1.parentNode === node2.parentNode ? 1 : 2) / node1.depth; + }); + } + else { + width = layoutInfo.width; + height = layoutInfo.height; + separation$$1 = separation(); + } + + var virtualRoot = seriesModel.getData().tree.root; + var realRoot = virtualRoot.children[0]; + + if (realRoot) { + init$2(virtualRoot); + eachAfter(realRoot, firstWalk, separation$$1); + virtualRoot.hierNode.modifier = - realRoot.hierNode.prelim; + eachBefore(realRoot, secondWalk); + + var left = realRoot; + var right = realRoot; + var bottom = realRoot; + eachBefore(realRoot, function (node) { + var x = node.getLayout().x; + if (x < left.getLayout().x) { + left = node; + } + if (x > right.getLayout().x) { + right = node; + } + if (node.depth > bottom.depth) { + bottom = node; + } + }); + + var delta = left === right ? 1 : separation$$1(left, right) / 2; + var tx = delta - left.getLayout().x; + var kx = 0; + var ky = 0; + var coorX = 0; + var coorY = 0; + if (layout === 'radial') { + kx = width / (right.getLayout().x + delta + tx); + // here we use (node.depth - 1), bucause the real root's depth is 1 + ky = height / ((bottom.depth - 1) || 1); + eachBefore(realRoot, function (node) { + coorX = (node.getLayout().x + tx) * kx; + coorY = (node.depth - 1) * ky; + var finalCoor = radialCoordinate(coorX, coorY); + node.setLayout({x: finalCoor.x, y: finalCoor.y, rawX: coorX, rawY: coorY}, true); + }); + } + else { + var orient = seriesModel.getOrient(); + if (orient === 'RL' || orient === 'LR') { + ky = height / (right.getLayout().x + delta + tx); + kx = width / ((bottom.depth - 1) || 1); + eachBefore(realRoot, function (node) { + coorY = (node.getLayout().x + tx) * ky; + coorX = orient === 'LR' + ? (node.depth - 1) * kx + : width - (node.depth - 1) * kx; + node.setLayout({x: coorX, y: coorY}, true); + }); + } + else if (orient === 'TB' || orient === 'BT') { + kx = width / (right.getLayout().x + delta + tx); + ky = height / ((bottom.depth - 1) || 1); + eachBefore(realRoot, function (node) { + coorX = (node.getLayout().x + tx) * kx; + coorY = orient === 'TB' + ? (node.depth - 1) * ky + : height - (node.depth - 1) * ky; + node.setLayout({x: coorX, y: coorY}, true); + }); + } + } + } +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +registerVisual(visualSymbol('tree', 'circle')); +registerLayout(treeLayout); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +function retrieveTargetInfo(payload, validPayloadTypes, seriesModel) { + if (payload && indexOf(validPayloadTypes, payload.type) >= 0) { + 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. +function getPathToRoot(node) { + var path = []; + while (node) { + node = node.parentNode; + node && path.push(node); + } + return path.reverse(); +} + +function aboveViewRoot(viewRoot, node) { + var viewPath = getPathToRoot(viewRoot); + return indexOf(viewPath, node) >= 0; +} + +// From root to the input node (the input node will be included). +function wrapTreePathInfo(node, seriesModel) { + var treePathInfo = []; + + while (node) { + var nodeDataIndex = node.dataIndex; + treePathInfo.push({ + name: node.name, + dataIndex: nodeDataIndex, + value: seriesModel.getRawValue(nodeDataIndex) + }); + node = node.parentNode; + } + + treePathInfo.reverse(); + + return treePathInfo; +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +SeriesModel.extend({ + + type: 'series.treemap', + + layoutMode: 'box', + + 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. ▷▶❒❐▼✚ + + 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: { + 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: { + show: true, + // Do not use textDistance, for ellipsis rect just the same as treemap node rect. + distance: 0, + padding: 5, + position: 'inside', // Can be [5, '5%'] or position stirng like 'insideTopLeft', ... + // formatter: null, + color: '#fff', + ellipsis: true + // align + // verticalAlign + }, + upperLabel: { // Label when node is parent. + show: false, + position: [0, '50%'], + height: 20, + // formatter: null, + color: '#fff', + ellipsis: true, + // align: null, + verticalAlign: 'middle' + }, + itemStyle: { + 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: { + upperLabel: { + show: true, + position: [0, '50%'], + color: '#fff', + ellipsis: true, + verticalAlign: 'middle' + } + }, + + visualDimension: 0, // Can be 0, 1, 2, 3. + visualMin: null, + visualMax: null, + + 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) { + // Create a virtual root. + var root = {name: option.name, children: option.data}; + + completeTreeValue(root); + + var levels = option.levels || []; + + levels = option.levels = setDefault(levels, ecModel); + + var treeOption = {}; + + treeOption.levels = levels; + + // 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, treeOption).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 = 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 node = this.getData().tree.getNodeByDataIndex(dataIndex); + params.treePathInfo = wrapTreePathInfo(node, this); + + 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 || {}; + 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 = createHashMap(); + /** + * @private + * @type {number} + */ + this._idIndexMapCount = 0; + } + + var index = idIndexMap.get(id); + if (index == null) { + idIndexMap.set(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.getRawData().tree.root; + + if (!viewRoot + || (viewRoot !== root && !root.contains(viewRoot)) + ) { + this._viewRoot = root; + } + } +}); + +/** + * @param {Object} dataNode + */ +function completeTreeValue(dataNode) { + // 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; + + each$1(dataNode.children, function (child) { + + completeTreeValue(child); + + var childValue = child.value; + isArray(childValue) && (childValue = childValue[0]); + + sum += childValue; + }); + + var thisValue = dataNode.value; + if (isArray(thisValue)) { + thisValue = thisValue[0]; + } + + if (thisValue == null || isNaN(thisValue)) { + thisValue = sum; + } + // Value should not less than 0. + if (thisValue < 0) { + thisValue = 0; + } + + isArray(dataNode.value) + ? (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; + each$1(levels, function (levelDefine) { + var model = new Model(levelDefine); + var modelColor = model.get('color'); + + if (model.get('itemStyle.color') + || (modelColor && modelColor !== 'none') + ) { + hasColorDefine = true; + } + }); + + if (!hasColorDefine) { + var level0 = levels[0] || (levels[0] = {}); + level0.color = globalColorList.slice(); + } + + return levels; +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var TEXT_PADDING = 8; +var ITEM_GAP = 8; +var ARRAY_LENGTH = 5; + +function Breadcrumb(containerGroup) { + /** + * @private + * @type {module:zrender/container/Group} + */ + this.group = new Group(); + + containerGroup.add(this.group); +} + +Breadcrumb.prototype = { + + constructor: Breadcrumb, + + render: function (seriesModel, api, targetNode, onSelect) { + var model = seriesModel.getModel('breadcrumb'); + var thisGroup = this.group; + + thisGroup.removeAll(); + + if (!model.get('show') || !targetNode) { + return; + } + + var normalStyleModel = model.getModel('itemStyle'); + // var emphasisStyleModel = model.getModel('emphasis.itemStyle'); + 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(targetNode, layoutParam, textStyleModel); + this._renderContent(seriesModel, layoutParam, normalStyleModel, textStyleModel, onSelect); + + positionElement(thisGroup, layoutParam.pos, layoutParam.box); + }, + + /** + * Prepare render list and total width + * @private + */ + _prepare: function (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 ( + seriesModel, layoutParam, normalStyleModel, textStyleModel, onSelect + ) { + // Start rendering. + var lastX = 0; + var emptyItemWidth = layoutParam.emptyItemWidth; + var height = seriesModel.get('breadcrumb.height'); + var availableSize = 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 itemNode = item.node; + 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 = null; + } + + var el = new Polygon({ + shape: { + points: makeItemPoints( + lastX, 0, itemWidth, height, + i === renderList.length - 1, i === 0 + ) + }, + style: defaults( + normalStyleModel.getItemStyle(), + { + lineJoin: 'bevel', + text: text, + textFill: textStyleModel.getTextColor(), + textFont: textStyleModel.getFont() + } + ), + z: 10, + onclick: curry(onSelect, itemNode) + }); + this.group.add(el); + + packEventData(el, seriesModel, itemNode); + + 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; +} + +// Package custom mouse event. +function packEventData(el, seriesModel, itemNode) { + el.eventData = { + componentType: 'series', + componentSubType: 'treemap', + seriesIndex: seriesModel.componentIndex, + seriesName: seriesModel.name, + seriesType: 'treemap', + selfType: 'breadcrumb', // Distinguish with click event on treemap node. + nodeData: { + dataIndex: itemNode && itemNode.dataIndex, + name: itemNode && itemNode.name + }, + treePathInfo: itemNode && wrapTreePathInfo(itemNode, seriesModel) + }; +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * @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 (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(); + } + } + } + }; +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var bind$1 = bind; +var Group$2 = Group; +var Rect$1 = Rect; +var each$8 = each$1; + +var DRAG_THRESHOLD = 3; +var PATH_LABEL_NOAMAL = ['label']; +var PATH_LABEL_EMPHASIS = ['emphasis', 'label']; +var PATH_UPPERLABEL_NORMAL = ['upperLabel']; +var PATH_UPPERLABEL_EMPHASIS = ['emphasis', 'upperLabel']; +var Z_BASE = 10; // Should bigger than every z. +var Z_BG = 1; +var Z_CONTENT = 2; + +var getItemStyleEmphasis = makeStyleMapper([ + ['fill', 'color'], + // `borderColor` and `borderWidth` has been occupied, + // so use `stroke` to indicate the stroke of the rect. + ['stroke', 'strokeColor'], + ['lineWidth', 'strokeWidth'], + ['shadowBlur'], + ['shadowOffsetX'], + ['shadowOffsetY'], + ['shadowColor'] +]); +var getItemStyleNormal = function (model) { + // Normal style props should include emphasis style props. + var itemStyle = getItemStyleEmphasis(model); + // Clear styles set by emphasis. + itemStyle.stroke = itemStyle.fill = itemStyle.lineWidth = null; + return itemStyle; +}; + +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'; + }, + + /** + * @override + */ + render: function (seriesModel, ecModel, api, payload) { + + var models = ecModel.findComponents({ + mainType: 'series', subType: 'treemap', query: payload + }); + if (indexOf(models, seriesModel) < 0) { + return; + } + + this.seriesModel = seriesModel; + this.api = api; + this.ecModel = ecModel; + + var types = ['treemapZoomToNode', 'treemapRootToNode']; + var targetInfo = retrieveTargetInfo(payload, types, 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$2(); + 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 = curry( + renderNode, seriesModel, + thisStorage, oldStorage, reRoot, + lastsForAnimation, willInvisibleEls + ); + + // Notice: when thisTree and oldTree are the same tree (see list.cloneShallow), + // 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$8(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(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$8(storage, function (store, storageName) { + var delEls = willDeleteEls[storageName]; + each$8(store, function (el) { + el && (delEls.push(el), el.__tmWillDelete = 1); + }); + }); + return willDeleteEls; + } + + function renderFinally() { + each$8(willDeleteEls, function (els) { + each$8(els, function (el) { + el.parent && el.parent.remove(el); + }); + }); + each$8(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 = createWrap(); + + // Make delete animations. + each$8(renderResult.willDeleteEls, function (store, storageName) { + each$8(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$8(this._storage, function (store, storageName) { + each$8(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 = 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$1(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$1(this._onPan, this)); + controller.on('zoom', bind$1(this._onZoom, this)); + } + + var rect = new BoundingRect(0, 0, api.getWidth(), api.getHeight()); + controller.setPointerChecker(function (e, x, y) { + return rect.contain(x, y); + }); + }, + + /** + * @private + */ + _clearController: function () { + var controller = this._controller; + if (controller) { + controller.dispose(); + controller = null; + } + }, + + /** + * @private + */ + _onPan: function (dx, dy) { + 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) { + 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 = create$1(); + translate(m, m, [-mouseX, -mouseY]); + scale$1(m, m, [scale, scale]); + 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) { + containerGroup.on('click', function (e) { + if (this._state !== 'ready') { + return; + } + + 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); + } + } + + }, this); + }, + + /** + * @private + */ + _renderBreadcrumb: function (seriesModel, api, targetInfo) { + if (!targetInfo) { + targetInfo = seriesModel.get('leafDepth', true) != null + ? {node: seriesModel.getViewRoot()} + // FIXME + // better way? + // Find breadcrumb tail on center of containerGroup. + : this.findTarget(api.getWidth() / 2, api.getHeight() / 2); + + if (!targetInfo) { + targetInfo = {node: seriesModel.getData().tree.root}; + } + } + + (this._breadcrumb || (this._breadcrumb = new Breadcrumb(this.group))) + .render(seriesModel, api, targetInfo.node, bind$1(onSelect, this)); + + function onSelect(node) { + if (this._state !== 'animating') { + 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; + } + + // ------------------------------------------------------------------- + // Start of closure variables available in "Procedures in renderNode". + + var thisLayout = thisNode.getLayout(); + + if (!thisLayout || !thisLayout.isInView) { + return; + } + + var thisWidth = thisLayout.width; + var thisHeight = thisLayout.height; + var borderWidth = thisLayout.borderWidth; + var thisInvisible = thisLayout.invisible; + + var thisRawIndex = thisNode.getRawIndex(); + var oldRawIndex = oldNode && oldNode.getRawIndex(); + + var thisViewChildren = thisNode.viewChildren; + var upperHeight = thisLayout.upperHeight; + var isParent = thisViewChildren && thisViewChildren.length; + var itemStyleNormalModel = thisNode.getModel('itemStyle'); + var itemStyleEmphasisModel = thisNode.getModel('emphasis.itemStyle'); + + // End of closure ariables available in "Procedures in renderNode". + // ----------------------------------------------------------------- + + // Node group + var group = giveGraphic('nodeGroup', Group$2); + + 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$1, depth, Z_BG); + bg && renderBackground(group, bg, isParent && thisLayout.upperHeight); + + // No children, render content. + if (!isParent) { + var content = giveGraphic('content', Rect$1, depth, Z_CONTENT); + content && renderContent(group, content); + } + + return group; + + // ---------------------------- + // | Procedures in renderNode | + // ---------------------------- + + function renderBackground(group, bg, useUpperLabel) { + // For tooltip. + bg.dataIndex = thisNode.dataIndex; + bg.seriesIndex = seriesModel.seriesIndex; + + bg.setShape({x: 0, y: 0, width: thisWidth, height: thisHeight}); + var visualBorderColor = thisNode.getVisual('borderColor', true); + var emphasisBorderColor = itemStyleEmphasisModel.get('borderColor'); + + updateStyle(bg, function () { + var normalStyle = getItemStyleNormal(itemStyleNormalModel); + normalStyle.fill = visualBorderColor; + var emphasisStyle = getItemStyleEmphasis(itemStyleEmphasisModel); + emphasisStyle.fill = emphasisBorderColor; + + if (useUpperLabel) { + var upperLabelWidth = thisWidth - 2 * borderWidth; + + prepareText( + normalStyle, emphasisStyle, visualBorderColor, upperLabelWidth, upperHeight, + {x: borderWidth, y: 0, width: upperLabelWidth, height: upperHeight} + ); + } + // For old bg. + else { + normalStyle.text = emphasisStyle.text = null; + } + + bg.setStyle(normalStyle); + setHoverStyle(bg, emphasisStyle); + }); + + group.add(bg); + } + + function renderContent(group, content) { + // For tooltip. + content.dataIndex = thisNode.dataIndex; + content.seriesIndex = seriesModel.seriesIndex; + + 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 = getItemStyleNormal(itemStyleNormalModel); + normalStyle.fill = visualColor; + var emphasisStyle = getItemStyleEmphasis(itemStyleEmphasisModel); + + prepareText(normalStyle, emphasisStyle, visualColor, contentWidth, contentHeight); + + content.setStyle(normalStyle); + 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, width, height, upperLabelRect) { + var nodeModel = thisNode.getModel(); + var text = retrieve( + seriesModel.getFormattedLabel( + thisNode.dataIndex, 'normal', null, null, upperLabelRect ? 'upperLabel' : 'label' + ), + nodeModel.get('name') + ); + if (!upperLabelRect && thisLayout.isLeafRoot) { + var iconChar = seriesModel.get('drillDownIcon', true); + text = iconChar ? iconChar + ' ' + text : text; + } + + var normalLabelModel = nodeModel.getModel( + upperLabelRect ? PATH_UPPERLABEL_NORMAL : PATH_LABEL_NOAMAL + ); + var emphasisLabelModel = nodeModel.getModel( + upperLabelRect ? PATH_UPPERLABEL_EMPHASIS : PATH_LABEL_EMPHASIS + ); + + var isShow = normalLabelModel.getShallow('show'); + + setLabelStyle( + normalStyle, emphasisStyle, normalLabelModel, emphasisLabelModel, + { + defaultText: isShow ? text : null, + autoColor: visualColor, + isRectText: true + } + ); + + upperLabelRect && (normalStyle.textRect = clone(upperLabelRect)); + + normalStyle.truncate = (isShow && normalLabelModel.get('ellipsis')) + ? { + outerWidth: width, + outerHeight: height, + minChar: 2 + } + : null; + } + + 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() + : 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; +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * @file Treemap action + */ + +var noop$1 = function () {}; + +var actionTypes = [ + 'treemapZoomToNode', + 'treemapRender', + 'treemapMove' +]; + +for (var i$2 = 0; i$2 < actionTypes.length; i$2++) { + registerAction({type: actionTypes[i$2], update: 'updateView'}, noop$1); +} + +registerAction( + {type: 'treemapRootToNode', update: 'updateView'}, + function (payload, ecModel) { + + ecModel.eachComponent( + {mainType: 'series', subType: 'treemap', query: payload}, + handleRootToNode + ); + + function handleRootToNode(model, index) { + var types = ['treemapZoomToNode', 'treemapRootToNode']; + var targetInfo = retrieveTargetInfo(payload, types, model); + + if (targetInfo) { + var originViewRoot = model.getViewRoot(); + if (originViewRoot) { + payload.direction = aboveViewRoot(originViewRoot, targetInfo.node) + ? 'rollUp' : 'drillDown'; + } + model.resetViewRoot(targetInfo.node); + } + } + } +); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var each$9 = each$1; +var isObject$5 = isObject$1; + +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 = 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' + assert$1(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 bind(this._normalizeData, this); + } +}; + +var visualHandlers = VisualMapping.visualHandlers = { + + color: { + + applyVisual: makeApplyVisual('color'), + + /** + * Create a mapper function + * @return {Function} + */ + getColorMapper: function () { + var thisOption = this.option; + + return bind( + thisOption.mappingMethod === 'category' + ? function (value, isNormalized) { + !isNormalized && (value = this._normalizeData(value)); + return doMapCategory.call(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 = fastLerp(value, thisOption.parsedVisual, out); + return returnRGBArray ? out : stringify(out, 'rgba'); + }, + this + ); + }, + + _doMap: { + linear: function (normalized) { + return stringify( + fastLerp(normalized, this.option.parsedVisual), + 'rgba' + ); + }, + category: doMapCategory, + piecewise: function (normalized, value) { + var result = getSpecifiedVisual.call(this, value); + if (result == null) { + result = stringify( + fastLerp(normalized, this.option.parsedVisual), + 'rgba' + ); + } + return result; + }, + fixed: doMapFixed + } + }, + + colorHue: makePartialColorVisualHandler(function (color, value) { + return modifyHSL(color, value); + }), + + colorSaturation: makePartialColorVisualHandler(function (color, value) { + return modifyHSL(color, null, value); + }), + + colorLightness: makePartialColorVisualHandler(function (color, value) { + return modifyHSL(color, null, null, value); + }), + + colorAlpha: makePartialColorVisualHandler(function (color, value) { + return modifyAlpha(color, value); + }), + + opacity: { + applyVisual: makeApplyVisual('opacity'), + _doMap: makeDoMap([0, 1]) + }, + + liftZ: { + applyVisual: makeApplyVisual('liftZ'), + _doMap: { + linear: doMapFixed, + category: doMapFixed, + piecewise: doMapFixed, + fixed: doMapFixed + } + }, + + symbol: { + applyVisual: function (value, getter, setter) { + var symbolCfg = this.mapValueToVisual(value); + if (isString(symbolCfg)) { + setter('symbol', symbolCfg); + } + else if (isObject$5(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; + + each$1(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$9(categories, function (cate, index) { + categoryMap[cate] = index; + }); + + // Process visual map input. + if (!isArray(visual)) { + var visualArr = []; + + if (isObject$1(visual)) { + each$9(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 = setVisualToOption(thisOption, 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 (isObject$1(visual)) { + each$9(visual, function (v) { + visualArr.push(v); + }); + } + else if (visual != null) { + visualArr.push(visual); + } + + var doNotNeedPair = {color: 1, symbol: 1}; + + if (!isCategory + && visualArr.length === 1 + && !doNotNeedPair.hasOwnProperty(thisOption.type) + ) { + // Do not care visualArr.length === 0, which is illegal. + visualArr[1] = visualArr[0]; + } + + setVisualToOption(thisOption, 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(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]; + } + } +} + +function setVisualToOption(thisOption, visualArr) { + thisOption.visual = visualArr; + if (thisOption.type === 'color') { + thisOption.parsedVisual = map(visualArr, function (item) { + return parse(item); + }); + } + return visualArr; +} + + +/** + * 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: noop +}; + + + +/** + * List available visual types. + * + * @public + * @return {Array.} + */ +VisualMapping.listVisualTypes = function () { + var visualTypes = []; + each$1(visualHandlers, function (handler, key) { + visualTypes.push(key); + }); + return visualTypes; +}; + +/** + * @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 (isObject$1(visual)) { + each$1(visual, callback, context); + } + else { + callback.call(context, visual); + } +}; + +VisualMapping.mapVisual = function (visual, callback, context) { + var isPrimary; + var newVisual = isArray(visual) + ? [] + : isObject$1(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 {Object} new object containers visual values. + * If no visuals, return null. + */ +VisualMapping.retrieveVisuals = function (obj) { + var ret = {}; + var hasVisual; + + obj && each$9(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$5(visualTypes)) { + var types = []; + each$9(visualTypes, function (item, type) { + types.push(type); + }); + visualTypes = types; + } + else if (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 + // FIXME + // It is supposed to compare value according to value type of dimension, + // but currently value type can exactly be string or number. + // Compromise for numeric-like string (like '12'), especially + // in the case that visualMap.categories is ['22', '33']. + || (typeof pieceValue === 'string' && 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; +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var isArray$2 = isArray; + +var ITEM_STYLE_NORMAL = 'itemStyle'; + +var treemapVisual = { + seriesType: 'treemap', + reset: function (seriesModel, ecModel, api, payload) { + var tree = seriesModel.getData().tree; + var root = tree.root; + var seriesItemStyleModel = seriesModel.getModel(ITEM_STYLE_NORMAL); + + if (root.isRemoved()) { + return; + } + + var levelItemStyles = 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. + each$1(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$1( + nodeModel, visuals, child, index, mapping, seriesModel + ); + travelTree( + child, childVisual, levelItemStyles, seriesItemStyleModel, + viewRootAncestors, seriesModel + ); + } + }); + } +} + +function buildVisuals( + nodeItemStyleModel, designatedVisual, levelItemStyle, seriesItemStyleModel +) { + var visuals = extend({}, designatedVisual); + + each$1(['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 = modifyHSL(color, null, null, colorSaturation); + } + if (colorAlpha) { + color = modifyAlpha(color, colorAlpha); + } + + return color; + } +} + +function calculateBorderColor(borderColorSaturation, thisNodeColor) { + return thisNodeColor != null + ? 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 visualMin = nodeModel.get('visualMin'); + var visualMax = nodeModel.get('visualMax'); + var dataExtent = nodeLayout.dataExtent.slice(); + visualMin != null && visualMin < dataExtent[0] && (dataExtent[0] = visualMin); + visualMax != null && visualMax > dataExtent[1] && (dataExtent[1] = visualMax); + + var colorMappingBy = nodeModel.get('colorMappingBy'); + var opt = { + type: rangeVisual.name, + dataExtent: 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$2(range) && range.length) ? {name: name, range: range} : null; +} + +function mapVisual$1(nodeModel, visuals, child, index, mapping, seriesModel) { + var childVisuals = 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; +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/* +* The treemap layout implementation references to the treemap +* layout of d3.js (d3/src/layout/treemap.js in v3). The use of +* the source code of this file is also subject to the terms +* and consitions of its license (BSD-3Clause, see +* ). +*/ + +var mathMax$4 = Math.max; +var mathMin$4 = Math.min; +var retrieveValue = retrieve; +var each$10 = each$1; + +var PATH_BORDER_WIDTH = ['itemStyle', 'borderWidth']; +var PATH_GAP_WIDTH = ['itemStyle', 'gapWidth']; +var PATH_UPPER_LABEL_SHOW = ['upperLabel', 'show']; +var PATH_UPPER_LABEL_HEIGHT = ['upperLabel', 'height']; + +/** + * @public + */ +var treemapLayout = { + seriesType: 'treemap', + reset: function (seriesModel, ecModel, api, payload) { + // Layout result in each node: + // {x, y, width, height, area, borderWidth} + var ecWidth = api.getWidth(); + var ecHeight = api.getHeight(); + var seriesOption = seriesModel.option; + + var layoutInfo = getLayoutRect( + seriesModel.getBoxLayoutParams(), + { + width: api.getWidth(), + height: api.getHeight() + } + ); + + var size = seriesOption.size || []; // Compatible with ec2. + var containerWidth = parsePercent$1( + retrieveValue(layoutInfo.width, size[0]), + ecWidth + ); + var containerHeight = parsePercent$1( + retrieveValue(layoutInfo.height, size[1]), + ecHeight + ); + + // Fetch payload info. + var payloadType = payload && payload.type; + var types = ['treemapZoomToNode', 'treemapRootToNode']; + var targetInfo = retrieveTargetInfo(payload, types, seriesModel); + var rootRect = (payloadType === 'treemapRender' || payloadType === 'treemapMove') + ? payload.rootRect : null; + var viewRoot = seriesModel.getViewRoot(); + var viewAbovePath = 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$10(viewAbovePath, function (node, index) { + var childValue = (viewAbovePath[index + 1] || viewRoot).getValue(); + node.setLayout(extend( + {dataExtent: [childValue, childValue], borderWidth: 0, upperHeight: 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 + * The implementation references to the treemap layout of d3.js. + * See the license statement at the head of this file. + * + * @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 nodeModel = node.getModel(); + var borderWidth = nodeModel.get(PATH_BORDER_WIDTH); + var halfGapWidth = nodeModel.get(PATH_GAP_WIDTH) / 2; + var upperLabelHeight = getUpperLabelHeight(nodeModel); + var upperHeight = Math.max(borderWidth, upperLabelHeight); + var layoutOffset = borderWidth - halfGapWidth; + var layoutOffsetUpper = upperHeight - halfGapWidth; + var nodeModel = node.getModel(); + + node.setLayout({ + borderWidth: borderWidth, + upperHeight: upperHeight, + upperLabelHeight: upperLabelHeight + }, true); + + width = mathMax$4(width - 2 * layoutOffset, 0); + height = mathMax$4(height - layoutOffset - layoutOffsetUpper, 0); + + var totalArea = width * height; + var viewChildren = initChildren( + node, nodeModel, totalArea, options, hideChildren, depth + ); + + if (!viewChildren.length) { + return; + } + + var rect = {x: layoutOffset, y: layoutOffsetUpper, width: width, height: height}; + var rowFixedLength = mathMin$4(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$4(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 = filter(viewChildren, function (child) { + return !child.isRemoved(); + }); + + sort$1(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$1(viewChildren, orderBy) { + if (orderBy) { + viewChildren.sort(function (a, b) { + var diff = orderBy === 'asc' + ? a.getValue() - b.getValue() : b.getValue() - a.getValue(); + return diff === 0 + ? (orderBy === 'asc' + ? a.dataIndex - b.dataIndex : b.dataIndex - a.dataIndex + ) + : diff; + }); + } + 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$10(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$4( + (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$4(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$4(modWH - 2 * halfGapWidth, 0); + + nodeLayout[xy[idx1WhenH]] = rect[xy[idx1WhenH]] + mathMin$4(halfGapWidth, wh1 / 2); + nodeLayout[xy[idx0WhenH]] = last + mathMin$4(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; + + // Considering border, suppose aspect ratio is 1. + var parentModel = parent.getModel(); + var borderWidth = parentModel.get(PATH_BORDER_WIDTH); + var upperHeight = Math.max(borderWidth, getUpperLabelHeight(parentModel, borderWidth)); + area += 4 * borderWidth * borderWidth + + (3 * borderWidth + upperHeight) * Math.pow(area, 0.5); + + area > MAX_SAFE_INTEGER && (area = 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$10(node.viewChildren || [], function (child) { + prunning(child, childClipRect, viewAbovePath, viewRoot, depth + 1); + }); +} + +function getUpperLabelHeight(model) { + return model.get(PATH_UPPER_LABEL_SHOW) ? model.get(PATH_UPPER_LABEL_HEIGHT) : 0; +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +registerVisual(treemapVisual); +registerLayout(treemapLayout); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * Graph data structure + * + * @module echarts/data/Graph + * @author Yi Shen(https://www.github.com/pissang) + */ + +// id may be function name of Object, add a prefix to avoid this problem. +function generateNodeKey (id) { + return '_EC_' + id; +} +/** + * @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[generateNodeKey(id)]) { + if (__DEV__) { + console.error('Graph nodes have duplicate name or id'); + } + return; + } + + var node = new Node(id, dataIndex); + node.hostGraph = this; + + this.nodes.push(node); + + nodesMap[generateNodeKey(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[generateNodeKey(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 (!Node.isInstance(n1)) { + n1 = nodesMap[generateNodeKey(n1)]; + } + if (!Node.isInstance(n2)) { + n2 = nodesMap[generateNodeKey(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 (Node.isInstance(n1)) { + n1 = n1.id; + } + if (Node.isInstance(n2)) { + 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 (!Node.isInstance(startNode)) { + startNode = this._nodesMap[generateNodeKey(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(context, 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$$1) { + this.dataIndex >= 0 + && this[hostName][dataName].setItemLayout(this.dataIndex, layout, merge$$1); + }, + + /** + * @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); + } + }; +}; + +mixin(Node, createGraphDataProxyMixin('hostGraph', 'data')); +mixin(Edge, createGraphDataProxyMixin('hostGraph', 'edgeData')); + +Graph.Node = Node; +Graph.Edge = Edge; + +enableClassCheck(Node); +enableClassCheck(Edge); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var createGraphFromNodeEdge = function (nodes, edges, seriesModel, directed, beforeLink) { + // ??? TODO + // support dataset? + var graph = new Graph(directed); + for (var i = 0; i < nodes.length; i++) { + graph.addNode(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(retrieve(link.id, source + ' > ' + target)); + linkCount++; + } + } + + var coordSys = seriesModel.get('coordinateSystem'); + var nodeData; + if (coordSys === 'cartesian2d' || coordSys === 'polar') { + nodeData = createListFromArray(nodes, seriesModel); + } + else { + var coordSysCtor = CoordinateSystemManager.get(coordSys); + var coordDimensions = (coordSysCtor && coordSysCtor.type !== 'view') + ? (coordSysCtor.dimensions || []) : []; + // FIXME: Some geo do not need `value` dimenson, whereas `calendar` needs + // `value` dimension, but graph need `value` dimension. It's better to + // uniform this behavior. + if (indexOf(coordDimensions, 'value') < 0) { + coordDimensions.concat(['value']); + } + + var dimensionNames = createDimensions(nodes, { + coordDimensions: coordDimensions + }); + nodeData = new List(dimensionNames, seriesModel); + nodeData.initData(nodes); + } + + var edgeData = new List(['value'], seriesModel); + 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; +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var GraphSeries = 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); + defaultEmphasis(option, ['edgeLabel'], ['show']); + }, + + 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'); + // For option `edgeLabel` can be found by label.xxx.xxx on item mode. + var fakeSeriesModel = new Model( + {label: edgeLabelModel.option}, + edgeLabelModel.parentModel, + ecModel + ); + var emphasisEdgeLabelModel = self.getModel('emphasis.edgeLabel'); + var emphasisFakeSeriesModel = new Model( + {emphasis: {label: emphasisEdgeLabelModel.option}}, + emphasisEdgeLabelModel.parentModel, + ecModel + ); + + edgeData.wrapMethod('getItemModel', function (model) { + model.customizeGetParent(edgeGetParent); + return model; + }); + + function edgeGetParent(path) { + path = this.parsePath(path); + return (path && path[0] === 'label') + ? fakeSeriesModel + : (path && path[0] === 'emphasis' && path[1] === 'label') + ? emphasisFakeSeriesModel + : this.parentModel; + } + } + }, + + /** + * @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 != null && html.push(sourceName); + targetName != null && html.push(targetName); + html = encodeHTML(html.join(' > ')); + + if (params.value) { + html += ' : ' + encodeHTML(params.value); + } + return html; + } + else { // dataType === 'node' or empty + return GraphSeries.superApply(this, 'formatTooltip', arguments); + } + }, + + _updateCategoriesData: function () { + var categories = map(this.option.categories || [], function (category) { + // Data must has value + return category.value != null ? category : 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; + }, + + isAnimationEnabled: function () { + return GraphSeries.superCall(this, 'isAnimationEnabled') + // 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: { + position: 'middle' + }, + + draggable: false, + + roam: false, + + // Default on center of graph + center: null, + + zoom: 1, + // Symbol size scale ratio in roam + nodeScaleRatio: 0.6, + // cursor: null, + + // categories: [], + + // data: [] + // Or + // nodes: [] + // + // links: [] + // Or + // edges: [] + + label: { + show: false, + formatter: '{b}' + }, + + itemStyle: {}, + + lineStyle: { + color: '#aaa', + width: 1, + curveness: 0, + opacity: 0.5 + }, + emphasis: { + label: { + show: true + } + } + } +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * Line path for bezier and straight line draw + */ + +var straightLineProto = Line.prototype; +var bezierCurveProto = BezierCurve.prototype; + +function isLine(shape) { + return isNaN(+shape.cpx1) || isNaN(+shape.cpy1); +} + +var LinePath = 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 normalize(p, p); + } +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * @module echarts/chart/helper/Line + */ + +var SYMBOL_CATEGORIES = ['fromSymbol', 'toSymbol']; + +function makeSymbolTypeKey(symbolCategory) { + return '_' + symbolCategory + 'Type'; +} +/** + * @inner + */ +function createSymbol$1(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 (!isArray(symbolSize)) { + symbolSize = [symbolSize, symbolSize]; + } + var symbolPath = 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 = sub([], toPos, fromPos); + 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$$1 = 5 * invScale; + // End + if (label.__position === 'end') { + textPosition = [d[0] * distance$$1 + toPos[0], d[1] * distance$$1 + 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$$1, cp[1] + n[1] * distance$$1]; + 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$$1 + fromPos[0], -d[1] * distance$$1 + 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$1(lineData, idx, seriesScope) { + Group.call(this); + + this._createLine(lineData, idx, seriesScope); +} + +var lineProto = Line$1.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; + initProps(line, { + shape: { + percent: 1 + } + }, seriesModel, idx); + + this.add(line); + + var label = new Text({ + name: 'label' + }); + this.add(label); + + each$1(SYMBOL_CATEGORIES, function (symbolCategory) { + var symbol = createSymbol$1(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); + updateProps(line, target, seriesModel, idx); + + each$1(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$1(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').getLineStyle(); + hoverLineStyle = itemModel.getModel('emphasis.lineStyle').getLineStyle(); + + labelModel = itemModel.getModel('label'); + hoverLabelModel = itemModel.getModel('emphasis.label'); + } + + var visualColor = lineData.getItemVisual(idx, 'color'); + var visualOpacity = retrieve3( + lineData.getItemVisual(idx, 'opacity'), + lineStyle.opacity, + 1 + ); + + line.useStyle(defaults( + { + strokeNoScale: true, + fill: 'none', + stroke: visualColor, + opacity: visualOpacity + }, + lineStyle + )); + line.hoverStyle = hoverLineStyle; + + // Update symbol + each$1(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 label = this.childOfName('label'); + var defaultLabelColor; + var baseText; + + // FIXME: the logic below probably should be merged to `graphic.setLabelStyle`. + if (showLabel || hoverShowLabel) { + defaultLabelColor = visualColor || '#000'; + + baseText = seriesModel.getFormattedLabel(idx, 'normal', lineData.dataType); + if (baseText == null) { + var rawVal = seriesModel.getRawValue(idx); + baseText = rawVal == null + ? lineData.getName(idx) + : isFinite(rawVal) + ? round$1(rawVal) + : rawVal; + } + } + var normalText = showLabel ? baseText : null; + var emphasisText = hoverShowLabel + ? retrieve2( + seriesModel.getFormattedLabel(idx, 'emphasis', lineData.dataType), + baseText + ) + : null; + + var labelStyle = label.style; + + // Always set `textStyle` even if `normalStyle.text` is null, because default + // values have to be set on `normalStyle`. + if (normalText != null || emphasisText != null) { + setTextStyle(label.style, labelModel, { + text: normalText + }, { + autoColor: defaultLabelColor + }); + + label.__textAlign = labelStyle.textAlign; + label.__verticalAlign = labelStyle.textVerticalAlign; + // 'start', 'middle', 'end' + label.__position = labelModel.get('position') || 'middle'; + } + + if (emphasisText != null) { + // Only these properties supported in this emphasis style here. + label.hoverStyle = { + text: emphasisText, + textFill: hoverLabelModel.getTextColor(true), + // For merging hover style to normal style, do not use + // `hoverLabelModel.getFont()` here. + fontStyle: hoverLabelModel.getShallow('fontStyle'), + fontWeight: hoverLabelModel.getShallow('fontWeight'), + fontSize: hoverLabelModel.getShallow('fontSize'), + fontFamily: hoverLabelModel.getShallow('fontFamily') + }; + } + else { + label.hoverStyle = { + text: null + }; + } + + label.ignore = !showLabel && !hoverShowLabel; + + setHoverStyle(this); +}; + +lineProto.highlight = function () { + this.trigger('emphasis'); +}; + +lineProto.downplay = function () { + this.trigger('normal'); +}; + +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(); +}; + +inherits(Line$1, Group); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * @module echarts/chart/helper/LineDraw + */ + +// import IncrementalDisplayable from 'zrender/src/graphic/IncrementalDisplayable'; + +/** + * @alias module:echarts/component/marker/LineDraw + * @constructor + */ +function LineDraw(ctor) { + this._ctor = ctor || Line$1; + + this.group = new Group(); +} + +var lineDrawProto = LineDraw.prototype; + +lineDrawProto.isPersistent = function () { + return true; +}; + +/** + * @param {module:echarts/data/List} lineData + */ +lineDrawProto.updateData = function (lineData) { + var lineDraw = this; + var group = lineDraw.group; + + var oldLineData = lineDraw._lineData; + lineDraw._lineData = lineData; + + // There is no oldLineData only when first rendering or switching from + // stream mode to normal mode, where previous elements should be removed. + if (!oldLineData) { + group.removeAll(); + } + + var seriesScope = makeSeriesScope$1(lineData); + + lineData.diff(oldLineData) + .add(function (idx) { + doAdd(lineDraw, lineData, idx, seriesScope); + }) + .update(function (newIdx, oldIdx) { + doUpdate(lineDraw, oldLineData, lineData, oldIdx, newIdx, seriesScope); + }) + .remove(function (idx) { + group.remove(oldLineData.getItemGraphicEl(idx)); + }) + .execute(); +}; + +function doAdd(lineDraw, lineData, idx, seriesScope) { + var itemLayout = lineData.getItemLayout(idx); + + if (!lineNeedsDraw(itemLayout)) { + return; + } + + var el = new lineDraw._ctor(lineData, idx, seriesScope); + lineData.setItemGraphicEl(idx, el); + lineDraw.group.add(el); +} + +function doUpdate(lineDraw, oldLineData, newLineData, oldIdx, newIdx, seriesScope) { + var itemEl = oldLineData.getItemGraphicEl(oldIdx); + + if (!lineNeedsDraw(newLineData.getItemLayout(newIdx))) { + lineDraw.group.remove(itemEl); + return; + } + + if (!itemEl) { + itemEl = new lineDraw._ctor(newLineData, newIdx, seriesScope); + } + else { + itemEl.updateData(newLineData, newIdx, seriesScope); + } + + newLineData.setItemGraphicEl(newIdx, itemEl); + + lineDraw.group.add(itemEl); +} + +lineDrawProto.updateLayout = function () { + var lineData = this._lineData; + + // Do not support update layout in incremental mode. + if (!lineData) { + return; + } + + lineData.eachItemGraphicEl(function (el, idx) { + el.updateLayout(lineData, idx); + }, this); +}; + +lineDrawProto.incrementalPrepareUpdate = function (lineData) { + this._seriesScope = makeSeriesScope$1(lineData); + this._lineData = null; + this.group.removeAll(); +}; + +lineDrawProto.incrementalUpdate = function (taskParams, lineData) { + function updateIncrementalAndHover(el) { + if (!el.isGroup) { + el.incremental = el.useHoverLayer = true; + } + } + + for (var idx = taskParams.start; idx < taskParams.end; idx++) { + var itemLayout = lineData.getItemLayout(idx); + + if (lineNeedsDraw(itemLayout)) { + var el = new this._ctor(lineData, idx, this._seriesScope); + el.traverse(updateIncrementalAndHover); + + this.group.add(el); + lineData.setItemGraphicEl(idx, el); + } + } +}; + +function makeSeriesScope$1(lineData) { + var hostModel = lineData.hostModel; + return { + lineStyle: hostModel.getModel('lineStyle').getLineStyle(), + hoverLineStyle: hostModel.getModel('emphasis.lineStyle').getLineStyle(), + labelModel: hostModel.getModel('label'), + hoverLabelModel: hostModel.getModel('emphasis.label') + }; +} + +lineDrawProto.remove = function () { + this._clearIncremental(); + this._incremental = null; + this.group.removeAll(); +}; + +lineDrawProto._clearIncremental = function () { + var incremental = this._incremental; + if (incremental) { + incremental.clearDisplaybles(); + } +}; + +function isPointNaN(pt) { + return isNaN(pt[0]) || isNaN(pt[1]); +} + +function lineNeedsDraw(pts) { + return !isPointNaN(pts[0]) && !isPointNaN(pts[1]); +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var v1 = []; +var v2 = []; +var v3 = []; +var quadraticAt$1 = quadraticAt; +var v2DistSquare = distSquare; +var mathAbs$1 = 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$1(p0[0], p1[0], p2[0], _t); + v1[1] = quadraticAt$1(p0[1], p1[1], p2[1], _t); + var diff = mathAbs$1(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$1(p0[0], p1[0], p2[0], t); + v2[1] = quadraticAt$1(p0[1], p1[1], p2[1], t); + v3[0] = quadraticAt$1(p0[0], p1[0], p2[0], next); + v3[1] = quadraticAt$1(p0[1], p1[1], p2[1], next); + + var diff = v2DistSquare(v2, center) - radiusSquare; + if (mathAbs$1(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 +var adjustEdge = function (graph, scale$$1) { + var tmp0 = []; + var quadraticSubdivide$$1 = quadraticSubdivide; + var pts = [[], [], []]; + var pts2 = [[], []]; + var v = []; + scale$$1 /= 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 = [ + clone$1(linePoints[0]), + clone$1(linePoints[1]) + ]; + if (linePoints[2]) { + linePoints.__original.push(clone$1(linePoints[2])); + } + } + var originalPoints = linePoints.__original; + // Quadratic curve + if (linePoints[2] != null) { + copy(pts[0], originalPoints[0]); + copy(pts[1], originalPoints[2]); + copy(pts[2], originalPoints[1]); + if (fromSymbol && fromSymbol != 'none') { + var symbolSize = getSymbolSize(edge.node1); + + var t = intersectCurveCircle(pts, originalPoints[0], symbolSize * scale$$1); + // Subdivide and get the second + quadraticSubdivide$$1(pts[0][0], pts[1][0], pts[2][0], t, tmp0); + pts[0][0] = tmp0[3]; + pts[1][0] = tmp0[4]; + quadraticSubdivide$$1(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$$1); + // Subdivide and get the first + quadraticSubdivide$$1(pts[0][0], pts[1][0], pts[2][0], t, tmp0); + pts[1][0] = tmp0[1]; + pts[2][0] = tmp0[2]; + quadraticSubdivide$$1(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 + copy(linePoints[0], pts[0]); + copy(linePoints[1], pts[2]); + copy(linePoints[2], pts[1]); + } + // Line + else { + copy(pts2[0], originalPoints[0]); + copy(pts2[1], originalPoints[1]); + + sub(v, pts2[1], pts2[0]); + normalize(v, v); + if (fromSymbol && fromSymbol != 'none') { + + var symbolSize = getSymbolSize(edge.node1); + + scaleAndAdd(pts2[0], pts2[0], v, symbolSize * scale$$1); + } + if (toSymbol && toSymbol != 'none') { + var symbolSize = getSymbolSize(edge.node2); + + scaleAndAdd(pts2[1], pts2[1], v, -symbolSize * scale$$1); + } + copy(linePoints[0], pts2[0]); + copy(linePoints[1], pts2[1]); + } + }); +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var nodeOpacityPath = ['itemStyle', 'opacity']; +var lineOpacityPath = ['lineStyle', 'opacity']; + +function getItemOpacity(item, opacityPath) { + return item.getVisual('opacity') || item.getModel().get(opacityPath); +} + +function fadeOutItem(item, opacityPath, opacityRatio) { + var el = item.getGraphicEl(); + + var opacity = getItemOpacity(item, opacityPath); + if (opacityRatio != null) { + opacity == null && (opacity = 1); + opacity *= opacityRatio; + } + + el.downplay && el.downplay(); + el.traverse(function (child) { + if (child.type !== 'group') { + child.setStyle('opacity', opacity); + } + }); +} + +function fadeInItem(item, opacityPath) { + var opacity = getItemOpacity(item, opacityPath); + var el = item.getGraphicEl(); + + el.highlight && el.highlight(); + el.traverse(function (child) { + if (child.type !== 'group') { + child.setStyle('opacity', opacity); + } + }); +} + +extendChartView({ + + type: 'graph', + + init: function (ecModel, api) { + var symbolDraw = new SymbolDraw(); + var lineDraw = new LineDraw(); + var group = this.group; + + this._controller = new RoamController(api.getZr()); + this._controllerHost = {target: group}; + + group.add(symbolDraw.group); + group.add(lineDraw.group); + + this._symbolDraw = symbolDraw; + this._lineDraw = lineDraw; + + 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 { + 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, ecModel, 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', el.__focusNodeAdjacency); + el.off('mouseout', el.__unfocusNodeAdjacency); + + if (itemModel.get('focusNodeAdjacency')) { + el.on('mouseover', el.__focusNodeAdjacency = function () { + api.dispatchAction({ + type: 'focusNodeAdjacency', + seriesId: seriesModel.id, + dataIndex: el.dataIndex + }); + }); + el.on('mouseout', el.__unfocusNodeAdjacency = function () { + api.dispatchAction({ + type: 'unfocusNodeAdjacency', + seriesId: seriesModel.id + }); + }); + + } + + }, this); + + data.graph.eachEdge(function (edge) { + var el = edge.getGraphicEl(); + + el.off('mouseover', el.__focusNodeAdjacency); + el.off('mouseout', el.__unfocusNodeAdjacency); + + if (edge.getModel().get('focusNodeAdjacency')) { + el.on('mouseover', el.__focusNodeAdjacency = function () { + api.dispatchAction({ + type: 'focusNodeAdjacency', + seriesId: seriesModel.id, + edgeDataIndex: edge.dataIndex + }); + }); + el.on('mouseout', el.__unfocusNodeAdjacency = function () { + api.dispatchAction({ + type: 'unfocusNodeAdjacency', + seriesId: seriesModel.id + }); + }); + } + }); + + 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, + textOrigin: 'center' + }); + symbolPath.hoverStyle && (symbolPath.hoverStyle.textPosition = textPosition); + } + else { + symbolPath.setStyle({ + textRotation: 0 + }); + } + }); + + this._firstRender = false; + }, + + dispose: function () { + this._controller && this._controller.dispose(); + this._controllerHost = {}; + }, + + focusNodeAdjacency: function (seriesModel, ecModel, api, payload) { + var data = this._model.getData(); + var graph = data.graph; + var dataIndex = payload.dataIndex; + var edgeDataIndex = payload.edgeDataIndex; + + var node = graph.getNodeByIndex(dataIndex); + var edge = graph.getEdgeByIndex(edgeDataIndex); + + if (!node && !edge) { + return; + } + + graph.eachNode(function (node) { + fadeOutItem(node, nodeOpacityPath, 0.1); + }); + graph.eachEdge(function (edge) { + fadeOutItem(edge, lineOpacityPath, 0.1); + }); + + if (node) { + fadeInItem(node, nodeOpacityPath); + each$1(node.edges, function (adjacentEdge) { + if (adjacentEdge.dataIndex < 0) { + return; + } + fadeInItem(adjacentEdge, lineOpacityPath); + fadeInItem(adjacentEdge.node1, nodeOpacityPath); + fadeInItem(adjacentEdge.node2, nodeOpacityPath); + }); + } + if (edge) { + fadeInItem(edge, lineOpacityPath); + fadeInItem(edge.node1, nodeOpacityPath); + fadeInItem(edge.node2, nodeOpacityPath); + } + }, + + unfocusNodeAdjacency: function (seriesModel, ecModel, api, payload) { + var graph = this._model.getData().graph; + + graph.eachNode(function (node) { + fadeOutItem(node, nodeOpacityPath); + }); + graph.eachEdge(function (edge) { + fadeOutItem(edge, lineOpacityPath); + }); + }, + + _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, ecModel, api) { + var controller = this._controller; + var controllerHost = this._controllerHost; + var group = this.group; + + controller.setPointerChecker(function (e, x, y) { + var rect = group.getBoundingRect(); + rect.applyTransform(group.transform); + return rect.contain(x, y) + && !onIrrelevantElement(e, api, seriesModel); + }); + + if (seriesModel.coordinateSystem.type !== 'view') { + controller.disable(); + return; + } + controller.enable(seriesModel.get('roam')); + controllerHost.zoomLimit = seriesModel.get('scaleLimit'); + controllerHost.zoom = seriesModel.coordinateSystem.getZoom(); + + controller + .off('pan') + .off('zoom') + .on('pan', function (dx, dy) { + updateViewOnPan(controllerHost, dx, dy); + api.dispatchAction({ + seriesId: seriesModel.id, + type: 'graphRoam', + dx: dx, + dy: dy + }); + }) + .on('zoom', function (zoom, mouseX, mouseY) { + updateViewOnZoom(controllerHost, 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(); + } +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +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] + */ +registerAction(actionInfo, function (payload, ecModel) { + ecModel.eachComponent({mainType: 'series', query: payload}, function (seriesModel) { + var coordSys = seriesModel.coordinateSystem; + + var res = updateCenterAndZoom(coordSys, payload); + + seriesModel.setCenter + && seriesModel.setCenter(res.center); + + seriesModel.setZoom + && seriesModel.setZoom(res.zoom); + }); +}); + + +/** + * @payload + * @property {number} [seriesIndex] + * @property {string} [seriesId] + * @property {string} [seriesName] + * @property {number} [dataIndex] + */ +registerAction({ + type: 'focusNodeAdjacency', + event: 'focusNodeAdjacency', + update: 'series.graph:focusNodeAdjacency' +}, function () {}); + +/** + * @payload + * @property {number} [seriesIndex] + * @property {string} [seriesId] + * @property {string} [seriesName] + */ +registerAction({ + type: 'unfocusNodeAdjacency', + event: 'unfocusNodeAdjacency', + update: 'series.graph:unfocusNodeAdjacency' +}, function () {}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + + +var categoryFilter = 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); +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + + +var categoryVisual = 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); + // Add prefix to avoid conflict with Object.prototype. + categoryNameIdxMap['ec-' + name] = idx; + + var itemModel = categoriesData.getItemModel(idx); + var color = itemModel.get('itemStyle.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['ec-' + category]; + } + if (!data.getItemVisual(idx, 'color', true)) { + data.setItemVisual( + idx, 'color', + categoriesData.getItemVisual(category, 'color') + ); + } + } + }); + } + }); +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + + +function normalize$1(a) { + if (!(a instanceof Array)) { + a = [a, a]; + } + return a; +} + +var edgeVisual = function (ecModel) { + ecModel.eachSeriesByType('graph', function (seriesModel) { + var graph = seriesModel.getGraph(); + var edgeData = seriesModel.getEdgeData(); + var symbolType = normalize$1(seriesModel.get('edgeSymbol')); + var symbolSize = normalize$1(seriesModel.get('edgeSymbolSize')); + + var colorQuery = 'lineStyle.color'.split('.'); + var opacityQuery = 'lineStyle.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$1(itemModel.getShallow('symbol', true)); + var symbolSize = normalize$1(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); + }); + }); +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +function simpleLayout$1(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); +} + +function simpleLayoutEdge(graph) { + graph.eachEdge(function (edge) { + var curveness = edge.getModel().get('lineStyle.curveness') || 0; + var p1 = clone$1(edge.node1.getLayout()); + var p2 = clone$1(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); + }); +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var simpleLayout = 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(); + + var dimensions = []; + each$1(coordSys.dimensions, function (coordDim) { + dimensions = dimensions.concat(data.mapDimension(coordDim, true)); + }); + + for (var dataIndex = 0; dataIndex < data.count(); dataIndex++) { + var value = []; + var hasValue = false; + for (var i = 0; i < dimensions.length; i++) { + var val = data.get(dimensions[i], dataIndex); + if (!isNaN(val)) { + hasValue = true; + } + value.push(val); + } + if (hasValue) { + data.setItemLayout(dataIndex, coordSys.dataToPoint(value)); + } + else { + // Also {Array.}, not undefined to avoid if...else... statement + data.setItemLayout(dataIndex, [NaN, NaN]); + } + } + + simpleLayoutEdge(data.graph); + } + else if (!layout || layout === 'none') { + simpleLayout$1(seriesModel); + } + }); +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +function circularLayout$1(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 : 1) / 2; + + node.setLayout([ + r * Math.cos(angle) + cx, + r * Math.sin(angle) + cy + ]); + + angle += unitAngle * (sum ? value : 1) / 2; + }); + + nodeData.setLayout({ + cx: cx, + cy: cy + }); + + graph.eachEdge(function (edge) { + var curveness = edge.getModel().get('lineStyle.curveness') || 0; + var p1 = clone$1(edge.node1.getLayout()); + var p2 = clone$1(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]); + }); +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var circularLayout = function (ecModel) { + ecModel.eachSeriesByType('graph', function (seriesModel) { + if (seriesModel.get('layout') === 'circular') { + circularLayout$1(seriesModel); + } + }); +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/* +* The layout implementation references to d3.js. The use of +* the source code of this file is also subject to the terms +* and consitions of its license (BSD-3Clause, see +* ). +*/ + +var scaleAndAdd$2 = scaleAndAdd; + +// function adjacentNode(n, e) { +// return e.n1 === n ? e.n2 : e.n1; +// } + +function forceLayout$1(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 = create( + width * (Math.random() - 0.5) + center[0], + height * (Math.random() - 0.5) + center[1] + ); + // } + } + n.pp = clone$1(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; + + sub(v12, n2.p, n1.p); + var d = len(v12) - e.d; + var w = n2.w / (n1.w + n2.w); + + if (isNaN(w)) { + w = 0; + } + + normalize(v12, v12); + + !n1.fixed && scaleAndAdd$2(n1.p, n1.p, v12, w * d * friction); + !n2.fixed && scaleAndAdd$2(n2.p, n2.p, v12, -(1 - w) * d * friction); + } + // Gravity + for (var i = 0; i < nLen; i++) { + var n = nodes[i]; + if (!n.fixed) { + sub(v12, center, n.p); + // var d = vec2.len(v12); + // vec2.scale(v12, v12, 1 / d); + // var gravityFactor = gravity; + scaleAndAdd$2(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]; + sub(v12, n2.p, n1.p); + var d = len(v12); + if (d === 0) { + // Random repulse + set(v12, Math.random() - 0.5, Math.random() - 0.5); + d = 1; + } + var repFact = (n1.rep + n2.rep) / d / d; + !n1.fixed && scaleAndAdd$2(n1.pp, n1.pp, v12, repFact); + !n2.fixed && scaleAndAdd$2(n2.pp, n2.pp, v12, -repFact); + } + } + var v = []; + for (var i = 0; i < nLen; i++) { + var n = nodes[i]; + if (!n.fixed) { + sub(v, n.p, n.pp); + scaleAndAdd$2(n.p, n.p, v, friction); + copy(n.pp, n.p); + } + } + + friction = friction * 0.992; + + cb && cb(nodes, edges, friction < 0.01); + } + }; +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var forceLayout = 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') { + simpleLayout$1(graphSeries); + } + else if (initLayout === 'circular') { + circularLayout$1(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 (!isArray(repulsion)) { + repulsion = [repulsion, repulsion]; + } + if (!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 rep = linearMap(value, nodeDataExtent, repulsion); + if (isNaN(rep)) { + rep = (repulsion[0] + repulsion[1]) / 2; + } + return { + w: rep, + rep: rep, + fixed: nodeData.getItemModel(idx).get('fixed'), + 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 = 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.curveness') || 0 + }; + }); + + var coordSys = graphSeries.coordinateSystem; + var rect = coordSys.getBoundingRect(); + var forceInstance = forceLayout$1(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 + 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] || []; + copy(points[0], p1); + 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; + } + }); +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +// FIXME Where to create the simple view coordinate system +function getViewRect$1(seriesModel, api, aspect) { + var option = seriesModel.getBoxLayoutParams(); + option.aspect = aspect; + return getLayoutRect(option, { + width: api.getWidth(), + height: api.getHeight() + }); +} + +var createView = function (ecModel, api) { + var viewList = []; + ecModel.eachSeriesByType('graph', function (seriesModel) { + var coordSysType = seriesModel.get('coordinateSystem'); + if (!coordSysType || coordSysType === 'view') { + + 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 = []; + + 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$1(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; + + var 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')); + + viewList.push(viewCoordSys); + } + }); + + return viewList; +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +registerProcessor(categoryFilter); + +registerVisual(visualSymbol('graph', 'circle', null)); +registerVisual(categoryVisual); +registerVisual(edgeVisual); + +registerLayout(simpleLayout); +registerLayout(circularLayout); +registerLayout(forceLayout); + +// Graph view coordinate system +registerCoordinateSystem('graphView', { + create: createView +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var GaugeSeries = SeriesModel.extend({ + + type: 'series.gauge', + + getInitialData: function (option, ecModel) { + var dataOpt = option.data || []; + if (!isArray(dataOpt)) { + dataOpt = [dataOpt]; + } + option.data = dataOpt; + return createListSimply(this, ['value']); + }, + + 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, + color: 'auto' + }, + pointer: { + show: true, + length: '80%', + width: 8 + }, + itemStyle: { + color: 'auto' + }, + title: { + show: true, + // x, y,单位px + offsetCenter: [0, '-40%'], + // 其余属性默认使用全局文本样式,详见TEXTSTYLE + color: '#333', + fontSize: 15 + }, + detail: { + show: true, + backgroundColor: 'rgba(0,0,0,0)', + borderWidth: 0, + borderColor: '#ccc', + width: 100, + height: null, // self-adaption + padding: [5, 10], + // x, y,单位px + offsetCenter: [0, '40%'], + // formatter: null, + // 其余属性默认使用全局文本样式,详见TEXTSTYLE + color: 'auto', + fontSize: 30 + } + } +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var PointerPath = Path.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; + } +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +function 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$1(center[0], api.getWidth()); + var cy = parsePercent$1(center[1], api.getHeight()); + var r = parsePercent$1(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 != null ? label : ''); + } + else if (typeof labelFormatter === 'function') { + label = labelFormatter(label); + } + } + + return label; +} + +var PI2$5 = Math.PI * 2; + +var GaugeView = Chart.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 + ); + }, + + dispose: function () {}, + + _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$5; + + 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 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$1( + splitLineModel.get('length'), r + ); + var tickLen = parsePercent$1( + 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(); + + 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 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( + round$1(i / splitNumber * (maxVal - minVal) + minVal), + labelModel.get('formatter') + ); + var distance = labelModel.get('distance'); + var autoColor = getColor(i / splitNumber); + + group.add(new Text({ + style: setTextStyle({}, labelModel, { + text: label, + x: unitX * (r - splitLineLen - distance) + cx, + y: unitY * (r - splitLineLen - distance) + cy, + textVerticalAlign: unitY < -0.4 ? 'top' : (unitY > 0.4 ? 'bottom' : 'middle'), + textAlign: unitX < -0.4 ? 'left' : (unitX > 0.4 ? 'right' : 'center') + }, {autoColor: autoColor}), + silent: true + })); + } + + // 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 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 group = this.group; + var oldData = this._data; + + if (!seriesModel.get('pointer.show')) { + // Remove old element + oldData && oldData.eachItemGraphicEl(function (el) { + group.remove(el); + }); + return; + } + + var valueExtent = [+seriesModel.get('min'), +seriesModel.get('max')]; + var angleExtent = [startAngle, endAngle]; + + var data = seriesModel.getData(); + var valueDim = data.mapDimension('value'); + + data.diff(oldData) + .add(function (idx) { + var pointer = new PointerPath({ + shape: { + angle: startAngle + } + }); + + initProps(pointer, { + shape: { + angle: linearMap(data.get(valueDim, idx), valueExtent, angleExtent, true) + } + }, seriesModel); + + group.add(pointer); + data.setItemGraphicEl(idx, pointer); + }) + .update(function (newIdx, oldIdx) { + var pointer = oldData.getItemGraphicEl(oldIdx); + + updateProps(pointer, { + shape: { + angle: linearMap(data.get(valueDim, 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$1( + pointerModel.get('width'), posInfo.r + ), + r: parsePercent$1(pointerModel.get('length'), posInfo.r) + }); + + pointer.useStyle(itemModel.getModel('itemStyle').getItemStyle()); + + if (pointer.style.fill === 'auto') { + pointer.setStyle('fill', getColor( + linearMap(data.get(valueDim, idx), valueExtent, [0, 1], true) + )); + } + + setHoverStyle( + pointer, itemModel.getModel('emphasis.itemStyle').getItemStyle() + ); + }); + + this._data = data; + }, + + _renderTitle: function ( + seriesModel, ecModel, api, getColor, posInfo + ) { + var data = seriesModel.getData(); + var valueDim = data.mapDimension('value'); + var titleModel = seriesModel.getModel('title'); + if (titleModel.get('show')) { + var offsetCenter = titleModel.get('offsetCenter'); + var x = posInfo.cx + parsePercent$1(offsetCenter[0], posInfo.r); + var y = posInfo.cy + parsePercent$1(offsetCenter[1], posInfo.r); + + var minVal = +seriesModel.get('min'); + var maxVal = +seriesModel.get('max'); + var value = seriesModel.getData().get(valueDim, 0); + var autoColor = getColor( + linearMap(value, [minVal, maxVal], [0, 1], true) + ); + + this.group.add(new Text({ + silent: true, + style: setTextStyle({}, titleModel, { + x: x, + y: y, + // FIXME First data name ? + text: data.getName(0), + textAlign: 'center', + textVerticalAlign: 'middle' + }, {autoColor: autoColor, forceRich: true}) + })); + } + }, + + _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 offsetCenter = detailModel.get('offsetCenter'); + var x = posInfo.cx + parsePercent$1(offsetCenter[0], posInfo.r); + var y = posInfo.cy + parsePercent$1(offsetCenter[1], posInfo.r); + var width = parsePercent$1(detailModel.get('width'), posInfo.r); + var height = parsePercent$1(detailModel.get('height'), posInfo.r); + var data = seriesModel.getData(); + var value = data.get(data.mapDimension('value'), 0); + var autoColor = getColor( + linearMap(value, [minVal, maxVal], [0, 1], true) + ); + + this.group.add(new Text({ + silent: true, + style: setTextStyle({}, detailModel, { + x: x, + y: y, + text: formatLabel( + // FIXME First data name ? + value, detailModel.get('formatter') + ), + textWidth: isNaN(width) ? null : width, + textHeight: isNaN(height) ? null : height, + textAlign: 'center', + textVerticalAlign: 'middle' + }, {autoColor: autoColor, forceRich: true}) + })); + } + } +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var FunnelSeries = 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.getRawData(); + }; + // Extend labelLine emphasis + this._defaultLabelLine(option); + }, + + getInitialData: function (option, ecModel) { + return createListSimply(this, ['value']); + }, + + _defaultLabelLine: function (option) { + // Extend labelLine emphasis + defaultEmphasis(option, 'labelLine', ['show']); + + var labelLineNormalOpt = option.labelLine; + var labelLineEmphasisOpt = option.emphasis.labelLine; + // Not show label line if `label.normal.show = false` + labelLineNormalOpt.show = labelLineNormalOpt.show + && option.label.show; + labelLineEmphasisOpt.show = labelLineEmphasisOpt.show + && option.emphasis.label.show; + }, + + // Overwrite + getDataParams: function (dataIndex) { + var data = this.getData(); + var params = FunnelSeries.superCall(this, 'getDataParams', dataIndex); + var valueDim = data.mapDimension('value'); + var sum = data.getSum(valueDim); + // Percent is 0 if sum is 0 + params.percent = !sum ? 0 : +(data.get(valueDim, dataIndex) / sum * 100).toFixed(2); + + params.$vars.push('percent'); + return params; + }, + + 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: { + show: true, + position: 'outer' + // formatter: 标签文本格式器,同Tooltip.formatter,不支持异步回调 + }, + labelLine: { + show: true, + length: 20, + lineStyle: { + // color: 各异, + width: 1, + type: 'solid' + } + }, + itemStyle: { + // color: 各异, + borderColor: '#fff', + borderWidth: 1 + }, + emphasis: { + label: { + show: true + } + } + } +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * Piece of pie including Sector, Label, LabelLine + * @constructor + * @extends {module:zrender/graphic/Group} + */ +function FunnelPiece(data, idx) { + + Group.call(this); + + var polygon = new Polygon(); + var labelLine = new Polyline(); + var text = new 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; + +var opacityAccessPath = ['itemStyle', '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 }); + initProps(polygon, { + style: { + opacity: opacity + } + }, seriesModel, idx); + } + else { + 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( + defaults( + { + lineJoin: 'round', + fill: visualColor + }, + itemStyleModel.getItemStyle(['opacity']) + ) + ); + polygon.hoverStyle = itemStyleModel.getModel('emphasis').getItemStyle(); + + this._updateLabel(data, idx); + + 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'); + + updateProps(labelLine, { + shape: { + points: labelLayout.linePoints || labelLayout.linePoints + } + }, seriesModel, idx); + + updateProps(labelText, { + style: { + x: labelLayout.x, + y: labelLayout.y + } + }, seriesModel, idx); + labelText.attr({ + rotation: labelLayout.rotation, + origin: [labelLayout.x, labelLayout.y], + z2: 10 + }); + + var labelModel = itemModel.getModel('label'); + var labelHoverModel = itemModel.getModel('emphasis.label'); + var labelLineModel = itemModel.getModel('labelLine'); + var labelLineHoverModel = itemModel.getModel('emphasis.labelLine'); + var visualColor = data.getItemVisual(idx, 'color'); + + setLabelStyle( + labelText.style, labelText.hoverStyle = {}, labelModel, labelHoverModel, + { + labelFetcher: data.hostModel, + labelDataIndex: idx, + defaultText: data.getName(idx), + autoColor: visualColor, + useInsideStyle: !!labelLayout.inside + }, + { + textAlign: labelLayout.textAlign, + textVerticalAlign: labelLayout.verticalAlign + } + ); + + 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()); + + labelLine.hoverStyle = labelLineHoverModel.getModel('lineStyle').getLineStyle(); +}; + +inherits(FunnelPiece, Group); + + +var FunnelView = Chart.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; + }, + + dispose: function () {} +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +function getViewRect$2(seriesModel, api) { + return getLayoutRect( + seriesModel.getBoxLayoutParams(), { + width: api.getWidth(), + height: api.getHeight() + } + ); +} + +function getSortedIndices(data, sort) { + var valueDim = data.mapDimension('value'); + var valueArr = data.mapArray(valueDim, function (val) { + return val; + }); + var indices = []; + var isAscending = sort === 'ascending'; + for (var i = 0, len = data.count(); i < len; i++) { + indices[i] = i; + } + + // Add custom sortable function & none sortable opetion by "options.sort" + if (typeof sort === 'function') { + indices.sort(sort); + } else if (sort !== 'none') { + indices.sort(function (a, b) { + return isAscending ? valueArr[a] - valueArr[b] : valueArr[b] - valueArr[a]; + }); + } + return indices; +} + +function labelLayout$1(data) { + data.each(function (idx) { + var itemModel = data.getItemModel(idx); + var labelModel = itemModel.getModel('label'); + var labelPosition = labelModel.get('position'); + + var labelLineModel = itemModel.getModel('labelLine'); + + 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 + }; + }); +} + +var funnelLayout = function (ecModel, api, payload) { + ecModel.eachSeriesByType('funnel', function (seriesModel) { + var data = seriesModel.getData(); + var valueDim = data.mapDimension('value'); + var sort = seriesModel.get('sort'); + var viewRect = getViewRect$2(seriesModel, api); + var indices = getSortedIndices(data, sort); + + var sizeExtent = [ + parsePercent$1(seriesModel.get('minSize'), viewRect.width), + parsePercent$1(seriesModel.get('maxSize'), viewRect.width) + ]; + var dataExtent = data.getDataExtent(valueDim); + 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(valueDim, idx) || 0; + var itemWidth = 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 itemModel = data.getItemModel(idx); + var height = itemModel.get('itemStyle.height'); + if (height == null) { + height = itemHeight; + } + else { + height = parsePercent$1(height, viewRect.height); + if (sort === 'ascending') { + height = -height; + } + } + + var start = getLinePoints(idx, y); + var end = getLinePoints(nextIdx, y + height); + + y += height + gap; + + data.setItemLayout(idx, { + points: start.concat(end.slice().reverse()) + }); + } + + labelLayout$1(data); + }); +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +registerVisual(dataColor('funnel')); +registerLayout(funnelLayout); +registerProcessor(dataFilter('funnel')); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var parallelPreprocessor = function (option) { + createParallelIfNeeded(option); + mergeAxisOptionFromParallel(option); +}; + +/** + * Create a parallel coordinate if not exists. + * @inner + */ +function createParallelIfNeeded(option) { + if (option.parallel) { + return; + } + + var hasParallelSeries = false; + + each$1(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 = normalizeToArray(option.parallelAxis); + + each$1(axes, function (axisOption) { + if (!isObject$1(axisOption)) { + return; + } + + var parallelIndex = axisOption.parallelIndex || 0; + var parallelOption = normalizeToArray(option.parallel)[parallelIndex]; + + if (parallelOption && parallelOption.parallelAxisDefault) { + merge(axisOption, parallelOption.parallelAxisDefault, false); + } + }); +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * @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, + + /** + * @override + */ + isHorizontal: function () { + return this.coordinateSystem.getModel().get('layout') !== 'horizontal'; + } + +}; + +inherits(ParallelAxis, Axis); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * Calculate slider move result. + * Usage: + * (1) If both handle0 and handle1 are needed to be moved, set minSpan the same as + * maxSpan and the same as `Math.abs(handleEnd[1] - handleEnds[0])`. + * (2) If handle0 is forbidden to cross handle1, set minSpan as `0`. + * + * @param {number} delta Move length. + * @param {Array.} handleEnds handleEnds[0] can 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 {number|string} handleIndex Can be 'all', means that both move the two handleEnds, + * where the input minSpan and maxSpan will not work. + * @param {number} [minSpan] The range of dataZoom can not be smaller than that. + * If not set, handle0 and cross handle1. If set as a non-negative + * number (including `0`), handles will push each other when reaching + * the minSpan. + * @param {number} [maxSpan] The range of dataZoom can not be larger than that. + * @return {Array.} The input handleEnds. + */ +var sliderMove = function (delta, handleEnds, extent, handleIndex, minSpan, maxSpan) { + // Normalize firstly. + handleEnds[0] = restrict$1(handleEnds[0], extent); + handleEnds[1] = restrict$1(handleEnds[1], extent); + + delta = delta || 0; + + var extentSpan = extent[1] - extent[0]; + + // Notice maxSpan and minSpan can be null/undefined. + if (minSpan != null) { + minSpan = restrict$1(minSpan, [0, extentSpan]); + } + if (maxSpan != null) { + maxSpan = Math.max(maxSpan, minSpan != null ? minSpan : 0); + } + if (handleIndex === 'all') { + minSpan = maxSpan = Math.abs(handleEnds[1] - handleEnds[0]); + handleIndex = 0; + } + + var originalDistSign = getSpanSign(handleEnds, handleIndex); + + handleEnds[handleIndex] += delta; + + // Restrict in extent. + var extentMinSpan = minSpan || 0; + var realExtent = extent.slice(); + originalDistSign.sign < 0 ? (realExtent[0] += extentMinSpan) : (realExtent[1] -= extentMinSpan); + handleEnds[handleIndex] = restrict$1(handleEnds[handleIndex], realExtent); + + // Expand span. + var currDistSign = getSpanSign(handleEnds, handleIndex); + if (minSpan != null && ( + currDistSign.sign !== originalDistSign.sign || currDistSign.span < minSpan + )) { + // If minSpan exists, 'cross' is forbinden. + handleEnds[1 - handleIndex] = handleEnds[handleIndex] + originalDistSign.sign * minSpan; + } + + // Shrink span. + var currDistSign = getSpanSign(handleEnds, handleIndex); + if (maxSpan != null && currDistSign.span > maxSpan) { + handleEnds[1 - handleIndex] = handleEnds[handleIndex] + currDistSign.sign * maxSpan; + } + + return handleEnds; +}; + +function getSpanSign(handleEnds, handleIndex) { + var dist = handleEnds[handleIndex] - handleEnds[1 - handleIndex]; + // If `handleEnds[0] === handleEnds[1]`, always believe that handleEnd[0] + // is at left of handleEnds[1] for non-cross case. + return {span: Math.abs(dist), sign: dist > 0 ? -1 : dist < 0 ? 1 : handleIndex ? -1 : 1}; +} + +function restrict$1(value, extend) { + return Math.min(extend[1], Math.max(extend[0], value)); +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * Parallel Coordinates + * + */ + +var each$11 = each$1; +var mathMin$5 = Math.min; +var mathMax$5 = Math.max; +var mathFloor$2 = Math.floor; +var mathCeil$2 = Math.ceil; +var round$2 = round$1; + +var PI$3 = Math.PI; + +function Parallel(parallelModel, ecModel, api) { + + /** + * key: dimension + * @type {Object.} + * @private + */ + this._axesMap = createHashMap(); + + /** + * 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$11(dimensions, function (dim, idx) { + + var axisIndex = parallelAxisIndex[idx]; + var axisModel = ecModel.getComponent('parallelAxis', axisIndex); + + var axis = this._axesMap.set(dim, new ParallelAxis( + dim, + createScaleByModel(axisModel), + [0, 0], + axisModel.get('type'), + axisIndex + )); + + var isCategory = axis.type === 'category'; + axis.onBand = isCategory && axisModel.get('boundaryGap'); + axis.inverse = axisModel.get('inverse'); + + // Injection + axisModel.axis = axis; + axis.model = axisModel; + axis.coordinateSystem = axisModel.coordinateSystem = this; + + }, 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); + }, + + /** + * @override + */ + containPoint: function (point) { + var layoutInfo = this._makeLayoutInfo(); + var axisBase = layoutInfo.axisBase; + var layoutBase = layoutInfo.layoutBase; + var pixelDimIndex = layoutInfo.pixelDimIndex; + var pAxis = point[1 - pixelDimIndex]; + var pLayout = point[pixelDimIndex]; + + return pAxis >= axisBase + && pAxis <= axisBase + layoutInfo.axisLength + && pLayout >= layoutBase + && pLayout <= layoutBase + layoutInfo.layoutLength; + }, + + getModel: function () { + return this._model; + }, + + /** + * Update properties from series + * @private + */ + _updateAxesFromSeries: function (parallelModel, ecModel) { + ecModel.eachSeries(function (seriesModel) { + + if (!parallelModel.contains(seriesModel, ecModel)) { + return; + } + + var data = seriesModel.getData(); + + each$11(this.dimensions, function (dim) { + var axis = this._axesMap.get(dim); + axis.scale.unionExtentFromData(data, data.mapDimension(dim)); + niceScaleExtent(axis.scale, 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 = getLayoutRect( + parallelModel.getBoxLayoutParams(), + { + width: api.getWidth(), + height: api.getHeight() + } + ); + + this._layoutAxes(); + }, + + /** + * @return {module:zrender/core/BoundingRect} + */ + getRect: function () { + return this._rect; + }, + + /** + * @private + */ + _makeLayoutInfo: function () { + var parallelModel = this._model; + var rect = this._rect; + var xy = ['x', 'y']; + var wh = ['width', 'height']; + var layout = parallelModel.get('layout'); + var pixelDimIndex = layout === 'horizontal' ? 0 : 1; + var layoutLength = rect[wh[pixelDimIndex]]; + var layoutExtent = [0, layoutLength]; + var axisCount = this.dimensions.length; + + var axisExpandWidth = restrict(parallelModel.get('axisExpandWidth'), layoutExtent); + var axisExpandCount = restrict(parallelModel.get('axisExpandCount') || 0, [0, axisCount]); + var axisExpandable = parallelModel.get('axisExpandable') + && axisCount > 3 + && axisCount > axisExpandCount + && axisExpandCount > 1 + && axisExpandWidth > 0 + && layoutLength > 0; + + // `axisExpandWindow` is According to the coordinates of [0, axisExpandLength], + // for sake of consider the case that axisCollapseWidth is 0 (when screen is narrow), + // where collapsed axes should be overlapped. + var axisExpandWindow = parallelModel.get('axisExpandWindow'); + var winSize; + if (!axisExpandWindow) { + winSize = restrict(axisExpandWidth * (axisExpandCount - 1), layoutExtent); + var axisExpandCenter = parallelModel.get('axisExpandCenter') || mathFloor$2(axisCount / 2); + axisExpandWindow = [axisExpandWidth * axisExpandCenter - winSize / 2]; + axisExpandWindow[1] = axisExpandWindow[0] + winSize; + } + else { + winSize = restrict(axisExpandWindow[1] - axisExpandWindow[0], layoutExtent); + axisExpandWindow[1] = axisExpandWindow[0] + winSize; + } + + var axisCollapseWidth = (layoutLength - winSize) / (axisCount - axisExpandCount); + // Avoid axisCollapseWidth is too small. + axisCollapseWidth < 3 && (axisCollapseWidth = 0); + + // Find the first and last indices > ewin[0] and < ewin[1]. + var winInnerIndices = [ + mathFloor$2(round$2(axisExpandWindow[0] / axisExpandWidth, 1)) + 1, + mathCeil$2(round$2(axisExpandWindow[1] / axisExpandWidth, 1)) - 1 + ]; + + // Pos in ec coordinates. + var axisExpandWindow0Pos = axisCollapseWidth / axisExpandWidth * axisExpandWindow[0]; + + return { + layout: layout, + pixelDimIndex: pixelDimIndex, + layoutBase: rect[xy[pixelDimIndex]], + layoutLength: layoutLength, + axisBase: rect[xy[1 - pixelDimIndex]], + axisLength: rect[wh[1 - pixelDimIndex]], + axisExpandable: axisExpandable, + axisExpandWidth: axisExpandWidth, + axisCollapseWidth: axisCollapseWidth, + axisExpandWindow: axisExpandWindow, + axisCount: axisCount, + winInnerIndices: winInnerIndices, + axisExpandWindow0Pos: axisExpandWindow0Pos + }; + }, + + /** + * @private + */ + _layoutAxes: function () { + var rect = this._rect; + var axes = this._axesMap; + var dimensions = this.dimensions; + var layoutInfo = this._makeLayoutInfo(); + var layout = layoutInfo.layout; + + axes.each(function (axis) { + var axisExtent = [0, layoutInfo.axisLength]; + var idx = axis.inverse ? 1 : 0; + axis.setExtent(axisExtent[idx], axisExtent[1 - idx]); + }); + + each$11(dimensions, function (dim, idx) { + var posInfo = (layoutInfo.axisExpandable + ? layoutAxisWithExpand : layoutAxisWithoutExpand + )(idx, layoutInfo); + + var positionTable = { + horizontal: { + x: posInfo.position, + y: layoutInfo.axisLength + }, + vertical: { + x: 0, + y: posInfo.position + } + }; + var rotationTable = { + horizontal: PI$3 / 2, + vertical: 0 + }; + + var position = [ + positionTable[layout].x + rect.x, + positionTable[layout].y + rect.y + ]; + + var rotation = rotationTable[layout]; + var transform = create$1(); + rotate(transform, transform, rotation); + translate(transform, transform, position); + + // TODO + // tick等排布信息。 + + // TODO + // 根据axis order 更新 dimensions顺序。 + + this._axesLayout[dim] = { + position: position, + rotation: rotation, + transform: transform, + axisNameAvailableWidth: posInfo.axisNameAvailableWidth, + axisLabelShow: posInfo.axisLabelShow, + nameTruncateMaxWidth: posInfo.nameTruncateMaxWidth, + tickDirection: 1, + labelDirection: 1 + }; + }, this); + }, + + /** + * Get axis by dim. + * @param {string} dim + * @return {module:echarts/coord/parallel/ParallelAxis} [description] + */ + getAxis: function (dim) { + return this._axesMap.get(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.get(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 {number} [start=0] the start dataIndex that travel from. + * @param {number} [end=data.count()] the next dataIndex of the last dataIndex will be travel. + */ + eachActiveState: function (data, callback, start, end) { + start == null && (start = 0); + end == null && (end = data.count()); + + var axesMap = this._axesMap; + var dimensions = this.dimensions; + var dataDimensions = []; + var axisModels = []; + + each$1(dimensions, function (axisDim) { + dataDimensions.push(data.mapDimension(axisDim)); + axisModels.push(axesMap.get(axisDim).model); + }); + + var hasActiveSet = this.hasAxisBrushed(); + + for (var dataIndex = start; dataIndex < end; dataIndex++) { + var activeState; + + if (!hasActiveSet) { + activeState = 'normal'; + } + else { + activeState = 'active'; + var values = data.getValues(dataDimensions, dataIndex); + for (var j = 0, lenj = dimensions.length; j < lenj; j++) { + var state = axisModels[j].getActiveState(values[j]); + + if (state === 'inactive') { + activeState = 'inactive'; + break; + } + } + } + + callback(activeState, dataIndex); + } + }, + + /** + * 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.get(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 applyTransform$1([coord, 0], axisLayout.transform); + }, + + /** + * Get axis layout. + */ + getAxisLayout: function (dim) { + return clone(this._axesLayout[dim]); + }, + + /** + * @param {Array.} point + * @return {Object} {axisExpandWindow, delta, behavior: 'jump' | 'slide' | 'none'}. + */ + getSlidedAxisExpandWindow: function (point) { + var layoutInfo = this._makeLayoutInfo(); + var pixelDimIndex = layoutInfo.pixelDimIndex; + var axisExpandWindow = layoutInfo.axisExpandWindow.slice(); + var winSize = axisExpandWindow[1] - axisExpandWindow[0]; + var extent = [0, layoutInfo.axisExpandWidth * (layoutInfo.axisCount - 1)]; + + // Out of the area of coordinate system. + if (!this.containPoint(point)) { + return {behavior: 'none', axisExpandWindow: axisExpandWindow}; + } + + // Conver the point from global to expand coordinates. + var pointCoord = point[pixelDimIndex] - layoutInfo.layoutBase - layoutInfo.axisExpandWindow0Pos; + + // For dragging operation convenience, the window should not be + // slided when mouse is the center area of the window. + var delta; + var behavior = 'slide'; + var axisCollapseWidth = layoutInfo.axisCollapseWidth; + var triggerArea = this._model.get('axisExpandSlideTriggerArea'); + // But consider touch device, jump is necessary. + var useJump = triggerArea[0] != null; + + if (axisCollapseWidth) { + if (useJump && axisCollapseWidth && pointCoord < winSize * triggerArea[0]) { + behavior = 'jump'; + delta = pointCoord - winSize * triggerArea[2]; + } + else if (useJump && axisCollapseWidth && pointCoord > winSize * (1 - triggerArea[0])) { + behavior = 'jump'; + delta = pointCoord - winSize * (1 - triggerArea[2]); + } + else { + (delta = pointCoord - winSize * triggerArea[1]) >= 0 + && (delta = pointCoord - winSize * (1 - triggerArea[1])) <= 0 + && (delta = 0); + } + delta *= layoutInfo.axisExpandWidth / axisCollapseWidth; + delta + ? sliderMove(delta, axisExpandWindow, extent, 'all') + // Avoid nonsense triger on mousemove. + : (behavior = 'none'); + } + // When screen is too narrow, make it visible and slidable, although it is hard to interact. + else { + var winSize = axisExpandWindow[1] - axisExpandWindow[0]; + var pos = extent[1] * pointCoord / winSize; + axisExpandWindow = [mathMax$5(0, pos - winSize / 2)]; + axisExpandWindow[1] = mathMin$5(extent[1], axisExpandWindow[0] + winSize); + axisExpandWindow[0] = axisExpandWindow[1] - winSize; + } + + return { + axisExpandWindow: axisExpandWindow, + behavior: behavior + }; + } +}; + +function restrict(len, extent) { + return mathMin$5(mathMax$5(len, extent[0]), extent[1]); +} + +function layoutAxisWithoutExpand(axisIndex, layoutInfo) { + var step = layoutInfo.layoutLength / (layoutInfo.axisCount - 1); + return { + position: step * axisIndex, + axisNameAvailableWidth: step, + axisLabelShow: true + }; +} + +function layoutAxisWithExpand(axisIndex, layoutInfo) { + var layoutLength = layoutInfo.layoutLength; + var axisExpandWidth = layoutInfo.axisExpandWidth; + var axisCount = layoutInfo.axisCount; + var axisCollapseWidth = layoutInfo.axisCollapseWidth; + var winInnerIndices = layoutInfo.winInnerIndices; + + var position; + var axisNameAvailableWidth = axisCollapseWidth; + var axisLabelShow = false; + var nameTruncateMaxWidth; + + if (axisIndex < winInnerIndices[0]) { + position = axisIndex * axisCollapseWidth; + nameTruncateMaxWidth = axisCollapseWidth; + } + else if (axisIndex <= winInnerIndices[1]) { + position = layoutInfo.axisExpandWindow0Pos + + axisIndex * axisExpandWidth - layoutInfo.axisExpandWindow[0]; + axisNameAvailableWidth = axisExpandWidth; + axisLabelShow = true; + } + else { + position = layoutLength - (axisCount - 1 - axisIndex) * axisCollapseWidth; + nameTruncateMaxWidth = axisCollapseWidth; + } + + return { + position: position, + axisNameAvailableWidth: axisNameAvailableWidth, + axisLabelShow: axisLabelShow, + nameTruncateMaxWidth: nameTruncateMaxWidth + }; +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * Parallel coordinate system creater. + */ + +function create$2(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; +} + +CoordinateSystemManager.register('parallel', {create: create$2}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var AxisModel$2 = 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'] + ] + )(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 = clone(intervals); + + // Normalize + if (activeIntervals) { + for (var i = activeIntervals.length - 1; i >= 0; i--) { + 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 || isNaN(value)) { + return 'inactive'; + } + + // Simple optimization + if (activeIntervals.length === 1) { + var interval = activeIntervals[0]; + if (interval[0] <= value && value <= interval[1]) { + return 'active'; + } + } + else { + 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$1 = { + + 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 +}; + +merge(AxisModel$2.prototype, axisModelCommonMixin); + +function getAxisType$1(axisName, option) { + return option.type || (option.data ? 'category' : 'value'); +} + +axisModelCreator('parallel', AxisModel$2, getAxisType$1, defaultOption$1); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +ComponentModel.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%' ? + axisExpandRate: 17, + axisExpandDebounce: 50, + // [out, in, jumpTarget]. In percentage. If use [null, 0.05], null means full. + // Do not doc to user until necessary. + axisExpandSlideTriggerArea: [-0.15, 0.05, 0.4], + axisExpandTriggerOn: 'click', // 'mousemove' or 'click' + + parallelAxisDefault: null + }, + + /** + * @override + */ + init: function () { + ComponentModel.prototype.init.apply(this, arguments); + + this.mergeOption({}); + }, + + /** + * @override + */ + mergeOption: function (newOption) { + var thisOption = this.option; + + newOption && 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) { + each$1( + ['axisExpandable', 'axisExpandCenter', 'axisExpandCount', 'axisExpandWidth', 'axisExpandWindow'], + 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 = filter(this.dependentModels.parallelAxis, function (axisModel) { + // Can not use this.contains here, because + // initialization has not been completed yet. + return (axisModel.get('parallelIndex') || 0) === this.componentIndex; + }, this); + + each$1(axisModels, function (axisModel) { + dimensions.push('dim' + axisModel.get('dim')); + parallelAxisIndex.push(axisModel.componentIndex); + }); + } + +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * @payload + * @property {string} parallelAxisId + * @property {Array.>} intervals + */ +var actionInfo$1 = { + type: 'axisAreaSelect', + event: 'axisAreaSelected' + // update: 'updateVisual' +}; + +registerAction(actionInfo$1, function (payload, ecModel) { + ecModel.eachComponent( + {mainType: 'parallelAxis', query: payload}, + function (parallelAxisModel) { + parallelAxisModel.axis.model.setActiveIntervals(payload.intervals); + } + ); +}); + +/** + * @payload + */ +registerAction('parallelAxisExpand', function (payload, ecModel) { + ecModel.eachComponent( + {mainType: 'parallel', query: payload}, + function (parallelModel) { + parallelModel.setAxisExpand(payload); + } + ); + +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var curry$2 = curry; +var each$12 = each$1; +var map$2 = map; +var mathMin$6 = Math.min; +var mathMax$6 = Math.max; +var mathPow$2 = 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 (__DEV__) { + assert$1(zr); + } + + Eventful.call(this); + + /** + * @type {module:zrender/zrender~ZRender} + * @private + */ + this._zr = zr; + + /** + * @type {module:zrender/container/Group} + * @readOnly + */ + this.group = new Group(); + + /** + * Only for drawing (after enabledBrush). + * 'line', 'rect', 'polygon' or false + * If passing false/null/undefined, disable brush. + * If passing 'auto', determined by panel.defaultBrushType + * @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 (__DEV__) { + this._mounted; + } + + /** + * @private + * @type {string} + */ + this._uid = 'brushController_' + baseUID++; + + /** + * @private + * @type {Object} + */ + this._handlers = {}; + each$12(mouseHandlers, function (handler, eventName) { + this._handlers[eventName] = 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 passing false/null/undefined, disable brush. + * If passing 'auto', determined by panel.defaultBrushType. + * ('auto' can not be used in global panel) + * @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] + * @param {number} [brushOption.z] + */ + enableBrush: function (brushOption) { + if (__DEV__) { + assert$1(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, // mandatory. + * clipPath, // mandatory. function. + * isTargetByCursor, // mandatory. function. + * defaultBrushType, // optional, only used when brushType is 'auto'. + * getLinearBrushOtherExtent, // optional. function. + * } + */ + setPanels: function (panelOpts) { + if (panelOpts && panelOpts.length) { + var panels = this._panels = {}; + each$1(panelOpts, function (panelOpts) { + panels[panelOpts.panelId] = clone(panelOpts); + }); + } + else { + this._panels = null; + } + return this; + }, + + /** + * @param {Object} [opt] + * @return {boolean} [opt.enableGlobalPan=false] + */ + mount: function (opt) { + opt = opt || {}; + + if (__DEV__) { + 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] + }); + this._transform = thisGroup.getLocalTransform(); + + return this; + }, + + eachCover: function (cb, context) { + each$12(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. (can not be 'auto') + * `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 (__DEV__) { + assert$1(this._mounted); + } + + brushOptionList = map(brushOptionList, function (brushOption) { + return merge(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 () { + if (__DEV__) { + if (!this._mounted) { + return; + } + } + + this.enableBrush(false); + + // container may 'removeAll' outside. + clearCovers(this); + this._zr.remove(this.group); + + if (__DEV__) { + this._mounted = false; // should be at last. + } + + return this; + }, + + dispose: function () { + this.unmount(); + this.off(); + } +}; + +mixin(BrushController, Eventful); + +function doEnableBrush(controller, brushOption) { + var zr = controller._zr; + + // Consider roam, which takes globalPan too. + if (!controller._enableGlobalPan) { + take(zr, MUTEX_RESOURCE_KEY, controller._uid); + } + + each$12(controller._handlers, function (handler, eventName) { + zr.on(eventName, handler); + }); + + controller._brushType = brushOption.brushType; + controller._brushOption = merge(clone(DEFAULT_BRUSH_OPT), brushOption, true); +} + +function doDisableBrush(controller) { + var zr = controller._zr; + + release(zr, MUTEX_RESOURCE_KEY, controller._uid); + + each$12(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); + cover.__brushOption = brushOption; + updateZ$1(cover, brushOption); + controller.group.add(cover); + return cover; +} + +function endCreating(controller, creatingCover) { + var coverRenderer = getCoverRenderer(creatingCover); + if (coverRenderer.endCreating) { + coverRenderer.endCreating(controller, creatingCover); + updateZ$1(creatingCover, creatingCover.__brushOption); + } + return creatingCover; +} + +function updateCoverShape(controller, cover) { + var brushOption = cover.__brushOption; + getCoverRenderer(cover).updateCoverShape( + controller, cover, brushOption.range, brushOption + ); +} + +function updateZ$1(cover, brushOption) { + var z = brushOption.z; + z == null && (z = COVER_Z); + cover.traverse(function (el) { + el.z = z; + el.z2 = 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]; +} + +// return target panel or `true` (means global panel) +function getPanelByPoint(controller, e, localCursorPoint) { + var panels = controller._panels; + if (!panels) { + return true; // Global panel + } + var panel; + var transform = controller._transform; + each$12(panels, function (pn) { + pn.isTargetByCursor(e, localCursorPoint, transform) && (panel = pn); + }); + return panel; +} + +// Return a panel or true +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$12(covers, function (cover) { + controller.group.remove(cover); + }, controller); + covers.length = 0; + + return !!originalLength; +} + +function trigger(controller, opt) { + var areas = map$2(controller._covers, function (cover) { + var brushOption = cover.__brushOption; + var range = 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$2(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 Group(); + + cover.add(new Rect({ + name: 'main', + style: makeStyle(brushOption), + silent: true, + draggable: true, + cursor: 'move', + drift: curry$2(doDrift, controller, cover, 'nswe'), + ondragend: curry$2(trigger, controller, {isEnd: true}) + })); + + each$12( + edgeNames, + function (name) { + cover.add(new Rect({ + name: name, + style: {opacity: 0}, + draggable: true, + silent: true, + invisible: true, + drift: curry$2(doDrift, controller, cover, name), + ondragend: curry$2(trigger, controller, {isEnd: true}) + })); + } + ); + + return cover; +} + +function updateBaseRect(controller, cover, localRange, brushOption) { + var lineWidth = brushOption.brushStyle.lineWidth || 0; + var handleSize = mathMax$6(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$12( + ['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 defaults({strokeNoScale: true}, brushOption.brushStyle); +} + +function formatRectRange(x, y, x2, y2) { + var min = [mathMin$6(x, x2), mathMin$6(y, y2)]; + var max = [mathMax$6(x, x2), mathMax$6(y, y2)]; + + return [ + [min[0], max[0]], // x range + [min[1], max[1]] // y range + ]; +} + +function getTransform$1(controller) { + return 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$$1 = {w: 'left', e: 'right', n: 'top', s: 'bottom'}; + var inverseMap = {left: 'w', right: 'e', top: 'n', bottom: 's'}; + var globalDir = transformDirection( + map$$1[localDirection], getTransform$1(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$12(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$12(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); + + return (panel && panel !== true) + ? panel.clipPath(data, controller._transform) + : clone(data); +} + +function pointsToRect(points) { + var xmin = mathMin$6(points[0][0], points[1][0]); + var ymin = mathMin$6(points[0][1], points[1][1]); + var xmax = mathMax$6(points[0][0], points[1][0]); + var ymax = mathMax$6(points[0][1], points[1][1]); + + return { + x: xmin, + y: ymin, + width: xmax - xmin, + height: ymax - ymin + }; +} + +function resetCursor(controller, e, localCursorPoint) { + // Check active + if (!controller._brushType) { + return; + } + + var zr = controller._zr; + var covers = controller._covers; + var currPanel = getPanelByPoint(controller, e, localCursorPoint); + + // Check whether in covers. + if (!controller._dragging) { + for (var i = 0; i < covers.length; i++) { + var brushOption = covers[i].__brushOption; + if (currPanel + && (currPanel === true || brushOption.panelId === currPanel.panelId) + && coverRenderers[brushOption.brushType].contain( + covers[i], localCursorPoint[0], localCursorPoint[1] + ) + ) { + // Use cursor style set on cover. + return; + } + } + } + + currPanel && 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, localCursorPoint, isEnd) { + var creatingCover = controller._creatingCover; + var panel = controller._creatingPanel; + var thisBrushOption = controller._brushOption; + var eventParams; + + controller._track.push(localCursorPoint.slice()); + + if (shouldShowCover(controller) || creatingCover) { + + if (panel && !creatingCover) { + thisBrushOption.brushMode === 'single' && clearCovers(controller); + var brushOption = clone(thisBrushOption); + brushOption.brushType = determineBrushType(brushOption.brushType, panel); + brushOption.panelId = panel === true ? null : panel.panelId; + creatingCover = controller._creatingCover = createCover(controller, brushOption); + controller._covers.push(creatingCover); + } + + if (creatingCover) { + var coverRenderer = coverRenderers[determineBrushType(controller._brushType, panel)]; + 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, e, localCursorPoint) && clearCovers(controller)) { + eventParams = {isEnd: isEnd, removeOnClick: true}; + } + } + + return eventParams; +} + +function determineBrushType(brushType, panel) { + if (brushType === 'auto') { + if (__DEV__) { + assert$1( + panel && panel.defaultBrushType, + 'MUST have defaultBrushType when brushType is "atuo"' + ); + } + return panel.defaultBrushType; + } + return brushType; +} + +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 localCursorPoint = this.group.transformCoordToLocal(e.offsetX, e.offsetY); + + this._creatingCover = null; + var panel = this._creatingPanel = getPanelByPoint(this, e, localCursorPoint); + + if (panel) { + this._dragging = true; + this._track = [localCursorPoint.slice()]; + } + } + }, + + mousemove: function (e) { + var localCursorPoint = this.group.transformCoordToLocal(e.offsetX, e.offsetY); + + resetCursor(this, e, localCursorPoint); + + if (this._dragging) { + + preventDefault(e); + + var eventParams = updateCoverByMouse(this, e, localCursorPoint, 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 localCursorPoint = this.group.transformCoordToLocal(e.offsetX, e.offsetY); + var eventParams = updateCoverByMouse(this, e, localCursorPoint, 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$2( + 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 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 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 Polygon({ + name: 'main', + draggable: true, + drift: curry$2(driftPolygon, controller, cover), + ondragend: curry$2(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$2( + 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$6(ends[0][xyIndex], ends[1][xyIndex]); + var max = mathMax$6(ends[0][xyIndex], ends[1][xyIndex]); + + return [min, max]; + }, + updateCoverShape: function (controller, cover, localRange, brushOption) { + var otherExtent; + // If brushWidth not specified, fit the panel. + var panel = getPanelByCover(controller, cover); + if (panel !== true && panel.getLinearBrushOtherExtent) { + otherExtent = panel.getLinearBrushOtherExtent( + xyIndex, controller._transform + ); + } + else { + var zr = controller._zr; + otherExtent = [0, [zr.getWidth(), zr.getHeight()][1 - xyIndex]]; + } + var rectRange = [localRange, otherExtent]; + xyIndex && rectRange.reverse(); + + updateBaseRect(controller, cover, rectRange, brushOption); + }, + updateCommon: updateCommon, + contain: mainShapeContain + }; +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +function makeRectPanelClipPath(rect) { + rect = normalizeRect(rect); + return function (localPoints, transform) { + return clipPointsByRect(localPoints, rect); + }; +} + +function makeLinearBrushOtherExtent(rect, specifiedXYIndex) { + rect = normalizeRect(rect); + return function (xyIndex) { + var idx = specifiedXYIndex != null ? specifiedXYIndex : xyIndex; + var brushWidth = idx ? rect.width : rect.height; + var base = idx ? rect.x : rect.y; + return [base, base + (brushWidth || 0)]; + }; +} + +function makeRectIsTargetByCursor(rect, api, targetModel) { + rect = normalizeRect(rect); + return function (e, localCursorPoint, transform) { + return rect.contain(localCursorPoint[0], localCursorPoint[1]) + && !onIrrelevantElement(e, api, targetModel); + }; +} + +// Consider width/height is negative. +function normalizeRect(rect) { + return BoundingRect.create(rect); +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var elementList = ['axisLine', 'axisTickLabel', 'axisName']; + +var AxisView$2 = extendComponentView({ + + type: 'parallelAxis', + + /** + * @override + */ + init: function (ecModel, api) { + AxisView$2.superApply(this, 'init', arguments); + + /** + * @type {module:echarts/component/helper/BrushController} + */ + (this._brushController = new BrushController(api.getZr())) + .on('brush', 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 Group(); + this.group.add(this._axisGroup); + + if (!axisModel.get('show')) { + return; + } + + var coordSysModel = getCoordSysModel(axisModel, ecModel); + var coordSys = coordSysModel.coordinateSystem; + + var areaSelectStyle = axisModel.getAreaSelectStyle(); + var areaWidth = areaSelectStyle.width; + + var dim = axisModel.axis.dim; + var axisLayout = coordSys.getAxisLayout(dim); + + var builderOpt = extend( + {strokeContainThreshold: areaWidth}, + axisLayout + ); + + var axisBuilder = new AxisBuilder(axisModel, builderOpt); + + each$1(elementList, axisBuilder.add, axisBuilder); + + this._axisGroup.add(axisBuilder.getGroup()); + + this._refreshBrushController( + builderOpt, areaSelectStyle, axisModel, coordSysModel, areaWidth, api + ); + + var animationModel = (payload && payload.animation === false) ? null : axisModel; + groupTransition(oldAxisGroup, this._axisGroup, animationModel); + }, + + // /** + // * @override + // */ + // updateVisual: function (axisModel, ecModel, api, payload) { + // this._brushController && this._brushController + // .updateCovers(getCoverInfoList(axisModel)); + // }, + + _refreshBrushController: function ( + builderOpt, areaSelectStyle, axisModel, coordSysModel, areaWidth, api + ) { + // After filtering, axis may change, select area needs to be update. + var extent = axisModel.axis.getExtent(); + var extentLen = extent[1] - extent[0]; + var extra = Math.min(30, Math.abs(extentLen) * 0.1); // Arbitrary value. + + // width/height might be negative, which will be + // normalized in BoundingRect. + var rect = BoundingRect.create({ + x: extent[0], + y: -areaWidth / 2, + width: extentLen, + height: areaWidth + }); + rect.x -= extra; + rect.width += 2 * extra; + + this._brushController + .mount({ + enableGlobalPan: true, + rotation: builderOpt.rotation, + position: builderOpt.position + }) + .setPanels([{ + panelId: 'pl', + clipPath: makeRectPanelClipPath(rect), + isTargetByCursor: makeRectIsTargetByCursor(rect, api, coordSysModel), + getLinearBrushOtherExtent: makeLinearBrushOtherExtent(rect, 0) + }]) + .enableBrush({ + brushType: 'lineX', + brushStyle: areaSelectStyle, + removeOnClick: true + }) + .updateCovers(getCoverInfoList(axisModel)); + }, + + _onBrush: function (coverInfoList, opt) { + // Do not cache these object, because the mey be changed. + var axisModel = this.axisModel; + var axis = axisModel.axis; + var intervals = 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; +} + +function getCoverInfoList(axisModel) { + var axis = axisModel.axis; + return map(axisModel.activeIntervals, function (interval) { + return { + brushType: 'lineX', + panelId: 'pl', + range: [ + axis.dataToCoord(interval[0], true), + axis.dataToCoord(interval[1], true) + ] + }; + }); +} + +function getCoordSysModel(axisModel, ecModel) { + return ecModel.getComponent( + 'parallel', axisModel.get('parallelIndex') + ); +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var CLICK_THRESHOLD = 5; // > 4 + +// Parallel view +extendComponentView({ + type: 'parallel', + + render: function (parallelModel, ecModel, api) { + this._model = parallelModel; + this._api = api; + + if (!this._handlers) { + this._handlers = {}; + each$1(handlers, function (handler, eventName) { + api.getZr().on(eventName, this._handlers[eventName] = bind(handler, this)); + }, this); + } + + createOrUpdate( + this, + '_throttledDispatchExpand', + parallelModel.get('axisExpandRate'), + 'fixRate' + ); + }, + + dispose: function (ecModel, api) { + each$1(this._handlers, function (handler, eventName) { + api.getZr().off(eventName, handler); + }); + this._handlers = null; + }, + + /** + * @param {Object} [opt] If null, cancle the last action triggering for debounce. + */ + _throttledDispatchExpand: function (opt) { + this._dispatchExpand(opt); + }, + + _dispatchExpand: function (opt) { + opt && this._api.dispatchAction( + extend({type: 'parallelAxisExpand'}, opt) + ); + } + +}); + +var handlers = { + + mousedown: function (e) { + if (checkTrigger(this, 'click')) { + this._mouseDownPoint = [e.offsetX, e.offsetY]; + } + }, + + mouseup: function (e) { + var mouseDownPoint = this._mouseDownPoint; + + if (checkTrigger(this, 'click') && mouseDownPoint) { + var point = [e.offsetX, e.offsetY]; + var dist = Math.pow(mouseDownPoint[0] - point[0], 2) + + Math.pow(mouseDownPoint[1] - point[1], 2); + + if (dist > CLICK_THRESHOLD) { + return; + } + + var result = this._model.coordinateSystem.getSlidedAxisExpandWindow( + [e.offsetX, e.offsetY] + ); + + result.behavior !== 'none' && this._dispatchExpand({ + axisExpandWindow: result.axisExpandWindow + }); + } + + this._mouseDownPoint = null; + }, + + mousemove: function (e) { + // Should do nothing when brushing. + if (this._mouseDownPoint || !checkTrigger(this, 'mousemove')) { + return; + } + var model = this._model; + var result = model.coordinateSystem.getSlidedAxisExpandWindow( + [e.offsetX, e.offsetY] + ); + + var behavior = result.behavior; + behavior === 'jump' && this._throttledDispatchExpand.debounceNextCall(model.get('axisExpandDebounce')); + this._throttledDispatchExpand( + behavior === 'none' + ? null // Cancle the last trigger, in case that mouse slide out of the area quickly. + : { + axisExpandWindow: result.axisExpandWindow, + // Jumping uses animation, and sliding suppresses animation. + animation: behavior === 'jump' ? null : false + } + ); + } +}; + +function checkTrigger(view, triggerOn) { + var model = view._model; + return model.get('axisExpandable') && model.get('axisExpandTriggerOn') === triggerOn; +} + +registerPreprocessor(parallelPreprocessor); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +SeriesModel.extend({ + + type: 'series.parallel', + + dependencies: ['parallel'], + + visualColorAccessPath: 'lineStyle.color', + + getInitialData: function (option, ecModel) { + var source = this.getSource(); + + setEncodeAndDimensions(source, this); + + return createListFromArray(source, this); + }, + + /** + * 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: { + show: false + }, + + inactiveOpacity: 0.05, + activeOpacity: 1, + + lineStyle: { + width: 1, + opacity: 0.45, + type: 'solid' + }, + emphasis: { + label: { + show: false + } + }, + + progressive: 500, + smooth: false, // true | false | number + + animationEasing: 'linear' + } +}); + +function setEncodeAndDimensions(source, seriesModel) { + // The mapping of parallelAxis dimension to data dimension can + // be specified in parallelAxis.option.dim. For example, if + // parallelAxis.option.dim is 'dim3', it mapping to the third + // dimension of data. But `data.encode` has higher priority. + // Moreover, parallelModel.dimension should not be regarded as data + // dimensions. Consider dimensions = ['dim4', 'dim2', 'dim6']; + + if (source.encodeDefine) { + return; + } + + var parallelModel = seriesModel.ecModel.getComponent( + 'parallel', seriesModel.get('parallelIndex') + ); + if (!parallelModel) { + return; + } + + var encodeDefine = source.encodeDefine = createHashMap(); + each$1(parallelModel.dimensions, function (axisDim) { + var dataDimIndex = convertDimNameToNumber(axisDim); + encodeDefine.set(axisDim, dataDimIndex); + }); +} + +function convertDimNameToNumber(dimName) { + return +dimName.replace('dim', ''); +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var DEFAULT_SMOOTH = 0.3; + +var ParallelView = Chart.extend({ + + type: 'parallel', + + init: function () { + + /** + * @type {module:zrender/container/Group} + * @private + */ + this._dataGroup = new Group(); + + this.group.add(this._dataGroup); + + /** + * @type {module:echarts/data/List} + */ + this._data; + + /** + * @type {boolean} + */ + this._initialized; + }, + + /** + * @override + */ + render: function (seriesModel, ecModel, api, payload) { + var dataGroup = this._dataGroup; + var data = seriesModel.getData(); + var oldData = this._data; + var coordSys = seriesModel.coordinateSystem; + var dimensions = coordSys.dimensions; + var seriesScope = makeSeriesScope$2(seriesModel); + + data.diff(oldData) + .add(add) + .update(update) + .remove(remove) + .execute(); + + function add(newDataIndex) { + var line = addEl(data, dataGroup, newDataIndex, dimensions, coordSys); + updateElCommon(line, data, newDataIndex, seriesScope); + } + + function update(newDataIndex, oldDataIndex) { + var line = oldData.getItemGraphicEl(oldDataIndex); + var points = createLinePoints(data, newDataIndex, dimensions, coordSys); + data.setItemGraphicEl(newDataIndex, line); + var animationModel = (payload && payload.animation === false) ? null : seriesModel; + updateProps(line, {shape: {points: points}}, animationModel, newDataIndex); + + updateElCommon(line, data, newDataIndex, seriesScope); + } + + function remove(oldDataIndex) { + var line = oldData.getItemGraphicEl(oldDataIndex); + dataGroup.remove(line); + } + + // First create + if (!this._initialized) { + this._initialized = true; + var clipPath = createGridClipShape$1( + coordSys, seriesModel, function () { + // Callback will be invoked immediately if there is no animation + setTimeout(function () { + dataGroup.removeClipPath(); + }); + } + ); + dataGroup.setClipPath(clipPath); + } + + this._data = data; + }, + + incrementalPrepareRender: function (seriesModel, ecModel, api) { + this._initialized = true; + this._data = null; + this._dataGroup.removeAll(); + }, + + incrementalRender: function (taskParams, seriesModel, ecModel) { + var data = seriesModel.getData(); + var coordSys = seriesModel.coordinateSystem; + var dimensions = coordSys.dimensions; + var seriesScope = makeSeriesScope$2(seriesModel); + + for (var dataIndex = taskParams.start; dataIndex < taskParams.end; dataIndex++) { + var line = addEl(data, this._dataGroup, dataIndex, dimensions, coordSys); + line.incremental = true; + updateElCommon(line, data, dataIndex, seriesScope); + } + }, + + dispose: function () {}, + + // _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$1(coordSys, seriesModel, cb) { + var parallelModel = coordSys.model; + var rect = coordSys.getRect(); + var rectEl = new 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); + 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(data.mapDimension(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 Polyline({ + shape: {points: points}, + silent: true, + z2: 10 + }); + dataGroup.add(line); + data.setItemGraphicEl(dataIndex, line); + return line; +} + +function makeSeriesScope$2(seriesModel) { + var smooth = seriesModel.get('smooth', true); + smooth === true && (smooth = DEFAULT_SMOOTH); + return { + lineStyle: seriesModel.getModel('lineStyle').getLineStyle(), + smooth: smooth != null ? smooth : DEFAULT_SMOOTH + }; +} + +function updateElCommon(el, data, dataIndex, seriesScope) { + var lineStyle = seriesScope.lineStyle; + + if (data.hasItemOption) { + var lineStyleModel = data.getItemModel(dataIndex).getModel('lineStyle'); + lineStyle = lineStyleModel.getLineStyle(); + } + + el.useStyle(lineStyle); + + var elStyle = el.style; + elStyle.fill = null; + // lineStyle.color have been set to itemVisual in module:echarts/visual/seriesColor. + elStyle.stroke = data.getItemVisual(dataIndex, 'color'); + // lineStyle.opacity have been set to itemVisual in parallelVisual. + elStyle.opacity = data.getItemVisual(dataIndex, 'opacity'); + + seriesScope.smooth && (el.shape.smooth = seriesScope.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' +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + + +var opacityAccessPath$1 = ['lineStyle', 'normal', 'opacity']; + +var parallelVisual = { + + seriesType: 'parallel', + + reset: function (seriesModel, ecModel, api) { + + var itemStyleModel = seriesModel.getModel('itemStyle'); + var lineStyleModel = seriesModel.getModel('lineStyle'); + 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').getLineStyle(); + + var coordSys = seriesModel.coordinateSystem; + var data = seriesModel.getData(); + + var opacityMap = { + normal: lineStyle.opacity, + active: activeOpacity, + inactive: inactiveOpacity + }; + + data.setVisual('color', color); + + function progress(params, data) { + coordSys.eachActiveState(data, function (activeState, dataIndex) { + var opacity = opacityMap[activeState]; + if (activeState === 'normal' && data.hasItemOption) { + var itemOpacity = data.getItemModel(dataIndex).get(opacityAccessPath$1, true); + itemOpacity != null && (opacity = itemOpacity); + } + data.setItemVisual(dataIndex, 'opacity', opacity); + }, params.start, params.end); + } + + return {progress: progress}; + } +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +registerVisual(parallelVisual); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * @file Get initial data and define sankey view's series model + * @author Deqing Li(annong035@gmail.com) + */ + +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; + } + }, + + setNodePosition: function (dataIndex, localPosition) { + var dataItem = this.option.data[dataIndex]; + dataItem.localX = localPosition[0]; + dataItem.localY = localPosition[1]; + }, + + /** + * 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 encodeHTML(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, + + // control if the node can move or not + draggable: true, + + // the number of iterations to change the position of the node + layoutIterations: 32, + + label: { + show: true, + position: 'right', + color: '#000', + fontSize: 12 + }, + + itemStyle: { + borderWidth: 1, + borderColor: '#333' + }, + + lineStyle: { + color: '#314656', + opacity: 0.2, + curveness: 0.5 + }, + + emphasis: { + label: { + show: true + }, + lineStyle: { + opacity: 0.6 + } + }, + + animationEasing: 'linear', + + animationDuration: 1000 + } + +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * @file The file used to draw sankey view + * @author Deqing Li(annong035@gmail.com) + */ + +var SankeyShape = 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(); + } +}); + +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; + // view width + var width = layoutInfo.width; + // view height + var height = layoutInfo.height; + + var nodeData = seriesModel.getData(); + var edgeData = seriesModel.getData('edge'); + + this._model = seriesModel; + + group.removeAll(); + + group.attr('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'); + var curvature = lineStyleModel.get('curveness'); + var n1Layout = edge.node1.getLayout(); + var node1Model =edge.node1.getModel(); + var dragX1 = node1Model.get('localX'); + var dragY1 = node1Model.get('localY'); + var n2Layout = edge.node2.getLayout(); + var node2Model = edge.node2.getModel(); + var dragX2 = node2Model.get('localX'); + var dragY2 = node2Model.get('localY'); + var edgeLayout = edge.getLayout(); + + curve.shape.extent = Math.max(1, edgeLayout.dy); + + var x1 = (dragX1 != null ? dragX1 * width : n1Layout.x) + n1Layout.dx; + var y1 = (dragY1 != null ? dragY1 * height : n1Layout.y) + edgeLayout.sy + edgeLayout.dy / 2; + var x2 = dragX2 != null ? dragX2 * width : n2Layout.x; + var y2 = (dragY2 != null ? dragY2 * height : 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; + } + + setHoverStyle(curve, edge.getModel('emphasis.lineStyle').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 dragX = itemModel.get('localX'); + var dragY = itemModel.get('localY'); + var labelModel = itemModel.getModel('label'); + var labelHoverModel = itemModel.getModel('emphasis.label'); + + var rect = new Rect({ + shape: { + x: dragX != null ? dragX * width : layout.x, + y: dragY != null ? dragY * height : layout.y, + width: layout.dx, + height: layout.dy + }, + style: itemModel.getModel('itemStyle').getItemStyle() + }); + + var hoverStyle = node.getModel('emphasis.itemStyle').getItemStyle(); + + setLabelStyle( + rect.style, hoverStyle, labelModel, labelHoverModel, + { + labelFetcher: seriesModel, + labelDataIndex: node.dataIndex, + defaultText: node.id, + isRectText: true + } + ); + + rect.setStyle('fill', node.getVisual('color')); + + setHoverStyle(rect, hoverStyle); + + group.add(rect); + + nodeData.setItemGraphicEl(node.dataIndex, rect); + + rect.dataType = 'node'; + }); + + var draggable = seriesModel.get('draggable'); + if (draggable) { + nodeData.eachItemGraphicEl(function (el, dataIndex) { + el.drift = function (dx, dy) { + this.shape.x += dx; + this.shape.y += dy; + this.dirty(); + api.dispatchAction({ + type: 'dragNode', + seriesId: seriesModel.id, + dataIndex: nodeData.getRawIndex(dataIndex), + localX: this.shape.x / width, + localY: this.shape.y / height + }); + }; + + el.draggable = true; + el.cursor = 'move'; + }); + } + + if (!this._data && seriesModel.get('animation')) { + group.setClipPath(createGridClipShape$2(group.getBoundingRect(), seriesModel, function () { + group.removeClipPath(); + })); + } + + this._data = seriesModel.getData(); + }, + + dispose: function () {} +}); + +// add animation to the view +function createGridClipShape$2(rect, seriesModel, cb) { + var rectEl = new Rect({ + shape: { + x: rect.x - 10, + y: rect.y - 10, + width: 0, + height: rect.height + 20 + } + }); + initProps(rectEl, { + shape: { + width: rect.width + 20, + height: rect.height + 20 + } + }, seriesModel, cb); + + return rectEl; +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +registerAction({ + type: 'dragNode', + event: 'dragNode', + // here can only use 'update' now, other value is not support in echarts. + update: 'update' +}, function (payload, ecModel) { + ecModel.eachComponent({mainType: 'series', subType: 'sankey', query: payload}, function (seriesModel) { + seriesModel.setNodePosition(payload.dataIndex, [payload.localX, payload.localY]); + }); +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/* +* The implementation references to d3.js. The use of the source +* code of this file is also subject to the terms and consitions +* of its license (BSD-3Clause, see ). +*/ + + +/** + * 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$$1(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]]; + } + } + + each$1(valuesByKey, function (value, key) { + mapObject[key] = map$$1(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++]; + + each$1(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$$1(array, 0), 0); + } + }; +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * @file The layout algorithm of sankey view + * @author Deqing Li(annong035@gmail.com) + */ + +var sankeyLayout = function (ecModel, api, payload) { + + ecModel.eachSeriesByType('sankey', function (seriesModel) { + + var nodeWidth = seriesModel.get('nodeWidth'); + var nodeGap = seriesModel.get('nodeGap'); + + var layoutInfo = getViewRect$3(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 = filter(nodes, 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$3(seriesModel, api) { + return getLayoutRect( + seriesModel.getBoxLayoutParams(), { + width: api.getWidth(), + height: api.getHeight() + } + ); +} + +function layoutSankey(nodes, edges, nodeWidth, nodeGap, width, height, iterations) { + computeNodeBreadths(nodes, edges, 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) { + each$1(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. + * + * Here we use Kahn algorithm to detect cycle when we traverse + * the node to computer the initial x position. + * + * @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, edges, nodeWidth, width) { + // Used to mark whether the edge is deleted. if it is deleted, + // the value is 0, otherwise it is 1. + var remainEdges = []; + // Storage each node's indegree. + var indegreeArr = []; + //Used to storage the node with indegree is equal to 0. + var zeroIndegrees = []; + var nextNode = []; + var x = 0; + var kx = 0; + + for (var i = 0; i < edges.length; i++) { + remainEdges[i] = 1; + } + + for (var i = 0; i < nodes.length; i++) { + indegreeArr[i] = nodes[i].inEdges.length; + if (indegreeArr[i] === 0) { + zeroIndegrees.push(nodes[i]); + } + } + + while (zeroIndegrees.length) { + each$1(zeroIndegrees, function (node) { + node.setLayout({x: x}, true); + node.setLayout({dx: nodeWidth}, true); + each$1(node.outEdges, function (edge) { + var indexEdge = edges.indexOf(edge); + remainEdges[indexEdge] = 0; + var targetNode = edge.node2; + var nodeIndex = nodes.indexOf(targetNode); + if (--indegreeArr[nodeIndex] === 0) { + nextNode.push(targetNode); + } + }); + }); + + ++x; + zeroIndegrees = nextNode; + nextNode = []; + } + + for (var i = 0; i < remainEdges.length; i++) { + if (__DEV__) { + if (remainEdges[i] === 1) { + throw new Error('Sankey is a DAG, the original data has cycle!'); + } + } + } + + 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) { + each$1(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) { + each$1(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 {number} 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 = []; + each$1(nodesByBreadth, function (nodes) { + var n = nodes.length; + var sum = 0; + each$1(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]; + + each$1(nodesByBreadth, function (nodes) { + each$1(nodes, function (node, i) { + node.setLayout({y: i}, true); + var nodeDy = node.getLayout().value * ky0; + node.setLayout({dy: nodeDy}, true); + }); + }); + + each$1(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) { + each$1(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) { + each$1(nodesByBreadth.slice().reverse(), function (nodes) { + each$1(nodes, function (node) { + if (node.outEdges.length) { + var y = sum(node.outEdges, weightedTarget) / sum(node.outEdges, getEdgeValue); + var nodeY = node.getLayout().y + (y - center$1(node)) * alpha; + node.setLayout({y: nodeY}, true); + } + }); + }); +} + +function weightedTarget(edge) { + return center$1(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) { + each$1(nodesByBreadth, function (nodes) { + each$1(nodes, function (node) { + if (node.inEdges.length) { + var y = sum(node.inEdges, weightedSource) / sum(node.inEdges, getEdgeValue); + var nodeY = node.getLayout().y + (y - center$1(node)) * alpha; + node.setLayout({y: nodeY}, true); + } + }); + }); +} + +function weightedSource(edge) { + return center$1(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) { + each$1(nodes, function (node) { + node.outEdges.sort(ascendingTargetDepth); + node.inEdges.sort(ascendingSourceDepth); + }); + each$1(nodes, function (node) { + var sy = 0; + var ty = 0; + each$1(node.outEdges, function (edge) { + edge.setLayout({sy: sy}, true); + sy += edge.getLayout().dy; + }); + each$1(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$1(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; +} + +function getEdgeValue(edge) { + return edge.getValue(); +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * @file Visual encoding for sankey view + * @author Deqing Li(annong035@gmail.com) + */ + +var sankeyVisual = function (ecModel, payload) { + ecModel.eachSeriesByType('sankey', function (seriesModel) { + var graph = seriesModel.getGraph(); + var nodes = graph.nodes; + if (nodes.length) { + var minValue = Infinity; + var maxValue = -Infinity; + each$1(nodes, function (node) { + var nodeValue = node.getLayout().value; + if (nodeValue < minValue) { + minValue = nodeValue; + } + if (nodeValue > maxValue) { + maxValue = nodeValue; + } + }); + + each$1(nodes, 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.color'); + if (customColor != null) { + node.setVisual('color', customColor); + } + }); + } + }); +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +registerLayout(sankeyLayout); +registerVisual(sankeyVisual); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + + +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 ordinalMeta; + + 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'; + ordinalMeta = xAxisModel.getOrdinalMeta(); + addOrdinal = true; + } + else if (yAxisType === 'category') { + option.layout = 'vertical'; + ordinalMeta = yAxisModel.getOrdinalMeta(); + addOrdinal = true; + } + else { + option.layout = option.layout || 'horizontal'; + } + + var coordDims = ['x', 'y']; + var baseAxisDimIndex = option.layout === 'horizontal' ? 0 : 1; + var baseAxisDim = this._baseAxisDim = coordDims[baseAxisDimIndex]; + var otherAxisDim = coordDims[1 - baseAxisDimIndex]; + var axisModels = [xAxisModel, yAxisModel]; + var baseAxisType = axisModels[baseAxisDimIndex].get('type'); + var otherAxisType = axisModels[1 - baseAxisDimIndex].get('type'); + var data = option.data; + + // ??? FIXME make a stage to perform data transfrom. + // MUST create a new data, consider setOption({}) again. + if (data && addOrdinal) { + var newOptionData = []; + each$1(data, function (item, index) { + var newItem; + if (item.value && isArray(item.value)) { + newItem = item.value.slice(); + item.value.unshift(index); + } + else if (isArray(item)) { + newItem = item.slice(); + item.unshift(index); + } + else { + newItem = item; + } + newOptionData.push(newItem); + }); + option.data = newOptionData; + } + + var defaultValueDimensions = this.defaultValueDimensions; + + return createListSimply( + this, + { + coordDimensions: [{ + name: baseAxisDim, + type: getDimensionTypeByAxis(baseAxisType), + ordinalMeta: ordinalMeta, + otherDims: { + tooltip: false, + itemName: 0 + }, + dimsDef: ['base'] + }, { + name: otherAxisDim, + type: getDimensionTypeByAxis(otherAxisType), + dimsDef: defaultValueDimensions.slice() + }], + dimensionsCount: defaultValueDimensions.length + 1 + } + ); + }, + + /** + * 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; + } + +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +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 + */ + defaultValueDimensions: [ + {name: 'min', defaultTooltip: true}, + {name: 'Q1', defaultTooltip: true}, + {name: 'median', defaultTooltip: true}, + {name: 'Q3', defaultTooltip: true}, + {name: 'max', defaultTooltip: true} + ], + + /** + * @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: { + color: '#fff', + borderWidth: 1 + }, + + emphasis: { + itemStyle: { + borderWidth: 2, + shadowBlur: 5, + shadowOffsetX: 2, + shadowOffsetY: 2, + shadowColor: 'rgba(0,0,0,0.4)' + } + }, + + animationEasing: 'elasticOut', + animationDuration: 800 + } +}); + +mixin(BoxplotSeries, seriesModelMixin, true); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +// Update common properties +var NORMAL_ITEM_STYLE_PATH = ['itemStyle']; +var EMPHASIS_ITEM_STYLE_PATH = ['emphasis', 'itemStyle']; + +var BoxplotView = Chart.extend({ + + type: 'boxplot', + + render: function (seriesModel, ecModel, api) { + var data = seriesModel.getData(); + var group = this.group; + var oldData = this._data; + + // There is no old data only when first rendering or switching from + // stream mode to normal mode, where previous elements should be removed. + if (!this._data) { + group.removeAll(); + } + + var constDim = seriesModel.get('layout') === 'horizontal' ? 1 : 0; + + data.diff(oldData) + .add(function (newIdx) { + if (data.hasValue(newIdx)) { + var itemLayout = data.getItemLayout(newIdx); + var symbolEl = createNormalBox(itemLayout, data, newIdx, constDim, 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; + } + + var itemLayout = data.getItemLayout(newIdx); + if (!symbolEl) { + symbolEl = createNormalBox(itemLayout, data, newIdx, constDim); + } + else { + updateNormalBoxData(itemLayout, symbolEl, data, newIdx); + } + + group.add(symbolEl); + + data.setItemGraphicEl(newIdx, symbolEl); + }) + .remove(function (oldIdx) { + var el = oldData.getItemGraphicEl(oldIdx); + el && group.remove(el); + }) + .execute(); + + this._data = data; + }, + + remove: function (ecModel) { + var group = this.group; + var data = this._data; + this._data = null; + data && data.eachItemGraphicEl(function (el) { + el && group.remove(el); + }); + }, + + dispose: noop + +}); + + +var BoxPath = Path.extend({ + + type: 'boxplotBoxPath', + + shape: {}, + + buildPath: function (ctx, shape) { + var ends = shape.points; + + var i = 0; + ctx.moveTo(ends[i][0], ends[i][1]); + i++; + for (; i < 4; i++) { + ctx.lineTo(ends[i][0], ends[i][1]); + } + ctx.closePath(); + + for (; i < ends.length; i++) { + ctx.moveTo(ends[i][0], ends[i][1]); + i++; + ctx.lineTo(ends[i][0], ends[i][1]); + } + } +}); + + +function createNormalBox(itemLayout, data, dataIndex, constDim, isInit) { + var ends = itemLayout.ends; + + var el = new BoxPath({ + shape: { + points: isInit + ? transInit(ends, constDim, itemLayout) + : ends + } + }); + + updateNormalBoxData(itemLayout, el, data, dataIndex, isInit); + + return el; +} + +function updateNormalBoxData(itemLayout, el, data, dataIndex, isInit) { + var seriesModel = data.hostModel; + var updateMethod = graphic[isInit ? 'initProps' : 'updateProps']; + + updateMethod( + el, + {shape: {points: itemLayout.ends}}, + seriesModel, + dataIndex + ); + + var itemModel = data.getItemModel(dataIndex); + var normalItemStyleModel = itemModel.getModel(NORMAL_ITEM_STYLE_PATH); + var borderColor = data.getItemVisual(dataIndex, 'color'); + + // Exclude borderColor. + var itemStyle = normalItemStyleModel.getItemStyle(['borderColor']); + itemStyle.stroke = borderColor; + itemStyle.strokeNoScale = true; + el.useStyle(itemStyle); + + el.z2 = 100; + + var hoverStyle = itemModel.getModel(EMPHASIS_ITEM_STYLE_PATH).getItemStyle(); + setHoverStyle(el, hoverStyle); +} + +function transInit(points, dim, itemLayout) { + return map(points, function (point) { + point = point.slice(); + point[dim] = itemLayout.initBaseline; + return point; + }); +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + + +var borderColorQuery = ['itemStyle', 'borderColor']; + +var boxplotVisual = 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)} + ); + }); + } + }); + +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var each$13 = each$1; + +var boxplotLayout = function (ecModel) { + + var groupResult = groupSeriesByAxis(ecModel); + + each$13(groupResult, function (groupItem) { + var seriesModels = groupItem.seriesModels; + + if (!seriesModels.length) { + return; + } + + calculateBase(groupItem); + + each$13(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 = 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$13(seriesModels, function (seriesModel) { + maxDataCount = Math.max(maxDataCount, seriesModel.getData().count()); + }); + extent = baseAxis.getExtent(), + Math.abs(extent[1] - extent[0]) / maxDataCount; + } + + each$13(seriesModels, function (seriesModel) { + var boxWidthBound = seriesModel.get('boxWidth'); + if (!isArray(boxWidthBound)) { + boxWidthBound = [boxWidthBound, boxWidthBound]; + } + boundList.push([ + parsePercent$1(boxWidthBound[0], bandWidth) || 0, + parsePercent$1(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$13(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 halfWidth = boxWidth / 2; + var cDimIdx = seriesModel.get('layout') === 'horizontal' ? 0 : 1; + var vDimIdx = 1 - cDimIdx; + var coordDims = ['x', 'y']; + var cDim = data.mapDimension(coordDims[cDimIdx]); + var vDims = data.mapDimension(coordDims[vDimIdx], true); + + if (cDim == null || vDims.length < 5) { + return; + } + + for (var dataIndex = 0; dataIndex < data.count(); dataIndex++) { + var axisDimVal = data.get(cDim, dataIndex); + + var median = getPoint(axisDimVal, vDims[2], dataIndex); + var end1 = getPoint(axisDimVal, vDims[0], dataIndex); + var end2 = getPoint(axisDimVal, vDims[1], dataIndex); + var end4 = getPoint(axisDimVal, vDims[3], dataIndex); + var end5 = getPoint(axisDimVal, vDims[4], dataIndex); + + var ends = []; + addBodyEnd(ends, end2, 0); + addBodyEnd(ends, end4, 1); + + ends.push(end1, end2, end5, end4); + layEndLine(ends, end1); + layEndLine(ends, end5); + layEndLine(ends, median); + + data.setItemLayout(dataIndex, { + initBaseline: median[vDimIdx], + ends: ends + }); + } + + function getPoint(axisDimVal, dimIdx, dataIndex) { + var val = data.get(dimIdx, dataIndex); + var p = []; + p[cDimIdx] = axisDimVal; + p[vDimIdx] = val; + var point; + if (isNaN(axisDimVal) || isNaN(val)) { + point = [NaN, NaN]; + } + else { + point = coordSys.dataToPoint(p); + point[cDimIdx] += offset; + } + return point; + } + + function addBodyEnd(ends, point, start) { + var point1 = point.slice(); + var point2 = point.slice(); + point1[cDimIdx] += halfWidth; + point2[cDimIdx] -= halfWidth; + start + ? ends.push(point1, point2) + : ends.push(point2, point1); + } + + function layEndLine(ends, endCenter) { + var from = endCenter.slice(); + var to = endCenter.slice(); + from[cDimIdx] -= halfWidth; + to[cDimIdx] += halfWidth; + ends.push(from, to); + } +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +registerVisual(boxplotVisual); +registerLayout(boxplotLayout); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var CandlestickSeries = SeriesModel.extend({ + + type: 'series.candlestick', + + dependencies: ['xAxis', 'yAxis', 'grid'], + + /** + * @readOnly + */ + defaultValueDimensions: [ + {name: 'open', defaultTooltip: true}, + {name: 'close', defaultTooltip: true}, + {name: 'lowest', defaultTooltip: true}, + {name: 'highest', defaultTooltip: true} + ], + + /** + * @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: { + color: '#c23531', // 阳线 positive + color0: '#314656', // 阴线 negative '#c23531', '#314656' + borderWidth: 1, + // FIXME + // ec2中使用的是lineStyle.color 和 lineStyle.color0 + borderColor: '#c23531', + borderColor0: '#314656' + }, + + emphasis: { + itemStyle: { + borderWidth: 2 + } + }, + + barMaxWidth: null, + barMinWidth: null, + barWidth: null, + + large: true, + largeThreshold: 600, + + progressive: 3e3, + progressiveThreshold: 1e4, + progressiveChunkMode: 'mod', + + animationUpdate: false, + animationEasing: 'linear', + animationDuration: 300 + }, + + /** + * Get dimension for shadow in dataZoom + * @return {string} dimension name + */ + getShadowDim: function () { + return 'open'; + }, + + brushSelector: function (dataIndex, data, selectors) { + var itemLayout = data.getItemLayout(dataIndex); + return itemLayout && selectors.rect(itemLayout.brushRect); + } + +}); + +mixin(CandlestickSeries, seriesModelMixin, true); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var NORMAL_ITEM_STYLE_PATH$1 = ['itemStyle']; +var EMPHASIS_ITEM_STYLE_PATH$1 = ['emphasis', 'itemStyle']; +var SKIP_PROPS = ['color', 'color0', 'borderColor', 'borderColor0']; + + +var CandlestickView = Chart.extend({ + + type: 'candlestick', + + render: function (seriesModel, ecModel, api) { + this._updateDrawMode(seriesModel); + + this._isLargeDraw + ? this._renderLarge(seriesModel) + : this._renderNormal(seriesModel); + }, + + incrementalPrepareRender: function (seriesModel, ecModel, api) { + this._clear(); + this._updateDrawMode(seriesModel); + }, + + incrementalRender: function (params, seriesModel, ecModel, api) { + this._isLargeDraw + ? this._incrementalRenderLarge(params, seriesModel) + : this._incrementalRenderNormal(params, seriesModel); + }, + + _updateDrawMode: function (seriesModel) { + var isLargeDraw = seriesModel.pipelineContext.large; + if (this._isLargeDraw == null || isLargeDraw ^ this._isLargeDraw) { + this._isLargeDraw = isLargeDraw; + this._clear(); + } + }, + + _renderNormal: function (seriesModel) { + var data = seriesModel.getData(); + var oldData = this._data; + var group = this.group; + var isSimpleBox = data.getLayout('isSimpleBox'); + + // There is no old data only when first rendering or switching from + // stream mode to normal mode, where previous elements should be removed. + if (!this._data) { + group.removeAll(); + } + + data.diff(oldData) + .add(function (newIdx) { + if (data.hasValue(newIdx)) { + var el; + + var itemLayout = data.getItemLayout(newIdx); + el = createNormalBox$1(itemLayout, newIdx, true); + initProps(el, {shape: {points: itemLayout.ends}}, seriesModel, newIdx); + + setBoxCommon(el, data, newIdx, isSimpleBox); + + group.add(el); + data.setItemGraphicEl(newIdx, el); + } + }) + .update(function (newIdx, oldIdx) { + var el = oldData.getItemGraphicEl(oldIdx); + + // Empty data + if (!data.hasValue(newIdx)) { + group.remove(el); + return; + } + + var itemLayout = data.getItemLayout(newIdx); + if (!el) { + el = createNormalBox$1(itemLayout, newIdx); + } + else { + updateProps(el, {shape: {points: itemLayout.ends}}, seriesModel, newIdx); + } + + setBoxCommon(el, data, newIdx, isSimpleBox); + + group.add(el); + data.setItemGraphicEl(newIdx, el); + }) + .remove(function (oldIdx) { + var el = oldData.getItemGraphicEl(oldIdx); + el && group.remove(el); + }) + .execute(); + + this._data = data; + }, + + _renderLarge: function (seriesModel) { + this._clear(); + createLarge$1(seriesModel, this.group); + }, + + _incrementalRenderNormal: function (params, seriesModel) { + var data = seriesModel.getData(); + var isSimpleBox = data.getLayout('isSimpleBox'); + + var dataIndex; + while ((dataIndex = params.next()) != null) { + var el; + + var itemLayout = data.getItemLayout(dataIndex); + el = createNormalBox$1(itemLayout, dataIndex); + setBoxCommon(el, data, dataIndex, isSimpleBox); + + el.incremental = true; + this.group.add(el); + } + }, + + _incrementalRenderLarge: function (params, seriesModel) { + createLarge$1(seriesModel, this.group, true); + }, + + remove: function (ecModel) { + this._clear(); + }, + + _clear: function () { + this.group.removeAll(); + this._data = null; + }, + + dispose: noop + +}); + + +var NormalBoxPath = Path.extend({ + + type: 'normalCandlestickBox', + + shape: {}, + + buildPath: function (ctx, shape) { + var ends = shape.points; + + if (this.__simpleBox) { + ctx.moveTo(ends[4][0], ends[4][1]); + ctx.lineTo(ends[6][0], ends[6][1]); + } + else { + ctx.moveTo(ends[0][0], ends[0][1]); + ctx.lineTo(ends[1][0], ends[1][1]); + ctx.lineTo(ends[2][0], ends[2][1]); + ctx.lineTo(ends[3][0], ends[3][1]); + ctx.closePath(); + + ctx.moveTo(ends[4][0], ends[4][1]); + ctx.lineTo(ends[5][0], ends[5][1]); + ctx.moveTo(ends[6][0], ends[6][1]); + ctx.lineTo(ends[7][0], ends[7][1]); + } + } +}); + +function createNormalBox$1(itemLayout, dataIndex, isInit) { + var ends = itemLayout.ends; + return new NormalBoxPath({ + shape: { + points: isInit + ? transInit$1(ends, itemLayout) + : ends + }, + z2: 100 + }); +} + +function setBoxCommon(el, data, dataIndex, isSimpleBox) { + var itemModel = data.getItemModel(dataIndex); + var normalItemStyleModel = itemModel.getModel(NORMAL_ITEM_STYLE_PATH$1); + var color = data.getItemVisual(dataIndex, 'color'); + var borderColor = data.getItemVisual(dataIndex, 'borderColor') || color; + + // Color must be excluded. + // Because symbol provide setColor individually to set fill and stroke + var itemStyle = normalItemStyleModel.getItemStyle(SKIP_PROPS); + + el.useStyle(itemStyle); + el.style.strokeNoScale = true; + el.style.fill = color; + el.style.stroke = borderColor; + + el.__simpleBox = isSimpleBox; + + var hoverStyle = itemModel.getModel(EMPHASIS_ITEM_STYLE_PATH$1).getItemStyle(); + setHoverStyle(el, hoverStyle); +} + +function transInit$1(points, itemLayout) { + return map(points, function (point) { + point = point.slice(); + point[1] = itemLayout.initBaseline; + return point; + }); +} + + + +var LargeBoxPath = Path.extend({ + + type: 'largeCandlestickBox', + + shape: {}, + + buildPath: function (ctx, shape) { + // Drawing lines is more efficient than drawing + // a whole line or drawing rects. + var points = shape.points; + for (var i = 0; i < points.length;) { + if (this.__sign === points[i++]) { + var x = points[i++]; + ctx.moveTo(x, points[i++]); + ctx.lineTo(x, points[i++]); + } + else { + i += 3; + } + } + } +}); + +function createLarge$1(seriesModel, group, incremental) { + var data = seriesModel.getData(); + var largePoints = data.getLayout('largePoints'); + + var elP = new LargeBoxPath({ + shape: {points: largePoints}, + __sign: 1 + }); + group.add(elP); + var elN = new LargeBoxPath({ + shape: {points: largePoints}, + __sign: -1 + }); + group.add(elN); + + setLargeStyle$1(1, elP, seriesModel, data); + setLargeStyle$1(-1, elN, seriesModel, data); + + if (incremental) { + elP.incremental = true; + elN.incremental = true; + } +} + +function setLargeStyle$1(sign, el, seriesModel, data) { + var suffix = sign > 0 ? 'P' : 'N'; + var borderColor = data.getVisual('borderColor' + suffix) + || data.getVisual('color' + suffix); + + // Color must be excluded. + // Because symbol provide setColor individually to set fill and stroke + var itemStyle = seriesModel.getModel(NORMAL_ITEM_STYLE_PATH$1).getItemStyle(SKIP_PROPS); + + el.useStyle(itemStyle); + el.style.fill = null; + el.style.stroke = borderColor; + // No different + // el.style.lineWidth = .5; +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var preprocessor = function (option) { + if (!option || !isArray(option.series)) { + return; + } + + // Translate 'k' to 'candlestick'. + each$1(option.series, function (seriesItem) { + if (isObject$1(seriesItem) && seriesItem.type === 'k') { + seriesItem.type = 'candlestick'; + } + }); +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var positiveBorderColorQuery = ['itemStyle', 'borderColor']; +var negativeBorderColorQuery = ['itemStyle', 'borderColor0']; +var positiveColorQuery = ['itemStyle', 'color']; +var negativeColorQuery = ['itemStyle', 'color0']; + +var candlestickVisual = { + + seriesType: 'candlestick', + + plan: createRenderPlanner(), + + // For legend. + performRawSeries: true, + + reset: function (seriesModel, ecModel) { + + var data = seriesModel.getData(); + var isLargeRender = seriesModel.pipelineContext.large; + + data.setVisual({ + legendSymbol: 'roundRect', + colorP: getColor(1, seriesModel), + colorN: getColor(-1, seriesModel), + borderColorP: getBorderColor(1, seriesModel), + borderColorN: getBorderColor(-1, seriesModel) + }); + + // Only visible series has each data be visual encoded + if (ecModel.isSeriesFiltered(seriesModel)) { + return; + } + + return !isLargeRender && {progress: progress}; + + + function progress(params, data) { + var dataIndex; + while ((dataIndex = params.next()) != null) { + var itemModel = data.getItemModel(dataIndex); + var sign = data.getItemLayout(dataIndex).sign; + + data.setItemVisual( + dataIndex, + { + color: getColor(sign, itemModel), + borderColor: getBorderColor(sign, itemModel) + } + ); + } + } + + function getColor(sign, model) { + return model.get( + sign > 0 ? positiveColorQuery : negativeColorQuery + ); + } + + function getBorderColor(sign, model) { + return model.get( + sign > 0 ? positiveBorderColorQuery : negativeBorderColorQuery + ); + } + + } + +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var LargeArr$1 = typeof Float32Array !== 'undefined' ? Float32Array : Array; + +var candlestickLayout = { + + seriesType: 'candlestick', + + plan: createRenderPlanner(), + + reset: function (seriesModel) { + + var coordSys = seriesModel.coordinateSystem; + var data = seriesModel.getData(); + var candleWidth = calculateCandleWidth(seriesModel, data); + var cDimIdx = 0; + var vDimIdx = 1; + var coordDims = ['x', 'y']; + var cDim = data.mapDimension(coordDims[cDimIdx]); + var vDims = data.mapDimension(coordDims[vDimIdx], true); + var openDim = vDims[0]; + var closeDim = vDims[1]; + var lowestDim = vDims[2]; + var highestDim = vDims[3]; + + data.setLayout({ + candleWidth: candleWidth, + // The value is experimented visually. + isSimpleBox: candleWidth <= 1.3 + }); + + if (cDim == null || vDims.length < 4) { + return; + } + + return { + progress: seriesModel.pipelineContext.large + ? largeProgress : normalProgress + }; + + function normalProgress(params, data) { + var dataIndex; + while ((dataIndex = params.next()) != null) { + + var axisDimVal = data.get(cDim, dataIndex); + var openVal = data.get(openDim, dataIndex); + var closeVal = data.get(closeDim, dataIndex); + var lowestVal = data.get(lowestDim, dataIndex); + var highestVal = data.get(highestDim, dataIndex); + + var ocLow = Math.min(openVal, closeVal); + var ocHigh = Math.max(openVal, closeVal); + + var ocLowPoint = getPoint(ocLow, axisDimVal); + var ocHighPoint = getPoint(ocHigh, axisDimVal); + var lowestPoint = getPoint(lowestVal, axisDimVal); + var highestPoint = getPoint(highestVal, axisDimVal); + + var ends = []; + addBodyEnd(ends, ocHighPoint, 0); + addBodyEnd(ends, ocLowPoint, 1); + + ends.push( + subPixelOptimizePoint(highestPoint), + subPixelOptimizePoint(ocHighPoint), + subPixelOptimizePoint(lowestPoint), + subPixelOptimizePoint(ocLowPoint) + ); + + data.setItemLayout(dataIndex, { + sign: getSign(data, dataIndex, openVal, closeVal, closeDim), + initBaseline: openVal > closeVal + ? ocHighPoint[vDimIdx] : ocLowPoint[vDimIdx], // open point. + ends: ends, + brushRect: makeBrushRect(lowestVal, highestVal, axisDimVal) + }); + } + + function getPoint(val, axisDimVal) { + var p = []; + p[cDimIdx] = axisDimVal; + p[vDimIdx] = val; + return (isNaN(axisDimVal) || isNaN(val)) + ? [NaN, NaN] + : coordSys.dataToPoint(p); + } + + function addBodyEnd(ends, point, start) { + var point1 = point.slice(); + var point2 = point.slice(); + + point1[cDimIdx] = subPixelOptimize( + point1[cDimIdx] + candleWidth / 2, 1, false + ); + point2[cDimIdx] = subPixelOptimize( + point2[cDimIdx] - candleWidth / 2, 1, true + ); + + start + ? ends.push(point1, point2) + : ends.push(point2, point1); + } + + function makeBrushRect(lowestVal, highestVal, axisDimVal) { + var pmin = getPoint(lowestVal, axisDimVal); + var pmax = getPoint(highestVal, axisDimVal); + + pmin[cDimIdx] -= candleWidth / 2; + pmax[cDimIdx] -= candleWidth / 2; + + return { + x: pmin[0], + y: pmin[1], + width: vDimIdx ? candleWidth : pmax[0] - pmin[0], + height: vDimIdx ? pmax[1] - pmin[1] : candleWidth + }; + } + + function subPixelOptimizePoint(point) { + point[cDimIdx] = subPixelOptimize(point[cDimIdx], 1); + return point; + } + } + + function largeProgress(params, data) { + // Structure: [sign, x, yhigh, ylow, sign, x, yhigh, ylow, ...] + var points = new LargeArr$1(params.count * 5); + var offset = 0; + var point; + var tmpIn = []; + var tmpOut = []; + var dataIndex; + + while ((dataIndex = params.next()) != null) { + var axisDimVal = data.get(cDim, dataIndex); + var openVal = data.get(openDim, dataIndex); + var closeVal = data.get(closeDim, dataIndex); + var lowestVal = data.get(lowestDim, dataIndex); + var highestVal = data.get(highestDim, dataIndex); + + if (isNaN(axisDimVal) || isNaN(lowestVal) || isNaN(highestVal)) { + points[offset++] = NaN; + offset += 4; + continue; + } + + points[offset++] = getSign(data, dataIndex, openVal, closeVal, closeDim); + + tmpIn[cDimIdx] = axisDimVal; + + tmpIn[vDimIdx] = lowestVal; + point = coordSys.dataToPoint(tmpIn, null, tmpOut); + points[offset++] = point ? point[0] : NaN; + points[offset++] = point ? point[1] : NaN; + tmpIn[vDimIdx] = highestVal; + point = coordSys.dataToPoint(tmpIn, null, tmpOut); + points[offset++] = point ? point[1] : NaN; + } + + data.setLayout('largePoints', points); + } + } +}; + +function getSign(data, dataIndex, openVal, closeVal, closeDim) { + var sign; + if (openVal > closeVal) { + sign = -1; + } + else if (openVal < closeVal) { + sign = 1; + } + else { + sign = dataIndex > 0 + // If close === open, compare with close of last record + ? (data.get(closeDim, dataIndex - 1) <= closeVal ? 1 : -1) + // No record of previous, set to be positive + : 1; + } + + return sign; +} + +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() + ); + + var barMaxWidth = parsePercent$1( + retrieve2(seriesModel.get('barMaxWidth'), bandWidth), + bandWidth + ); + var barMinWidth = parsePercent$1( + retrieve2(seriesModel.get('barMinWidth'), 1), + bandWidth + ); + var barWidth = seriesModel.get('barWidth'); + + return barWidth != null + ? parsePercent$1(barWidth, bandWidth) + // Put max outer to ensure bar visible in spite of overlap. + : Math.max(Math.min(bandWidth / 2, barMaxWidth), barMinWidth); +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +registerPreprocessor(preprocessor); +registerVisual(candlestickVisual); +registerLayout(candlestickLayout); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +SeriesModel.extend({ + + type: 'series.effectScatter', + + dependencies: ['grid', 'polar'], + + getInitialData: function (option, ecModel) { + return createListFromArray(this.getSource(), this); + }, + + brushSelector: 'point', + + defaultOption: { + coordinateSystem: 'cartesian2d', + zlevel: 0, + z: 2, + legendHoverLink: true, + + effectType: 'ripple', + + progressive: 0, + + // 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: { + // opacity: 1 + // } + } + +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * Symbol with ripple effect + * @module echarts/chart/helper/EffectSymbol + */ + +var EFFECT_RIPPLE_NUMBER = 3; + +function normalizeSymbolSize$1(symbolSize) { + if (!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 SymbolClz$1(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 = createSymbol( + // symbolType, -0.5, -0.5, 1, 1, color + // ); + // If width/height are set too small (e.g., set to 1) on ios10 + // and macOS Sierra, a circle stroke become a rect, no matter what + // the scale is set. So we set width/height as 2. See #4136. + var ripplePath = createSymbol( + symbolType, -1, -1, 2, 2, color + ); + ripplePath.attr({ + style: { + strokeNoScale: true + }, + z2: 99, + silent: true, + scale: [0.5, 0.5] + }); + + var delay = -i / EFFECT_RIPPLE_NUMBER * effectCfg.period + effectCfg.effectOffset; + // TODO Configurable effectCfg.period + ripplePath.animate('', true) + .when(effectCfg.period, { + scale: [effectCfg.rippleScale / 2, effectCfg.rippleScale / 2] + }) + .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.length; 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$1(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] = parsePercent$1(symbolOffset[0], symbolSize[0]); + pos[1] = parsePercent$1(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.highlight(); + if (effectCfg.showEffectOn !== 'render') { + this.startEffectAnimation(effectCfg); + } + }; + var onNormal = function () { + symbol.downplay(); + 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(); +}; + +inherits(EffectSymbol, Group); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +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); + }, + + updateTransform: function (seriesModel, ecModel, api) { + var data = seriesModel.getData(); + + this.group.dirty(); + + var res = pointsLayout().reset(seriesModel); + if (res.progress) { + res.progress({ start: 0, end: data.count() }, data); + } + + this._symbolDraw.updateLayout(data); + }, + + _updateGroupTransform: function (seriesModel) { + var coordSys = seriesModel.coordinateSystem; + if (coordSys && coordSys.getRoamTransform) { + this.group.transform = clone$2(coordSys.getRoamTransform()); + this.group.decomposeTransform(); + } + }, + + remove: function (ecModel, api) { + this._symbolDraw && this._symbolDraw.remove(api); + }, + + dispose: function () {} +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +registerVisual(visualSymbol('effectScatter', 'circle')); +registerLayout(pointsLayout('effectScatter')); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var Uint32Arr = typeof Uint32Array === 'undefined' ? Array : Uint32Array; +var Float64Arr = typeof Float64Array === 'undefined' ? Array : Float64Array; + +function compatEc2(seriesOpt) { + var data = seriesOpt.data; + if (data && data[0] && data[0][0] && data[0][0].coord) { + if (__DEV__) { + console.warn('Lines data configuration has been changed to' + + ' { coords:[[1,2],[2,3]] }'); + } + seriesOpt.data = 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 mergeAll([target, itemOpt[0], itemOpt[1]]); + }); + } +} + +var LinesSeries = SeriesModel.extend({ + + type: 'series.lines', + + dependencies: ['grid', 'polar'], + + visualColorAccessPath: 'lineStyle.color', + + init: function (option) { + // The input data may be null/undefined. + option.data = option.data || []; + + // Not using preprocessor because mergeOption may not have series.type + compatEc2(option); + + var result = this._processFlatCoordsArray(option.data); + this._flatCoords = result.flatCoords; + this._flatCoordsOffset = result.flatCoordsOffset; + if (result.flatCoords) { + option.data = new Float32Array(result.count); + } + + LinesSeries.superApply(this, 'init', arguments); + }, + + mergeOption: function (option) { + // The input data may be null/undefined. + option.data = option.data || []; + + compatEc2(option); + + if (option.data) { + // Only update when have option data to merge. + var result = this._processFlatCoordsArray(option.data); + this._flatCoords = result.flatCoords; + this._flatCoordsOffset = result.flatCoordsOffset; + if (result.flatCoords) { + option.data = new Float32Array(result.count); + } + } + + LinesSeries.superApply(this, 'mergeOption', arguments); + }, + + appendData: function (params) { + var result = this._processFlatCoordsArray(params.data); + if (result.flatCoords) { + if (!this._flatCoords) { + this._flatCoords = result.flatCoords; + this._flatCoordsOffset = result.flatCoordsOffset; + } + else { + this._flatCoords = concatArray(this._flatCoords, result.flatCoords); + this._flatCoordsOffset = concatArray(this._flatCoordsOffset, result.flatCoordsOffset); + } + params.data = new Float32Array(result.count); + } + + this.getRawData().appendData(params.data); + }, + + _getCoordsFromItemModel: function (idx) { + var itemModel = this.getData().getItemModel(idx); + var coords = (itemModel.option instanceof Array) + ? itemModel.option : itemModel.getShallow('coords'); + + if (__DEV__) { + 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.'); + } + } + return coords; + }, + + getLineCoordsCount: function (idx) { + if (this._flatCoordsOffset) { + return this._flatCoordsOffset[idx * 2 + 1]; + } + else { + return this._getCoordsFromItemModel(idx).length; + } + }, + + getLineCoords: function (idx, out) { + if (this._flatCoordsOffset) { + var offset = this._flatCoordsOffset[idx * 2]; + var len = this._flatCoordsOffset[idx * 2 + 1]; + for (var i = 0; i < len; i++) { + out[i] = out[i] || []; + out[i][0] = this._flatCoords[offset + i * 2]; + out[i][1] = this._flatCoords[offset + i * 2 + 1]; + } + return len; + } + else { + var coords = this._getCoordsFromItemModel(idx); + for (var i = 0; i < coords.length; i++) { + out[i] = out[i] || []; + out[i][0] = coords[i][0]; + out[i][1] = coords[i][1]; + } + return coords.length; + } + }, + + _processFlatCoordsArray: function (data) { + var startOffset = 0; + if (this._flatCoords) { + startOffset = this._flatCoords.length; + } + // Stored as a typed array. In format + // Points Count(2) | x | y | x | y | Points Count(3) | x | y | x | y | x | y | + if (typeof data[0] === 'number') { + var len = data.length; + // Store offset and len of each segment + var coordsOffsetAndLenStorage = new Uint32Arr(len); + var coordsStorage = new Float64Arr(len); + var coordsCursor = 0; + var offsetCursor = 0; + var dataCount = 0; + for (var i = 0; i < len;) { + dataCount++; + var count = data[i++]; + // Offset + coordsOffsetAndLenStorage[offsetCursor++] = coordsCursor + startOffset; + // Len + coordsOffsetAndLenStorage[offsetCursor++] = count; + for (var k = 0; k < count; k++) { + var x = data[i++]; + var y = data[i++]; + coordsStorage[coordsCursor++] = x; + coordsStorage[coordsCursor++] = y; + + if (i > len) { + if (__DEV__) { + throw new Error('Invalid data format.'); + } + } + } + } + + return { + flatCoordsOffset: new Uint32Array(coordsOffsetAndLenStorage.buffer, 0, offsetCursor), + flatCoords: coordsStorage, + count: dataCount + }; + } + + return { + flatCoordsOffset: null, + flatCoords: null, + count: data.length + }; + }, + + getInitialData: function (option, ecModel) { + if (__DEV__) { + var CoordSys = CoordinateSystemManager.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 != null) { + 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'); + var html = []; + fromName != null && html.push(fromName); + toName != null && html.push(toName); + + return encodeHTML(html.join(' > ')); + }, + + preventIncremental: function () { + return !!this.get('effect.show'); + }, + + getProgressive: function () { + var progressive = this.option.progressive; + if (progressive == null) { + return this.option.large ? 1e4 : this.get('progressive'); + } + return progressive; + }, + + getProgressiveThreshold: function () { + var progressiveThreshold = this.option.progressiveThreshold; + if (progressiveThreshold == null) { + return this.option.large ? 2e4 : this.get('progressiveThreshold'); + } + return progressiveThreshold; + }, + + defaultOption: { + coordinateSystem: 'geo', + zlevel: 0, + z: 2, + legendHoverLink: true, + + hoverAnimation: true, + // Cartesian coordinate system + xAxisIndex: 0, + yAxisIndex: 0, + + symbol: ['none', 'none'], + symbolSize: [10, 10], + // 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.color + // color + }, + + large: false, + // Available when large is true + largeThreshold: 2000, + + // If lines are polyline + // polyline not support curveness, label, animation + polyline: false, + + label: { + show: false, + position: 'end' + // distance: 5, + // formatter: 标签文本格式器,同Tooltip.formatter,不支持异步回调 + }, + + lineStyle: { + opacity: 0.5 + } + } +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * Provide effect for line + * @module echarts/chart/helper/EffectLine + */ + +/** + * @constructor + * @extends {module:zrender/graphic/Group} + * @alias {module:echarts/chart/helper/Line} + */ +function EffectLine(lineData, idx, seriesScope) { + 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$1(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 (!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 = 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 = retrieve(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 (dist(symbol.__p1, symbol.__cp1) + + 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$$1 = quadraticAt; + var quadraticDerivativeAt$$1 = quadraticDerivativeAt; + pos[0] = quadraticAt$$1(p1[0], cp1[0], p2[0], t); + pos[1] = quadraticAt$$1(p1[1], cp1[1], p2[1], t); + + // Tangent + var tx = quadraticDerivativeAt$$1(p1[0], cp1[0], p2[0], t); + var ty = quadraticDerivativeAt$$1(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); +}; + +inherits(EffectLine, Group); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * @module echarts/chart/helper/Line + */ + +/** + * @constructor + * @extends {module:zrender/graphic/Group} + * @alias {module:echarts/chart/helper/Polyline} + */ +function Polyline$2(lineData, idx, seriesScope) { + Group.call(this); + + this._createPolyline(lineData, idx, seriesScope); +} + +var polylineProto = Polyline$2.prototype; + +polylineProto._createPolyline = function (lineData, idx, seriesScope) { + // var seriesModel = lineData.hostModel; + var points = lineData.getItemLayout(idx); + + var line = new 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) + } + }; + 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').getLineStyle(); + hoverLineStyle = itemModel.getModel('emphasis.lineStyle').getLineStyle(); + } + line.useStyle(defaults( + { + strokeNoScale: true, + fill: 'none', + stroke: visualColor + }, + lineStyle + )); + line.hoverStyle = hoverLineStyle; + + setHoverStyle(this); +}; + +polylineProto.updateLayout = function (lineData, idx) { + var polyline = this.childAt(0); + polyline.setShape('points', lineData.getItemLayout(idx)); +}; + +inherits(Polyline$2, Group); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * Provide effect for line + * @module echarts/chart/helper/EffectLine + */ + +/** + * @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$2(lineData, idx, seriesScope); +}; + +// Overwrite +effectPolylineProto.updateAnimationPoints = function (symbol, points) { + this._points = points; + var accLenArr = [0]; + var len$$1 = 0; + for (var i = 1; i < points.length; i++) { + var p1 = points[i - 1]; + var p2 = points[i]; + len$$1 += dist(p1, p2); + accLenArr.push(len$$1); + } + if (len$$1 === 0) { + return; + } + + for (var i = 0; i < accLenArr.length; i++) { + accLenArr[i] /= len$$1; + } + this._offsets = accLenArr; + this._length = len$$1; +}; + +// 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$$1 = 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 - 1); + for (frame = start; frame >= 0; frame--) { + if (offsets[frame] <= t) { + break; + } + } + // PENDING really need to do this ? + frame = Math.min(frame, len$$1 - 2); + } + else { + for (var frame = lastFrame; frame < len$$1; frame++) { + if (offsets[frame] > t) { + break; + } + } + frame = Math.min(frame - 1, len$$1 - 2); + } + + lerp( + symbol.position, points[frame], points[frame + 1], + (t - offsets[frame]) / (offsets[frame + 1] - offsets[frame]) + ); + + var tx = points[frame + 1][0] - points[frame][0]; + var ty = points[frame + 1][1] - points[frame][1]; + symbol.rotation = -Math.atan2(ty, tx) - Math.PI / 2; + + this._lastFrame = frame; + this._lastFramePercent = t; + + symbol.ignore = false; +}; + +inherits(EffectPolyline, EffectLine); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +// TODO Batch by color + +var LargeLineShape = extendShape({ + + shape: { + polyline: false, + curveness: 0, + segs: [] + }, + + buildPath: function (path, shape) { + var segs = shape.segs; + var curveness = shape.curveness; + + if (shape.polyline) { + for (var i = 0; i < segs.length;) { + var count = segs[i++]; + if (count > 0) { + path.moveTo(segs[i++], segs[i++]); + for (var k = 1; k < count; k++) { + path.lineTo(segs[i++], segs[i++]); + } + } + } + } + else { + for (var i = 0; i < segs.length;) { + var x0 = segs[i++]; + var y0 = segs[i++]; + var x1 = segs[i++]; + var y1 = segs[i++]; + path.moveTo(x0, y0); + if (curveness > 0) { + var x2 = (x0 + x1) / 2 - (y0 - y1) * curveness; + var y2 = (y0 + y1) / 2 - (x1 - x0) * curveness; + path.quadraticCurveTo(x2, y2, x1, y1); + } + else { + path.lineTo(x1, y1); + } + } + } + }, + + findDataIndex: function (x, y) { + + var shape = this.shape; + var segs = shape.segs; + var curveness = shape.curveness; + + if (shape.polyline) { + var dataIndex = 0; + for (var i = 0; i < segs.length;) { + var count = segs[i++]; + if (count > 0) { + var x0 = segs[i++]; + var y0 = segs[i++]; + for (var k = 1; k < count; k++) { + var x1 = segs[i++]; + var y1 = segs[i++]; + if (containStroke$1(x0, y0, x1, y1)) { + return dataIndex; + } + } + } + + dataIndex++; + } + } + else { + var dataIndex = 0; + for (var i = 0; i < segs.length;) { + var x0 = segs[i++]; + var y0 = segs[i++]; + var x1 = segs[i++]; + var y1 = segs[i++]; + if (curveness > 0) { + var x2 = (x0 + x1) / 2 - (y0 - y1) * curveness; + var y2 = (y0 + y1) / 2 - (x1 - x0) * curveness; + + if (containStroke$3(x0, y0, x2, y2, x1, y1)) { + return dataIndex; + } + } + else { + if (containStroke$1(x0, y0, x1, y1)) { + return dataIndex; + } + } + + dataIndex++; + } + } + + return -1; + } +}); + +function LargeLineDraw() { + this.group = new Group(); +} + +var largeLineProto = LargeLineDraw.prototype; + +largeLineProto.isPersistent = function () { + return !this._incremental; +}; + +/** + * Update symbols draw by new data + * @param {module:echarts/data/List} data + */ +largeLineProto.updateData = function (data) { + this.group.removeAll(); + + var lineEl = new LargeLineShape({ + rectHover: true, + cursor: 'default' + }); + lineEl.setShape({ + segs: data.getLayout('linesPoints') + }); + + this._setCommon(lineEl, data); + + // Add back + this.group.add(lineEl); + + this._incremental = null; +}; + +/** + * @override + */ +largeLineProto.incrementalPrepareUpdate = function (data) { + this.group.removeAll(); + + this._clearIncremental(); + + if (data.count() > 5e5) { + if (!this._incremental) { + this._incremental = new IncrementalDisplayble({ + silent: true + }); + } + this.group.add(this._incremental); + } + else { + this._incremental = null; + } +}; + +/** + * @override + */ +largeLineProto.incrementalUpdate = function (taskParams, data) { + var lineEl = new LargeLineShape(); + lineEl.setShape({ + segs: data.getLayout('linesPoints') + }); + + this._setCommon(lineEl, data, !!this._incremental); + + if (!this._incremental) { + lineEl.rectHover = true; + lineEl.cursor = 'default'; + lineEl.__startIndex = taskParams.start; + this.group.add(lineEl); + } + else { + this._incremental.addDisplayable(lineEl, true); + } +}; + +/** + * @override + */ +largeLineProto.remove = function () { + this._clearIncremental(); + this._incremental = null; + this.group.removeAll(); +}; + +largeLineProto._setCommon = function (lineEl, data, isIncremental) { + var hostModel = data.hostModel; + + lineEl.setShape({ + polyline: hostModel.get('polyline'), + curveness: hostModel.get('lineStyle.curveness') + }); + + lineEl.useStyle( + hostModel.getModel('lineStyle').getLineStyle() + ); + lineEl.style.strokeNoScale = true; + + var visualColor = data.getVisual('color'); + if (visualColor) { + lineEl.setStyle('stroke', visualColor); + } + lineEl.setStyle('fill'); + + if (!isIncremental) { + // Enable tooltip + // PENDING May have performance issue when path is extremely large + lineEl.seriesIndex = hostModel.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 + lineEl.__startIndex; + } + }); + } +}; + +largeLineProto._clearIncremental = function () { + var incremental = this._incremental; + if (incremental) { + incremental.clearDisplaybles(); + } +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var linesLayout = { + seriesType: 'lines', + + plan: createRenderPlanner(), + + reset: function (seriesModel) { + var coordSys = seriesModel.coordinateSystem; + var isPolyline = seriesModel.get('polyline'); + var isLarge = seriesModel.pipelineContext.large; + + function progress(params, lineData) { + var lineCoords = []; + if (isLarge) { + var points; + var segCount = params.end - params.start; + if (isPolyline) { + var totalCoordsCount = 0; + for (var i = params.start; i < params.end; i++) { + totalCoordsCount += seriesModel.getLineCoordsCount(i); + } + points = new Float32Array(segCount + totalCoordsCount * 2); + } + else { + points = new Float32Array(segCount * 4); + } + + var offset = 0; + var pt = []; + for (var i = params.start; i < params.end; i++) { + var len = seriesModel.getLineCoords(i, lineCoords); + if (isPolyline) { + points[offset++] = len; + } + for (var k = 0; k < len; k++) { + pt = coordSys.dataToPoint(lineCoords[k], false, pt); + points[offset++] = pt[0]; + points[offset++] = pt[1]; + } + } + + lineData.setLayout('linesPoints', points); + } + else { + for (var i = params.start; i < params.end; i++) { + var itemModel = lineData.getItemModel(i); + var len = seriesModel.getLineCoords(i, lineCoords); + + var pts = []; + if (isPolyline) { + for (var j = 0; j < len; j++) { + pts.push(coordSys.dataToPoint(lineCoords[j])); + } + } + else { + pts[0] = coordSys.dataToPoint(lineCoords[0]); + pts[1] = coordSys.dataToPoint(lineCoords[1]); + + var curveness = itemModel.get('lineStyle.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(i, pts); + } + } + } + + return { progress: progress }; + } +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +extendChartView({ + + type: 'lines', + + init: function () {}, + + render: function (seriesModel, ecModel, api) { + var data = seriesModel.getData(); + + var lineDraw = this._updateLineDraw(data, seriesModel); + + var zlevel = seriesModel.get('zlevel'); + var trailLength = seriesModel.get('effect.trailLength'); + + var zr = api.getZr(); + // Avoid the drag cause ghost shadow + // FIXME Better way ? + // SVG doesn't support + var isSvg = zr.painter.getType() === 'svg'; + if (!isSvg) { + zr.painter.getLayer(zlevel).clear(true); + } + // Config layer with motion blur + if (this._lastZlevel != null && !isSvg) { + zr.configLayer(this._lastZlevel, { + motionBlur: false + }); + } + if (this._showEffect(seriesModel) && trailLength) { + if (__DEV__) { + 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'); + } + + if (!isSvg) { + zr.configLayer(zlevel, { + motionBlur: true, + lastFrameAlpha: Math.max(Math.min(trailLength / 10 + 0.9, 1), 0) + }); + } + } + + lineDraw.updateData(data); + + this._lastZlevel = zlevel; + + this._finished = true; + }, + + incrementalPrepareRender: function (seriesModel, ecModel, api) { + var data = seriesModel.getData(); + + var lineDraw = this._updateLineDraw(data, seriesModel); + + lineDraw.incrementalPrepareUpdate(data); + + this._clearLayer(api); + + this._finished = false; + }, + + incrementalRender: function (taskParams, seriesModel, ecModel) { + this._lineDraw.incrementalUpdate(taskParams, seriesModel.getData()); + + this._finished = taskParams.end === seriesModel.getData().count(); + }, + + updateTransform: function (seriesModel, ecModel, api) { + var data = seriesModel.getData(); + var pipelineContext = seriesModel.pipelineContext; + + if (!this._finished || pipelineContext.large || pipelineContext.progressiveRender) { + // TODO Don't have to do update in large mode. Only do it when there are millions of data. + return { + update: true + }; + } + else { + // TODO Use same logic with ScatterView. + // Manually update layout + var res = linesLayout.reset(seriesModel); + if (res.progress) { + res.progress({ start: 0, end: data.count() }, data); + } + this._lineDraw.updateLayout(); + this._clearLayer(api); + } + }, + + _updateLineDraw: function (data, seriesModel) { + var lineDraw = this._lineDraw; + var hasEffect = this._showEffect(seriesModel); + var isPolyline = !!seriesModel.get('polyline'); + var pipelineContext = seriesModel.pipelineContext; + var isLargeDraw = pipelineContext.large; + + if (__DEV__) { + if (hasEffect && isLargeDraw) { + console.warn('Large lines not support effect'); + } + } + if (!lineDraw + || hasEffect !== this._hasEffet + || isPolyline !== this._isPolyline + || isLargeDraw !== this._isLargeDraw + ) { + if (lineDraw) { + lineDraw.remove(); + } + lineDraw = this._lineDraw = isLargeDraw + ? new LargeLineDraw() + : new LineDraw( + isPolyline + ? (hasEffect ? EffectPolyline : Polyline$2) + : (hasEffect ? EffectLine : Line$1) + ); + this._hasEffet = hasEffect; + this._isPolyline = isPolyline; + this._isLargeDraw = isLargeDraw; + this.group.removeAll(); + } + + this.group.add(lineDraw.group); + + return lineDraw; + }, + + _showEffect: function (seriesModel) { + return !!seriesModel.get('effect.show'); + }, + + _clearLayer: function (api) { + // Not use motion when dragging or zooming + var zr = api.getZr(); + var isSvg = zr.painter.getType() === 'svg'; + if (!isSvg && this._lastZlevel != null) { + zr.painter.getLayer(this._lastZlevel).clear(true); + } + }, + + remove: function (ecModel, api) { + this._lineDraw && this._lineDraw.remove(); + this._lineDraw = null; + // Clear motion when lineDraw is removed + this._clearLayer(api); + }, + + dispose: function () {} +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + + +function normalize$2(a) { + if (!(a instanceof Array)) { + a = [a, a]; + } + return a; +} + +var opacityQuery = 'lineStyle.opacity'.split('.'); + +var linesVisual = { + seriesType: 'lines', + reset: function (seriesModel, ecModel, api) { + var symbolType = normalize$2(seriesModel.get('symbol')); + var symbolSize = normalize$2(seriesModel.get('symbolSize')); + var data = seriesModel.getData(); + + data.setVisual('fromSymbol', symbolType && symbolType[0]); + data.setVisual('toSymbol', symbolType && symbolType[1]); + data.setVisual('fromSymbolSize', symbolSize && symbolSize[0]); + data.setVisual('toSymbolSize', symbolSize && symbolSize[1]); + data.setVisual('opacity', seriesModel.get(opacityQuery)); + + function dataEach(data, idx) { + var itemModel = data.getItemModel(idx); + var symbolType = normalize$2(itemModel.getShallow('symbol', true)); + var symbolSize = normalize$2(itemModel.getShallow('symbolSize', true)); + var opacity = itemModel.get(opacityQuery); + + symbolType[0] && data.setItemVisual(idx, 'fromSymbol', symbolType[0]); + symbolType[1] && data.setItemVisual(idx, 'toSymbol', symbolType[1]); + symbolSize[0] && data.setItemVisual(idx, 'fromSymbolSize', symbolSize[0]); + symbolSize[1] && data.setItemVisual(idx, 'toSymbolSize', symbolSize[1]); + + data.setItemVisual(idx, 'opacity', opacity); + } + + return {dataEach: data.hasItemOption ? dataEach : null}; + } +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +registerLayout(linesLayout); +registerVisual(linesVisual); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +SeriesModel.extend({ + type: 'series.heatmap', + + getInitialData: function (option, ecModel) { + return createListFromArray(this.getSource(), this, { + generateCoord: 'value' + }); + }, + + preventIncremental: function () { + var coordSysCreator = CoordinateSystemManager.get(this.get('coordinateSystem')); + if (coordSysCreator && coordSysCreator.dimensions) { + return coordSysCreator.dimensions[0] === 'lng' && coordSysCreator.dimensions[1] === 'lat'; + } + }, + + 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 + } +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * @file defines echarts Heatmap Chart + * @author Ovilia (me@zhangwenli.com) + * Inspired by https://github.com/mourner/simpleheat + * + * @module + */ + +var GRADIENT_LEVELS = 256; + +/** + * Heatmap Chart + * + * @class + */ +function Heatmap() { + var canvas = 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); + } + + if (!canvas.width || !canvas.height) { + // Avoid "Uncaught DOMException: Failed to execute 'getImageData' on + // 'CanvasRenderingContext2D': The source height is 0." + return canvas; + } + + // 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 = 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 = [0, 0, 0, 0]; + 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; + } +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +function getIsInPiecewiseRange(dataExtent, pieceList, selected) { + var dataSpan = dataExtent[1] - dataExtent[0]; + pieceList = 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'; +} + +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 (__DEV__) { + if (!visualMapOfThisSeries) { + throw new Error('Heatmap must use with visualMap'); + } + } + + this.group.removeAll(); + + this._incrementalDisplayable = null; + + var coordSys = seriesModel.coordinateSystem; + if (coordSys.type === 'cartesian2d' || coordSys.type === 'calendar') { + this._renderOnCartesianAndCalendar(seriesModel, api, 0, seriesModel.getData().count()); + } + else if (isGeoCoordSys(coordSys)) { + this._renderOnGeo( + coordSys, seriesModel, visualMapOfThisSeries, api + ); + } + }, + + incrementalPrepareRender: function (seriesModel, ecModel, api) { + this.group.removeAll(); + }, + + incrementalRender: function (params, seriesModel, ecModel, api) { + var coordSys = seriesModel.coordinateSystem; + if (coordSys) { + this._renderOnCartesianAndCalendar(seriesModel, api, params.start, params.end, true); + } + }, + + _renderOnCartesianAndCalendar: function (seriesModel, api, start, end, incremental) { + + var coordSys = seriesModel.coordinateSystem; + var width; + var height; + + if (coordSys.type === 'cartesian2d') { + var xAxis = coordSys.getAxis('x'); + var yAxis = coordSys.getAxis('y'); + + if (__DEV__) { + 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'); + } + } + + width = xAxis.getBandWidth(); + height = yAxis.getBandWidth(); + } + + var group = this.group; + var data = seriesModel.getData(); + + var itemStyleQuery = 'itemStyle'; + var hoverItemStyleQuery = 'emphasis.itemStyle'; + var labelQuery = 'label'; + var hoverLabelQuery = 'emphasis.label'; + var style = seriesModel.getModel(itemStyleQuery).getItemStyle(['color']); + var hoverStl = seriesModel.getModel(hoverItemStyleQuery).getItemStyle(); + var labelModel = seriesModel.getModel(labelQuery); + var hoverLabelModel = seriesModel.getModel(hoverLabelQuery); + var coordSysType = coordSys.type; + + + var dataDims = coordSysType === 'cartesian2d' + ? [ + data.mapDimension('x'), + data.mapDimension('y'), + data.mapDimension('value') + ] + : [ + data.mapDimension('time'), + data.mapDimension('value') + ]; + + for (var idx = start; idx < end; idx++) { + var rect; + + if (coordSysType === 'cartesian2d') { + // Ignore empty data + if (isNaN(data.get(dataDims[2], idx))) { + continue; + } + + var point = coordSys.dataToPoint([ + data.get(dataDims[0], idx), + data.get(dataDims[1], idx) + ]); + + rect = new 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') + } + }); + } + else { + // Ignore empty data + if (isNaN(data.get(dataDims[1], idx))) { + continue; + } + + rect = new Rect({ + z2: 1, + shape: coordSys.dataToRect([data.get(dataDims[0], idx)]).contentShape, + style: { + fill: data.getItemVisual(idx, 'color'), + opacity: data.getItemVisual(idx, 'opacity') + } + }); + } + + var itemModel = data.getItemModel(idx); + + // 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]; + } + + setLabelStyle( + style, hoverStl, labelModel, hoverLabelModel, + { + labelFetcher: seriesModel, + labelDataIndex: idx, + defaultText: defaultText, + isRectText: true + } + ); + + rect.setStyle(style); + setHoverStyle(rect, data.hasItemOption ? hoverStl : extend({}, hoverStl)); + + rect.incremental = incremental; + // PENDING + if (incremental) { + // Rect must use hover layer if it's incremental. + rect.useHoverLayer = true; + } + + 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 Heatmap()); + 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(); + 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 dims = [ + data.mapDimension('lng'), + data.mapDimension('lat'), + data.mapDimension('value') + ]; + + var points = data.mapArray(dims, 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 ZImage({ + style: { + width: width, + height: height, + x: x, + y: y, + image: hmLayer.canvas + }, + silent: true + }); + this.group.add(img); + }, + + dispose: function () {} +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var PictorialBarSeries = BaseBarSeries.extend({ + + type: 'series.pictorialBar', + + dependencies: ['grid'], + + defaultOption: { + symbol: 'circle', // Customized bar shape + symbolSize: null, // Can be ['100%', '100%'], null means auto. + symbolRotate: null, + + symbolPosition: null, // 'start' or 'end' or 'center', null means auto. + symbolOffset: null, + symbolMargin: null, // start margin and end margin. Can be a number or a percent string. + // Auto margin by defualt. + symbolRepeat: false, // false/null/undefined, means no repeat. + // Can be true, means auto calculate repeat times and cut by data. + // Can be a number, specifies repeat times, and do not cut by data. + // Can be 'fixed', means auto calculate repeat times but do not cut by data. + symbolRepeatDirection: 'end', // 'end' means from 'start' to 'end'. + + symbolClip: false, + symbolBoundingData: null, // Can be 60 or -40 or [-40, 60] + symbolPatternSize: 400, // 400 * 400 px + + barGap: '-100%', // In most case, overlap is needed. + + // z can be set in data item, which is z2 actually. + + // Disable progressive + progressive: 0, + hoverAnimation: false // Open only when needed. + }, + + getInitialData: function (option) { + // Disable stack. + option.stack = null; + return PictorialBarSeries.superApply(this, 'getInitialData', arguments); + } +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var BAR_BORDER_WIDTH_QUERY$1 = ['itemStyle', 'borderWidth']; + +// index: +isHorizontal +var LAYOUT_ATTRS = [ + {xy: 'x', wh: 'width', index: 0, posDesc: ['left', 'right']}, + {xy: 'y', wh: 'height', index: 1, posDesc: ['top', 'bottom']} +]; + +var pathForLineWidth = new Circle(); + +var BarView$1 = extendChartView({ + + type: 'pictorialBar', + + render: 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 coordSysRect = cartesian.grid.getRect(); + + var opt = { + ecSize: {width: api.getWidth(), height: api.getHeight()}, + seriesModel: seriesModel, + coordSys: cartesian, + coordSysExtent: [ + [coordSysRect.x, coordSysRect.x + coordSysRect.width], + [coordSysRect.y, coordSysRect.y + coordSysRect.height] + ], + isHorizontal: isHorizontal, + valueDim: LAYOUT_ATTRS[+isHorizontal], + categoryDim: LAYOUT_ATTRS[1 - isHorizontal] + }; + + data.diff(oldData) + .add(function (dataIndex) { + if (!data.hasValue(dataIndex)) { + return; + } + + var itemModel = getItemModel(data, dataIndex); + var symbolMeta = getSymbolMeta(data, dataIndex, itemModel, opt); + + var bar = createBar(data, opt, symbolMeta); + + data.setItemGraphicEl(dataIndex, bar); + group.add(bar); + + updateCommon$1(bar, opt, symbolMeta); + }) + .update(function (newIndex, oldIndex) { + var bar = oldData.getItemGraphicEl(oldIndex); + + if (!data.hasValue(newIndex)) { + group.remove(bar); + return; + } + + var itemModel = getItemModel(data, newIndex); + var symbolMeta = getSymbolMeta(data, newIndex, itemModel, opt); + + var pictorialShapeStr = getShapeStr(data, symbolMeta); + if (bar && pictorialShapeStr !== bar.__pictorialShapeStr) { + group.remove(bar); + data.setItemGraphicEl(newIndex, null); + bar = null; + } + + if (bar) { + updateBar(bar, opt, symbolMeta); + } + else { + bar = createBar(data, opt, symbolMeta, true); + } + + data.setItemGraphicEl(newIndex, bar); + bar.__pictorialSymbolMeta = symbolMeta; + // Add back + group.add(bar); + + updateCommon$1(bar, opt, symbolMeta); + }) + .remove(function (dataIndex) { + var bar = oldData.getItemGraphicEl(dataIndex); + bar && removeBar(oldData, dataIndex, bar.__pictorialSymbolMeta.animationModel, bar); + }) + .execute(); + + this._data = data; + + return this.group; + }, + + dispose: noop, + + remove: function (ecModel, api) { + var group = this.group; + var data = this._data; + if (ecModel.get('animation')) { + if (data) { + data.eachItemGraphicEl(function (bar) { + removeBar(data, bar.dataIndex, ecModel, bar); + }); + } + } + else { + group.removeAll(); + } + } +}); + + +// Set or calculate default value about symbol, and calculate layout info. +function getSymbolMeta(data, dataIndex, itemModel, opt) { + var layout = data.getItemLayout(dataIndex); + var symbolRepeat = itemModel.get('symbolRepeat'); + var symbolClip = itemModel.get('symbolClip'); + var symbolPosition = itemModel.get('symbolPosition') || 'start'; + var symbolRotate = itemModel.get('symbolRotate'); + var rotation = (symbolRotate || 0) * Math.PI / 180 || 0; + var symbolPatternSize = itemModel.get('symbolPatternSize') || 2; + var isAnimationEnabled = itemModel.isAnimationEnabled(); + + var symbolMeta = { + dataIndex: dataIndex, + layout: layout, + itemModel: itemModel, + symbolType: data.getItemVisual(dataIndex, 'symbol') || 'circle', + color: data.getItemVisual(dataIndex, 'color'), + symbolClip: symbolClip, + symbolRepeat: symbolRepeat, + symbolRepeatDirection: itemModel.get('symbolRepeatDirection'), + symbolPatternSize: symbolPatternSize, + rotation: rotation, + animationModel: isAnimationEnabled ? itemModel : null, + hoverAnimation: isAnimationEnabled && itemModel.get('hoverAnimation'), + z2: itemModel.getShallow('z', true) || 0 + }; + + prepareBarLength(itemModel, symbolRepeat, layout, opt, symbolMeta); + + prepareSymbolSize( + data, dataIndex, layout, symbolRepeat, symbolClip, symbolMeta.boundingLength, + symbolMeta.pxSign, symbolPatternSize, opt, symbolMeta + ); + + prepareLineWidth(itemModel, symbolMeta.symbolScale, rotation, opt, symbolMeta); + + var symbolSize = symbolMeta.symbolSize; + var symbolOffset = itemModel.get('symbolOffset'); + if (isArray(symbolOffset)) { + symbolOffset = [ + parsePercent$1(symbolOffset[0], symbolSize[0]), + parsePercent$1(symbolOffset[1], symbolSize[1]) + ]; + } + + prepareLayoutInfo( + itemModel, symbolSize, layout, symbolRepeat, symbolClip, symbolOffset, + symbolPosition, symbolMeta.valueLineWidth, symbolMeta.boundingLength, symbolMeta.repeatCutLength, + opt, symbolMeta + ); + + return symbolMeta; +} + +// bar length can be negative. +function prepareBarLength(itemModel, symbolRepeat, layout, opt, output) { + var valueDim = opt.valueDim; + var symbolBoundingData = itemModel.get('symbolBoundingData'); + var valueAxis = opt.coordSys.getOtherAxis(opt.coordSys.getBaseAxis()); + var zeroPx = valueAxis.toGlobalCoord(valueAxis.dataToCoord(0)); + var pxSignIdx = 1 - +(layout[valueDim.wh] <= 0); + var boundingLength; + + if (isArray(symbolBoundingData)) { + var symbolBoundingExtent = [ + convertToCoordOnAxis(valueAxis, symbolBoundingData[0]) - zeroPx, + convertToCoordOnAxis(valueAxis, symbolBoundingData[1]) - zeroPx + ]; + symbolBoundingExtent[1] < symbolBoundingExtent[0] && (symbolBoundingExtent.reverse()); + boundingLength = symbolBoundingExtent[pxSignIdx]; + } + else if (symbolBoundingData != null) { + boundingLength = convertToCoordOnAxis(valueAxis, symbolBoundingData) - zeroPx; + } + else if (symbolRepeat) { + boundingLength = opt.coordSysExtent[valueDim.index][pxSignIdx] - zeroPx; + } + else { + boundingLength = layout[valueDim.wh]; + } + + output.boundingLength = boundingLength; + + if (symbolRepeat) { + output.repeatCutLength = layout[valueDim.wh]; + } + + output.pxSign = boundingLength > 0 ? 1 : boundingLength < 0 ? -1 : 0; +} + +function convertToCoordOnAxis(axis, value) { + return axis.toGlobalCoord(axis.dataToCoord(axis.scale.parse(value))); +} + +// Support ['100%', '100%'] +function prepareSymbolSize( + data, dataIndex, layout, symbolRepeat, symbolClip, boundingLength, + pxSign, symbolPatternSize, opt, output +) { + var valueDim = opt.valueDim; + var categoryDim = opt.categoryDim; + var categorySize = Math.abs(layout[categoryDim.wh]); + + var symbolSize = data.getItemVisual(dataIndex, 'symbolSize'); + if (isArray(symbolSize)) { + symbolSize = symbolSize.slice(); + } + else { + if (symbolSize == null) { + symbolSize = '100%'; + } + symbolSize = [symbolSize, symbolSize]; + } + + // Note: percentage symbolSize (like '100%') do not consider lineWidth, because it is + // to complicated to calculate real percent value if considering scaled lineWidth. + // So the actual size will bigger than layout size if lineWidth is bigger than zero, + // which can be tolerated in pictorial chart. + + symbolSize[categoryDim.index] = parsePercent$1( + symbolSize[categoryDim.index], + categorySize + ); + symbolSize[valueDim.index] = parsePercent$1( + symbolSize[valueDim.index], + symbolRepeat ? categorySize : Math.abs(boundingLength) + ); + + output.symbolSize = symbolSize; + + // If x or y is less than zero, show reversed shape. + var symbolScale = output.symbolScale = [ + symbolSize[0] / symbolPatternSize, + symbolSize[1] / symbolPatternSize + ]; + // Follow convention, 'right' and 'top' is the normal scale. + symbolScale[valueDim.index] *= (opt.isHorizontal ? -1 : 1) * pxSign; +} + +function prepareLineWidth(itemModel, symbolScale, rotation, opt, output) { + // In symbols are drawn with scale, so do not need to care about the case that width + // or height are too small. But symbol use strokeNoScale, where acture lineWidth should + // be calculated. + var valueLineWidth = itemModel.get(BAR_BORDER_WIDTH_QUERY$1) || 0; + + if (valueLineWidth) { + pathForLineWidth.attr({ + scale: symbolScale.slice(), + rotation: rotation + }); + pathForLineWidth.updateTransform(); + valueLineWidth /= pathForLineWidth.getLineScale(); + valueLineWidth *= symbolScale[opt.valueDim.index]; + } + + output.valueLineWidth = valueLineWidth; +} + +function prepareLayoutInfo( + itemModel, symbolSize, layout, symbolRepeat, symbolClip, symbolOffset, + symbolPosition, valueLineWidth, boundingLength, repeatCutLength, opt, output +) { + var categoryDim = opt.categoryDim; + var valueDim = opt.valueDim; + var pxSign = output.pxSign; + + var unitLength = Math.max(symbolSize[valueDim.index] + valueLineWidth, 0); + var pathLen = unitLength; + + // Note: rotation will not effect the layout of symbols, because user may + // want symbols to rotate on its center, which should not be translated + // when rotating. + + if (symbolRepeat) { + var absBoundingLength = Math.abs(boundingLength); + + var symbolMargin = retrieve(itemModel.get('symbolMargin'), '15%') + ''; + var hasEndGap = false; + if (symbolMargin.lastIndexOf('!') === symbolMargin.length - 1) { + hasEndGap = true; + symbolMargin = symbolMargin.slice(0, symbolMargin.length - 1); + } + symbolMargin = parsePercent$1(symbolMargin, symbolSize[valueDim.index]); + + var uLenWithMargin = Math.max(unitLength + symbolMargin * 2, 0); + + // When symbol margin is less than 0, margin at both ends will be subtracted + // to ensure that all of the symbols will not be overflow the given area. + var endFix = hasEndGap ? 0 : symbolMargin * 2; + + // Both final repeatTimes and final symbolMargin area calculated based on + // boundingLength. + var repeatSpecified = isNumeric(symbolRepeat); + var repeatTimes = repeatSpecified + ? symbolRepeat + : toIntTimes((absBoundingLength + endFix) / uLenWithMargin); + + // Adjust calculate margin, to ensure each symbol is displayed + // entirely in the given layout area. + var mDiff = absBoundingLength - repeatTimes * unitLength; + symbolMargin = mDiff / 2 / (hasEndGap ? repeatTimes : repeatTimes - 1); + uLenWithMargin = unitLength + symbolMargin * 2; + endFix = hasEndGap ? 0 : symbolMargin * 2; + + // Update repeatTimes when not all symbol will be shown. + if (!repeatSpecified && symbolRepeat !== 'fixed') { + repeatTimes = repeatCutLength + ? toIntTimes((Math.abs(repeatCutLength) + endFix) / uLenWithMargin) + : 0; + } + + pathLen = repeatTimes * uLenWithMargin - endFix; + output.repeatTimes = repeatTimes; + output.symbolMargin = symbolMargin; + } + + var sizeFix = pxSign * (pathLen / 2); + var pathPosition = output.pathPosition = []; + pathPosition[categoryDim.index] = layout[categoryDim.wh] / 2; + pathPosition[valueDim.index] = symbolPosition === 'start' + ? sizeFix + : symbolPosition === 'end' + ? boundingLength - sizeFix + : boundingLength / 2; // 'center' + if (symbolOffset) { + pathPosition[0] += symbolOffset[0]; + pathPosition[1] += symbolOffset[1]; + } + + var bundlePosition = output.bundlePosition = []; + bundlePosition[categoryDim.index] = layout[categoryDim.xy]; + bundlePosition[valueDim.index] = layout[valueDim.xy]; + + var barRectShape = output.barRectShape = extend({}, layout); + barRectShape[valueDim.wh] = pxSign * Math.max( + Math.abs(layout[valueDim.wh]), Math.abs(pathPosition[valueDim.index] + sizeFix) + ); + barRectShape[categoryDim.wh] = layout[categoryDim.wh]; + + var clipShape = output.clipShape = {}; + // Consider that symbol may be overflow layout rect. + clipShape[categoryDim.xy] = -layout[categoryDim.xy]; + clipShape[categoryDim.wh] = opt.ecSize[categoryDim.wh]; + clipShape[valueDim.xy] = 0; + clipShape[valueDim.wh] = layout[valueDim.wh]; +} + +function createPath(symbolMeta) { + var symbolPatternSize = symbolMeta.symbolPatternSize; + var path = createSymbol( + // Consider texture img, make a big size. + symbolMeta.symbolType, + -symbolPatternSize / 2, + -symbolPatternSize / 2, + symbolPatternSize, + symbolPatternSize, + symbolMeta.color + ); + path.attr({ + culling: true + }); + path.type !== 'image' && path.setStyle({ + strokeNoScale: true + }); + + return path; +} + +function createOrUpdateRepeatSymbols(bar, opt, symbolMeta, isUpdate) { + var bundle = bar.__pictorialBundle; + var symbolSize = symbolMeta.symbolSize; + var valueLineWidth = symbolMeta.valueLineWidth; + var pathPosition = symbolMeta.pathPosition; + var valueDim = opt.valueDim; + var repeatTimes = symbolMeta.repeatTimes || 0; + + var index = 0; + var unit = symbolSize[opt.valueDim.index] + valueLineWidth + symbolMeta.symbolMargin * 2; + + eachPath(bar, function (path) { + path.__pictorialAnimationIndex = index; + path.__pictorialRepeatTimes = repeatTimes; + if (index < repeatTimes) { + updateAttr(path, null, makeTarget(index), symbolMeta, isUpdate); + } + else { + updateAttr(path, null, {scale: [0, 0]}, symbolMeta, isUpdate, function () { + bundle.remove(path); + }); + } + + updateHoverAnimation(path, symbolMeta); + + index++; + }); + + for (; index < repeatTimes; index++) { + var path = createPath(symbolMeta); + path.__pictorialAnimationIndex = index; + path.__pictorialRepeatTimes = repeatTimes; + bundle.add(path); + + var target = makeTarget(index); + + updateAttr( + path, + { + position: target.position, + scale: [0, 0] + }, + { + scale: target.scale, + rotation: target.rotation + }, + symbolMeta, + isUpdate + ); + + // FIXME + // If all emphasis/normal through action. + path + .on('mouseover', onMouseOver) + .on('mouseout', onMouseOut); + + updateHoverAnimation(path, symbolMeta); + } + + function makeTarget(index) { + var position = pathPosition.slice(); + // (start && pxSign > 0) || (end && pxSign < 0): i = repeatTimes - index + // Otherwise: i = index; + var pxSign = symbolMeta.pxSign; + var i = index; + if (symbolMeta.symbolRepeatDirection === 'start' ? pxSign > 0 : pxSign < 0) { + i = repeatTimes - 1 - index; + } + position[valueDim.index] = unit * (i - repeatTimes / 2 + 0.5) + pathPosition[valueDim.index]; + + return { + position: position, + scale: symbolMeta.symbolScale.slice(), + rotation: symbolMeta.rotation + }; + } + + function onMouseOver() { + eachPath(bar, function (path) { + path.trigger('emphasis'); + }); + } + + function onMouseOut() { + eachPath(bar, function (path) { + path.trigger('normal'); + }); + } +} + +function createOrUpdateSingleSymbol(bar, opt, symbolMeta, isUpdate) { + var bundle = bar.__pictorialBundle; + var mainPath = bar.__pictorialMainPath; + + if (!mainPath) { + mainPath = bar.__pictorialMainPath = createPath(symbolMeta); + bundle.add(mainPath); + + updateAttr( + mainPath, + { + position: symbolMeta.pathPosition.slice(), + scale: [0, 0], + rotation: symbolMeta.rotation + }, + { + scale: symbolMeta.symbolScale.slice() + }, + symbolMeta, + isUpdate + ); + + mainPath + .on('mouseover', onMouseOver) + .on('mouseout', onMouseOut); + } + else { + updateAttr( + mainPath, + null, + { + position: symbolMeta.pathPosition.slice(), + scale: symbolMeta.symbolScale.slice(), + rotation: symbolMeta.rotation + }, + symbolMeta, + isUpdate + ); + } + + updateHoverAnimation(mainPath, symbolMeta); + + function onMouseOver() { + this.trigger('emphasis'); + } + + function onMouseOut() { + this.trigger('normal'); + } +} + +// bar rect is used for label. +function createOrUpdateBarRect(bar, symbolMeta, isUpdate) { + var rectShape = extend({}, symbolMeta.barRectShape); + + var barRect = bar.__pictorialBarRect; + if (!barRect) { + barRect = bar.__pictorialBarRect = new Rect({ + z2: 2, + shape: rectShape, + silent: true, + style: { + stroke: 'transparent', + fill: 'transparent', + lineWidth: 0 + } + }); + + bar.add(barRect); + } + else { + updateAttr(barRect, null, {shape: rectShape}, symbolMeta, isUpdate); + } +} + +function createOrUpdateClip(bar, opt, symbolMeta, isUpdate) { + // If not clip, symbol will be remove and rebuilt. + if (symbolMeta.symbolClip) { + var clipPath = bar.__pictorialClipPath; + var clipShape = extend({}, symbolMeta.clipShape); + var valueDim = opt.valueDim; + var animationModel = symbolMeta.animationModel; + var dataIndex = symbolMeta.dataIndex; + + if (clipPath) { + updateProps( + clipPath, {shape: clipShape}, animationModel, dataIndex + ); + } + else { + clipShape[valueDim.wh] = 0; + clipPath = new Rect({shape: clipShape}); + bar.__pictorialBundle.setClipPath(clipPath); + bar.__pictorialClipPath = clipPath; + + var target = {}; + target[valueDim.wh] = symbolMeta.clipShape[valueDim.wh]; + + graphic[isUpdate ? 'updateProps' : 'initProps']( + clipPath, {shape: target}, animationModel, dataIndex + ); + } + } +} + +function getItemModel(data, dataIndex) { + var itemModel = data.getItemModel(dataIndex); + itemModel.getAnimationDelayParams = getAnimationDelayParams; + itemModel.isAnimationEnabled = isAnimationEnabled; + return itemModel; +} + +function getAnimationDelayParams(path) { + // The order is the same as the z-order, see `symbolRepeatDiretion`. + return { + index: path.__pictorialAnimationIndex, + count: path.__pictorialRepeatTimes + }; +} + +function isAnimationEnabled() { + // `animation` prop can be set on itemModel in pictorial bar chart. + return this.parentModel.isAnimationEnabled() && !!this.getShallow('animation'); +} + +function updateHoverAnimation(path, symbolMeta) { + path.off('emphasis').off('normal'); + + var scale = symbolMeta.symbolScale.slice(); + + symbolMeta.hoverAnimation && path + .on('emphasis', function() { + this.animateTo({ + scale: [scale[0] * 1.1, scale[1] * 1.1] + }, 400, 'elasticOut'); + }) + .on('normal', function() { + this.animateTo({ + scale: scale.slice() + }, 400, 'elasticOut'); + }); +} + +function createBar(data, opt, symbolMeta, isUpdate) { + // bar is the main element for each data. + var bar = new Group(); + // bundle is used for location and clip. + var bundle = new Group(); + bar.add(bundle); + bar.__pictorialBundle = bundle; + bundle.attr('position', symbolMeta.bundlePosition.slice()); + + if (symbolMeta.symbolRepeat) { + createOrUpdateRepeatSymbols(bar, opt, symbolMeta); + } + else { + createOrUpdateSingleSymbol(bar, opt, symbolMeta); + } + + createOrUpdateBarRect(bar, symbolMeta, isUpdate); + + createOrUpdateClip(bar, opt, symbolMeta, isUpdate); + + bar.__pictorialShapeStr = getShapeStr(data, symbolMeta); + bar.__pictorialSymbolMeta = symbolMeta; + + return bar; +} + +function updateBar(bar, opt, symbolMeta) { + var animationModel = symbolMeta.animationModel; + var dataIndex = symbolMeta.dataIndex; + var bundle = bar.__pictorialBundle; + + updateProps( + bundle, {position: symbolMeta.bundlePosition.slice()}, animationModel, dataIndex + ); + + if (symbolMeta.symbolRepeat) { + createOrUpdateRepeatSymbols(bar, opt, symbolMeta, true); + } + else { + createOrUpdateSingleSymbol(bar, opt, symbolMeta, true); + } + + createOrUpdateBarRect(bar, symbolMeta, true); + + createOrUpdateClip(bar, opt, symbolMeta, true); +} + +function removeBar(data, dataIndex, animationModel, bar) { + // Not show text when animating + var labelRect = bar.__pictorialBarRect; + labelRect && (labelRect.style.text = null); + + var pathes = []; + eachPath(bar, function (path) { + pathes.push(path); + }); + bar.__pictorialMainPath && pathes.push(bar.__pictorialMainPath); + + // I do not find proper remove animation for clip yet. + bar.__pictorialClipPath && (animationModel = null); + + each$1(pathes, function (path) { + updateProps( + path, {scale: [0, 0]}, animationModel, dataIndex, + function () { + bar.parent && bar.parent.remove(bar); + } + ); + }); + + data.setItemGraphicEl(dataIndex, null); +} + +function getShapeStr(data, symbolMeta) { + return [ + data.getItemVisual(symbolMeta.dataIndex, 'symbol') || 'none', + !!symbolMeta.symbolRepeat, + !!symbolMeta.symbolClip + ].join(':'); +} + +function eachPath(bar, cb, context) { + // Do not use Group#eachChild, because it do not support remove. + each$1(bar.__pictorialBundle.children(), function (el) { + el !== bar.__pictorialBarRect && cb.call(context, el); + }); +} + +function updateAttr(el, immediateAttrs, animationAttrs, symbolMeta, isUpdate, cb) { + immediateAttrs && el.attr(immediateAttrs); + // when symbolCip used, only clip path has init animation, otherwise it would be weird effect. + if (symbolMeta.symbolClip && !isUpdate) { + animationAttrs && el.attr(animationAttrs); + } + else { + animationAttrs && graphic[isUpdate ? 'updateProps' : 'initProps']( + el, animationAttrs, symbolMeta.animationModel, symbolMeta.dataIndex, cb + ); + } +} + +function updateCommon$1(bar, opt, symbolMeta) { + var color = symbolMeta.color; + var dataIndex = symbolMeta.dataIndex; + var itemModel = symbolMeta.itemModel; + // Color must be excluded. + // Because symbol provide setColor individually to set fill and stroke + var normalStyle = itemModel.getModel('itemStyle').getItemStyle(['color']); + var hoverStyle = itemModel.getModel('emphasis.itemStyle').getItemStyle(); + var cursorStyle = itemModel.getShallow('cursor'); + + eachPath(bar, function (path) { + // PENDING setColor should be before setStyle!!! + path.setColor(color); + path.setStyle(defaults( + { + fill: color, + opacity: symbolMeta.opacity + }, + normalStyle + )); + setHoverStyle(path, hoverStyle); + + cursorStyle && (path.cursor = cursorStyle); + path.z2 = symbolMeta.z2; + }); + + var barRectHoverStyle = {}; + var barPositionOutside = opt.valueDim.posDesc[+(symbolMeta.boundingLength > 0)]; + var barRect = bar.__pictorialBarRect; + + setLabel( + barRect.style, barRectHoverStyle, itemModel, + color, opt.seriesModel, dataIndex, barPositionOutside + ); + + setHoverStyle(barRect, barRectHoverStyle); +} + +function toIntTimes(times) { + var roundedTimes = Math.round(times); + // Escapse accurate error + return Math.abs(times - roundedTimes) < 1e-4 + ? roundedTimes + : Math.ceil(times); +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +// In case developer forget to include grid component +registerLayout(curry( + layout, 'pictorialBar' +)); +registerVisual(visualSymbol('pictorialBar', 'roundRect')); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * @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; + +}; + +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'; + + }, + + /** + * @override + */ + pointToData: function (point, clamp) { + return this.coordinateSystem.pointToData(point, clamp)[0]; + }, + + /** + * 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 + +}; + +inherits(SingleAxis, Axis); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * Single coordinates system. + */ + +/** + * 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 = 'single'; + + /** + * Add it just for draw tooltip. + * + * @type {Array.} + * @readOnly + */ + this.dimensions = ['single']; + + /** + * @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', + + axisPointerEnabled: true, + + 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, + 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; + axis.coordinateSystem = this; + this._axis = axis; + }, + + /** + * Update axis scale after data processed + * @param {module:echarts/model/Global} ecModel + * @param {module:echarts/ExtensionAPI} api + */ + update: function (ecModel, api) { + ecModel.eachSeries(function (seriesModel) { + if (seriesModel.coordinateSystem === this) { + var data = seriesModel.getData(); + each$1(data.mapDimension(this.dimension, true), function (dim) { + this._axis.scale.unionExtentFromData(data, dim); + }, this); + niceScaleExtent(this._axis.scale, 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 = 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; + }, + + /** + * @return {Array.} + */ + getAxes: function () { + return [this._axis]; + }, + + /** + * @return {Object} {baseAxes: [], otherAxes: []} + */ + getTooltipAxes: function () { + return {baseAxes: [this.getAxis()]}; + }, + + /** + * 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; + + if (val instanceof Array) { + val = val[0]; + } + + pt[idx] = axis.toGlobalCoord(axis.dataToCoord(+val)); + pt[1 - idx] = idx === 0 ? (rect.y + rect.height / 2) : (rect.x + rect.width / 2); + return pt; + } + +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * Single coordinate system creator. + */ + +/** + * Create single coordinate system and inject it into seriesModel. + * + * @param {module:echarts/model/Global} ecModel + * @param {module:echarts/ExtensionAPI} api + * @return {Array.} + */ +function create$3(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 && singleAxisModel.coordinateSystem; + } + }); + + return singles; +} + +CoordinateSystemManager.register('single', { + create: create$3, + dimensions: Single.prototype.dimensions +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * @param {Object} opt {labelInside} + * @return {Object} { + * position, rotation, labelDirection, labelOffset, + * tickDirection, labelRotate, z2 + * } + */ +function layout$2(axisModel, opt) { + opt = opt || {}; + 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.get('axisTick.inside')) { + layout.tickDirection = -layout.tickDirection; + } + + if (retrieve(opt.labelInside, axisModel.get('axisLabel.inside'))) { + layout.labelDirection = -layout.labelDirection; + } + + var labelRotation = opt.rotate; + labelRotation == null && (labelRotation = axisModel.get('axisLabel.rotate')); + layout.labelRotation = axisPosition === 'top' ? -labelRotation : labelRotation; + + layout.z2 = 1; + + return layout; +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + + +var axisBuilderAttrs$2 = [ + 'axisLine', 'axisTickLabel', 'axisName' +]; + +var selfBuilderAttr = 'splitLine'; + +var SingleAxisView = AxisView.extend({ + + type: 'singleAxis', + + axisPointerClass: 'SingleAxisPointer', + + render: function (axisModel, ecModel, api, payload) { + + var group = this.group; + + group.removeAll(); + + var layout = layout$2(axisModel); + + var axisBuilder = new AxisBuilder(axisModel, layout); + + each$1(axisBuilderAttrs$2, axisBuilder.add, axisBuilder); + + group.add(axisBuilder.getGroup()); + + if (axisModel.get(selfBuilderAttr + '.show')) { + this['_' + selfBuilderAttr](axisModel); + } + + SingleAxisView.superCall(this, 'render', axisModel, ecModel, api, payload); + }, + + _splitLine: function(axisModel) { + var axis = axisModel.axis; + + if (axis.scale.isBlank()) { + return; + } + + var splitLineModel = axisModel.getModel('splitLine'); + var lineStyleModel = splitLineModel.getModel('lineStyle'); + var lineWidth = lineStyleModel.get('width'); + var lineColors = lineStyleModel.get('color'); + + lineColors = lineColors instanceof Array ? lineColors : [lineColors]; + + var gridRect = axisModel.coordinateSystem.getRect(); + var isHorizontal = axis.isHorizontal(); + + var splitLines = []; + var lineCount = 0; + + var ticksCoords = axis.getTicksCoords({ + tickModel: splitLineModel + }); + + var p1 = []; + var p2 = []; + + for (var i = 0; i < ticksCoords.length; ++i) { + var tickCoord = axis.toGlobalCoord(ticksCoords[i].coord); + 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 Line( + 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(mergePath(splitLines[i], { + style: { + stroke: lineColors[i % lineColors.length], + lineDash: lineStyleModel.getLineDash(lineWidth), + lineWidth: lineWidth + }, + silent: true + })); + } + } +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var AxisModel$4 = ComponentModel.extend({ + + type: 'singleAxis', + + layoutMode: 'box', + + /** + * @type {module:echarts/coord/single/SingleAxis} + */ + axis: null, + + /** + * @type {module:echarts/coord/single/Single} + */ + coordinateSystem: null, + + /** + * @override + */ + getCoordSysModel: function () { + return this; + } + +}); + +var defaultOption$2 = { + + left: '5%', + top: '5%', + right: '5%', + bottom: '5%', + + type: 'value', + + position: 'bottom', + + orient: 'horizontal', + + axisLine: { + show: true, + lineStyle: { + width: 2, + type: 'solid' + } + }, + + // Single coordinate system and single axis is the, + // which is used as the parent tooltip model. + // same model, so we set default tooltip show as true. + tooltip: { + show: true + }, + + axisTick: { + show: true, + length: 6, + lineStyle: { + width: 2 + } + }, + + axisLabel: { + show: true, + interval: 'auto' + }, + + splitLine: { + show: true, + lineStyle: { + type: 'dashed', + opacity: 0.2 + } + } +}; + +function getAxisType$2(axisName, option) { + return option.type || (option.data ? 'category' : 'value'); +} + +merge(AxisModel$4.prototype, axisModelCommonMixin); + +axisModelCreator('single', AxisModel$4, getAxisType$2, defaultOption$2); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * @param {Object} finder contains {seriesIndex, dataIndex, dataIndexInside} + * @param {module:echarts/model/Global} ecModel + * @return {Object} {point: [x, y], el: ...} point Will not be null. + */ +var findPointFromSeries = function (finder, ecModel) { + var point = []; + var seriesIndex = finder.seriesIndex; + var seriesModel; + if (seriesIndex == null || !( + seriesModel = ecModel.getSeriesByIndex(seriesIndex) + )) { + return {point: []}; + } + + var data = seriesModel.getData(); + var dataIndex = queryDataIndex(data, finder); + if (dataIndex == null || dataIndex < 0 || isArray(dataIndex)) { + return {point: []}; + } + + var el = data.getItemGraphicEl(dataIndex); + var coordSys = seriesModel.coordinateSystem; + + if (seriesModel.getTooltipPosition) { + point = seriesModel.getTooltipPosition(dataIndex) || []; + } + else if (coordSys && coordSys.dataToPoint) { + point = coordSys.dataToPoint( + data.getValues( + map(coordSys.dimensions, function (dim) { + return data.mapDimension(dim); + }), dataIndex, true + ) + ) || []; + } + else if (el) { + // Use graphic bounding rect + var rect = el.getBoundingRect().clone(); + rect.applyTransform(el.transform); + point = [ + rect.x + rect.width / 2, + rect.y + rect.height / 2 + ]; + } + + return {point: point, el: el}; +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var each$14 = each$1; +var curry$3 = curry; +var inner$7 = makeInner(); + +/** + * Basic logic: check all axis, if they do not demand show/highlight, + * then hide/downplay them. + * + * @param {Object} coordSysAxesInfo + * @param {Object} payload + * @param {string} [payload.currTrigger] 'click' | 'mousemove' | 'leave' + * @param {Array.} [payload.x] x and y, which are mandatory, specify a point to + * trigger axisPointer and tooltip. + * @param {Array.} [payload.y] x and y, which are mandatory, specify a point to + * trigger axisPointer and tooltip. + * @param {Object} [payload.seriesIndex] finder, optional, restrict target axes. + * @param {Object} [payload.dataIndex] finder, restrict target axes. + * @param {Object} [payload.axesInfo] finder, restrict target axes. + * [{ + * axisDim: 'x'|'y'|'angle'|..., + * axisIndex: ..., + * value: ... + * }, ...] + * @param {Function} [payload.dispatchAction] + * @param {Object} [payload.tooltipOption] + * @param {Object|Array.|Function} [payload.position] Tooltip position, + * which can be specified in dispatchAction + * @param {module:echarts/model/Global} ecModel + * @param {module:echarts/ExtensionAPI} api + * @return {Object} content of event obj for echarts.connect. + */ +var axisTrigger = function (payload, ecModel, api) { + var currTrigger = payload.currTrigger; + var point = [payload.x, payload.y]; + var finder = payload; + var dispatchAction = payload.dispatchAction || bind(api.dispatchAction, api); + var coordSysAxesInfo = ecModel.getComponent('axisPointer').coordSysAxesInfo; + + // Pending + // See #6121. But we are not able to reproduce it yet. + if (!coordSysAxesInfo) { + return; + } + + if (illegalPoint(point)) { + // Used in the default behavior of `connection`: use the sample seriesIndex + // and dataIndex. And also used in the tooltipView trigger. + point = findPointFromSeries({ + seriesIndex: finder.seriesIndex, + // Do not use dataIndexInside from other ec instance. + // FIXME: auto detect it? + dataIndex: finder.dataIndex + }, ecModel).point; + } + var isIllegalPoint = illegalPoint(point); + + // Axis and value can be specified when calling dispatchAction({type: 'updateAxisPointer'}). + // Notice: In this case, it is difficult to get the `point` (which is necessary to show + // tooltip, so if point is not given, we just use the point found by sample seriesIndex + // and dataIndex. + var inputAxesInfo = finder.axesInfo; + + var axesInfo = coordSysAxesInfo.axesInfo; + var shouldHide = currTrigger === 'leave' || illegalPoint(point); + var outputFinder = {}; + + var showValueMap = {}; + var dataByCoordSys = {list: [], map: {}}; + var updaters = { + showPointer: curry$3(showPointer, showValueMap), + showTooltip: curry$3(showTooltip, dataByCoordSys) + }; + + // Process for triggered axes. + each$14(coordSysAxesInfo.coordSysMap, function (coordSys, coordSysKey) { + // If a point given, it must be contained by the coordinate system. + var coordSysContainsPoint = isIllegalPoint || coordSys.containPoint(point); + + each$14(coordSysAxesInfo.coordSysAxesInfo[coordSysKey], function (axisInfo, key) { + var axis = axisInfo.axis; + var inputAxisInfo = findInputAxisInfo(inputAxesInfo, axisInfo); + // If no inputAxesInfo, no axis is restricted. + if (!shouldHide && coordSysContainsPoint && (!inputAxesInfo || inputAxisInfo)) { + var val = inputAxisInfo && inputAxisInfo.value; + if (val == null && !isIllegalPoint) { + val = axis.pointToData(point); + } + val != null && processOnAxis(axisInfo, val, updaters, false, outputFinder); + } + }); + }); + + // Process for linked axes. + var linkTriggers = {}; + each$14(axesInfo, function (tarAxisInfo, tarKey) { + var linkGroup = tarAxisInfo.linkGroup; + + // If axis has been triggered in the previous stage, it should not be triggered by link. + if (linkGroup && !showValueMap[tarKey]) { + each$14(linkGroup.axesInfo, function (srcAxisInfo, srcKey) { + var srcValItem = showValueMap[srcKey]; + // If srcValItem exist, source axis is triggered, so link to target axis. + if (srcAxisInfo !== tarAxisInfo && srcValItem) { + var val = srcValItem.value; + linkGroup.mapper && (val = tarAxisInfo.axis.scale.parse(linkGroup.mapper( + val, makeMapperParam(srcAxisInfo), makeMapperParam(tarAxisInfo) + ))); + linkTriggers[tarAxisInfo.key] = val; + } + }); + } + }); + each$14(linkTriggers, function (val, tarKey) { + processOnAxis(axesInfo[tarKey], val, updaters, true, outputFinder); + }); + + updateModelActually(showValueMap, axesInfo, outputFinder); + dispatchTooltipActually(dataByCoordSys, point, payload, dispatchAction); + dispatchHighDownActually(axesInfo, dispatchAction, api); + + return outputFinder; +}; + +function processOnAxis(axisInfo, newValue, updaters, dontSnap, outputFinder) { + var axis = axisInfo.axis; + + if (axis.scale.isBlank() || !axis.containData(newValue)) { + return; + } + + if (!axisInfo.involveSeries) { + updaters.showPointer(axisInfo, newValue); + return; + } + + // Heavy calculation. So put it after axis.containData checking. + var payloadInfo = buildPayloadsBySeries(newValue, axisInfo); + var payloadBatch = payloadInfo.payloadBatch; + var snapToValue = payloadInfo.snapToValue; + + // Fill content of event obj for echarts.connect. + // By defualt use the first involved series data as a sample to connect. + if (payloadBatch[0] && outputFinder.seriesIndex == null) { + extend(outputFinder, payloadBatch[0]); + } + + // If no linkSource input, this process is for collecting link + // target, where snap should not be accepted. + if (!dontSnap && axisInfo.snap) { + if (axis.containData(snapToValue) && snapToValue != null) { + newValue = snapToValue; + } + } + + updaters.showPointer(axisInfo, newValue, payloadBatch, outputFinder); + // Tooltip should always be snapToValue, otherwise there will be + // incorrect "axis value ~ series value" mapping displayed in tooltip. + updaters.showTooltip(axisInfo, payloadInfo, snapToValue); +} + +function buildPayloadsBySeries(value, axisInfo) { + var axis = axisInfo.axis; + var dim = axis.dim; + var snapToValue = value; + var payloadBatch = []; + var minDist = Number.MAX_VALUE; + var minDiff = -1; + + each$14(axisInfo.seriesModels, function (series, idx) { + var dataDim = series.getData().mapDimension(dim, true); + var seriesNestestValue; + var dataIndices; + + if (series.getAxisTooltipData) { + var result = series.getAxisTooltipData(dataDim, value, axis); + dataIndices = result.dataIndices; + seriesNestestValue = result.nestestValue; + } + else { + dataIndices = series.getData().indicesOfNearest( + dataDim[0], + value, + // Add a threshold to avoid find the wrong dataIndex + // when data length is not same. + // false, + axis.type === 'category' ? 0.5 : null + ); + if (!dataIndices.length) { + return; + } + seriesNestestValue = series.getData().get(dataDim[0], dataIndices[0]); + } + + if (seriesNestestValue == null || !isFinite(seriesNestestValue)) { + return; + } + + var diff = value - seriesNestestValue; + var dist = Math.abs(diff); + // Consider category case + if (dist <= minDist) { + if (dist < minDist || (diff >= 0 && minDiff < 0)) { + minDist = dist; + minDiff = diff; + snapToValue = seriesNestestValue; + payloadBatch.length = 0; + } + each$14(dataIndices, function (dataIndex) { + payloadBatch.push({ + seriesIndex: series.seriesIndex, + dataIndexInside: dataIndex, + dataIndex: series.getData().getRawIndex(dataIndex) + }); + }); + } + }); + + return { + payloadBatch: payloadBatch, + snapToValue: snapToValue + }; +} + +function showPointer(showValueMap, axisInfo, value, payloadBatch) { + showValueMap[axisInfo.key] = {value: value, payloadBatch: payloadBatch}; +} + +function showTooltip(dataByCoordSys, axisInfo, payloadInfo, value) { + var payloadBatch = payloadInfo.payloadBatch; + var axis = axisInfo.axis; + var axisModel = axis.model; + var axisPointerModel = axisInfo.axisPointerModel; + + // If no data, do not create anything in dataByCoordSys, + // whose length will be used to judge whether dispatch action. + if (!axisInfo.triggerTooltip || !payloadBatch.length) { + return; + } + + var coordSysModel = axisInfo.coordSys.model; + var coordSysKey = makeKey(coordSysModel); + var coordSysItem = dataByCoordSys.map[coordSysKey]; + if (!coordSysItem) { + coordSysItem = dataByCoordSys.map[coordSysKey] = { + coordSysId: coordSysModel.id, + coordSysIndex: coordSysModel.componentIndex, + coordSysType: coordSysModel.type, + coordSysMainType: coordSysModel.mainType, + dataByAxis: [] + }; + dataByCoordSys.list.push(coordSysItem); + } + + coordSysItem.dataByAxis.push({ + axisDim: axis.dim, + axisIndex: axisModel.componentIndex, + axisType: axisModel.type, + axisId: axisModel.id, + value: value, + // Caustion: viewHelper.getValueLabel is actually on "view stage", which + // depends that all models have been updated. So it should not be performed + // here. Considering axisPointerModel used here is volatile, which is hard + // to be retrieve in TooltipView, we prepare parameters here. + valueLabelOpt: { + precision: axisPointerModel.get('label.precision'), + formatter: axisPointerModel.get('label.formatter') + }, + seriesDataIndices: payloadBatch.slice() + }); +} + +function updateModelActually(showValueMap, axesInfo, outputFinder) { + var outputAxesInfo = outputFinder.axesInfo = []; + // Basic logic: If no 'show' required, 'hide' this axisPointer. + each$14(axesInfo, function (axisInfo, key) { + var option = axisInfo.axisPointerModel.option; + var valItem = showValueMap[key]; + + if (valItem) { + !axisInfo.useHandle && (option.status = 'show'); + option.value = valItem.value; + // For label formatter param and highlight. + option.seriesDataIndices = (valItem.payloadBatch || []).slice(); + } + // When always show (e.g., handle used), remain + // original value and status. + else { + // If hide, value still need to be set, consider + // click legend to toggle axis blank. + !axisInfo.useHandle && (option.status = 'hide'); + } + + // If status is 'hide', should be no info in payload. + option.status === 'show' && outputAxesInfo.push({ + axisDim: axisInfo.axis.dim, + axisIndex: axisInfo.axis.model.componentIndex, + value: option.value + }); + }); +} + +function dispatchTooltipActually(dataByCoordSys, point, payload, dispatchAction) { + // Basic logic: If no showTip required, hideTip will be dispatched. + if (illegalPoint(point) || !dataByCoordSys.list.length) { + dispatchAction({type: 'hideTip'}); + return; + } + + // In most case only one axis (or event one series is used). It is + // convinient to fetch payload.seriesIndex and payload.dataIndex + // dirtectly. So put the first seriesIndex and dataIndex of the first + // axis on the payload. + var sampleItem = ((dataByCoordSys.list[0].dataByAxis[0] || {}).seriesDataIndices || [])[0] || {}; + + dispatchAction({ + type: 'showTip', + escapeConnect: true, + x: point[0], + y: point[1], + tooltipOption: payload.tooltipOption, + position: payload.position, + dataIndexInside: sampleItem.dataIndexInside, + dataIndex: sampleItem.dataIndex, + seriesIndex: sampleItem.seriesIndex, + dataByCoordSys: dataByCoordSys.list + }); +} + +function dispatchHighDownActually(axesInfo, dispatchAction, api) { + // FIXME + // highlight status modification shoule be a stage of main process? + // (Consider confilct (e.g., legend and axisPointer) and setOption) + + var zr = api.getZr(); + var highDownKey = 'axisPointerLastHighlights'; + var lastHighlights = inner$7(zr)[highDownKey] || {}; + var newHighlights = inner$7(zr)[highDownKey] = {}; + + // Update highlight/downplay status according to axisPointer model. + // Build hash map and remove duplicate incidentally. + each$14(axesInfo, function (axisInfo, key) { + var option = axisInfo.axisPointerModel.option; + option.status === 'show' && each$14(option.seriesDataIndices, function (batchItem) { + var key = batchItem.seriesIndex + ' | ' + batchItem.dataIndex; + newHighlights[key] = batchItem; + }); + }); + + // Diff. + var toHighlight = []; + var toDownplay = []; + each$1(lastHighlights, function (batchItem, key) { + !newHighlights[key] && toDownplay.push(batchItem); + }); + each$1(newHighlights, function (batchItem, key) { + !lastHighlights[key] && toHighlight.push(batchItem); + }); + + toDownplay.length && api.dispatchAction({ + type: 'downplay', escapeConnect: true, batch: toDownplay + }); + toHighlight.length && api.dispatchAction({ + type: 'highlight', escapeConnect: true, batch: toHighlight + }); +} + +function findInputAxisInfo(inputAxesInfo, axisInfo) { + for (var i = 0; i < (inputAxesInfo || []).length; i++) { + var inputAxisInfo = inputAxesInfo[i]; + if (axisInfo.axis.dim === inputAxisInfo.axisDim + && axisInfo.axis.model.componentIndex === inputAxisInfo.axisIndex + ) { + return inputAxisInfo; + } + } +} + +function makeMapperParam(axisInfo) { + var axisModel = axisInfo.axis.model; + var item = {}; + var dim = item.axisDim = axisInfo.axis.dim; + item.axisIndex = item[dim + 'AxisIndex'] = axisModel.componentIndex; + item.axisName = item[dim + 'AxisName'] = axisModel.name; + item.axisId = item[dim + 'AxisId'] = axisModel.id; + return item; +} + +function illegalPoint(point) { + return !point || point[0] == null || isNaN(point[0]) || point[1] == null || isNaN(point[1]); +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var AxisPointerModel = extendComponentModel({ + + type: 'axisPointer', + + coordSysAxesInfo: null, + + defaultOption: { + // 'auto' means that show when triggered by tooltip or handle. + show: 'auto', + // 'click' | 'mousemove' | 'none' + triggerOn: null, // set default in AxisPonterView.js + + zlevel: 0, + z: 50, + + type: 'line', + // axispointer triggered by tootip determine snap automatically, + // see `modelHelper`. + snap: false, + triggerTooltip: true, + + value: null, + status: null, // Init value depends on whether handle is used. + + // [group0, group1, ...] + // Each group can be: { + // mapper: function () {}, + // singleTooltip: 'multiple', // 'multiple' or 'single' + // xAxisId: ..., + // yAxisName: ..., + // angleAxisIndex: ... + // } + // mapper: can be ignored. + // input: {axisInfo, value} + // output: {axisInfo, value} + link: [], + + // Do not set 'auto' here, otherwise global animation: false + // will not effect at this axispointer. + animation: null, + animationDurationUpdate: 200, + + lineStyle: { + color: '#aaa', + width: 1, + type: 'solid' + }, + + shadowStyle: { + color: 'rgba(150,150,150,0.3)' + }, + + label: { + show: true, + formatter: null, // string | Function + precision: 'auto', // Or a number like 0, 1, 2 ... + margin: 3, + color: '#fff', + padding: [5, 7, 5, 7], + backgroundColor: 'auto', // default: axis line color + borderColor: null, + borderWidth: 0, + shadowBlur: 3, + shadowColor: '#aaa' + // Considering applicability, common style should + // better not have shadowOffset. + // shadowOffsetX: 0, + // shadowOffsetY: 2 + }, + + handle: { + show: false, + icon: 'M10.7,11.9v-1.3H9.3v1.3c-4.9,0.3-8.8,4.4-8.8,9.4c0,5,3.9,9.1,8.8,9.4h1.3c4.9-0.3,8.8-4.4,8.8-9.4C19.5,16.3,15.6,12.2,10.7,11.9z M13.3,24.4H6.7v-1.2h6.6z M13.3,22H6.7v-1.2h6.6z M13.3,19.6H6.7v-1.2h6.6z', // jshint ignore:line + size: 45, + // handle margin is from symbol center to axis, which is stable when circular move. + margin: 50, + // color: '#1b8bbd' + // color: '#2f4554' + color: '#333', + shadowBlur: 3, + shadowColor: '#aaa', + shadowOffsetX: 0, + shadowOffsetY: 2, + + // For mobile performance + throttle: 40 + } + } + +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var inner$8 = makeInner(); +var each$15 = each$1; + +/** + * @param {string} key + * @param {module:echarts/ExtensionAPI} api + * @param {Function} handler + * param: {string} currTrigger + * param: {Array.} point + */ +function register(key, api, handler) { + if (env$1.node) { + return; + } + + var zr = api.getZr(); + inner$8(zr).records || (inner$8(zr).records = {}); + + initGlobalListeners(zr, api); + + var record = inner$8(zr).records[key] || (inner$8(zr).records[key] = {}); + record.handler = handler; +} + +function initGlobalListeners(zr, api) { + if (inner$8(zr).initialized) { + return; + } + + inner$8(zr).initialized = true; + + useHandler('click', curry(doEnter, 'click')); + useHandler('mousemove', curry(doEnter, 'mousemove')); + // useHandler('mouseout', onLeave); + useHandler('globalout', onLeave); + + function useHandler(eventType, cb) { + zr.on(eventType, function (e) { + var dis = makeDispatchAction(api); + + each$15(inner$8(zr).records, function (record) { + record && cb(record, e, dis.dispatchAction); + }); + + dispatchTooltipFinally(dis.pendings, api); + }); + } +} + +function dispatchTooltipFinally(pendings, api) { + var showLen = pendings.showTip.length; + var hideLen = pendings.hideTip.length; + + var actuallyPayload; + if (showLen) { + actuallyPayload = pendings.showTip[showLen - 1]; + } + else if (hideLen) { + actuallyPayload = pendings.hideTip[hideLen - 1]; + } + if (actuallyPayload) { + actuallyPayload.dispatchAction = null; + api.dispatchAction(actuallyPayload); + } +} + +function onLeave(record, e, dispatchAction) { + record.handler('leave', null, dispatchAction); +} + +function doEnter(currTrigger, record, e, dispatchAction) { + record.handler(currTrigger, e, dispatchAction); +} + +function makeDispatchAction(api) { + var pendings = { + showTip: [], + hideTip: [] + }; + // FIXME + // better approach? + // 'showTip' and 'hideTip' can be triggered by axisPointer and tooltip, + // which may be conflict, (axisPointer call showTip but tooltip call hideTip); + // So we have to add "final stage" to merge those dispatched actions. + var dispatchAction = function (payload) { + var pendingList = pendings[payload.type]; + if (pendingList) { + pendingList.push(payload); + } + else { + payload.dispatchAction = dispatchAction; + api.dispatchAction(payload); + } + }; + + return { + dispatchAction: dispatchAction, + pendings: pendings + }; +} + +/** + * @param {string} key + * @param {module:echarts/ExtensionAPI} api + */ +function unregister(key, api) { + if (env$1.node) { + return; + } + var zr = api.getZr(); + var record = (inner$8(zr).records || {})[key]; + if (record) { + inner$8(zr).records[key] = null; + } +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var AxisPointerView = extendComponentView({ + + type: 'axisPointer', + + render: function (globalAxisPointerModel, ecModel, api) { + var globalTooltipModel = ecModel.getComponent('tooltip'); + var triggerOn = globalAxisPointerModel.get('triggerOn') + || (globalTooltipModel && globalTooltipModel.get('triggerOn') || 'mousemove|click'); + + // Register global listener in AxisPointerView to enable + // AxisPointerView to be independent to Tooltip. + register( + 'axisPointer', + api, + function (currTrigger, e, dispatchAction) { + // If 'none', it is not controlled by mouse totally. + if (triggerOn !== 'none' + && (currTrigger === 'leave' || triggerOn.indexOf(currTrigger) >= 0) + ) { + dispatchAction({ + type: 'updateAxisPointer', + currTrigger: currTrigger, + x: e && e.offsetX, + y: e && e.offsetY + }); + } + } + ); + }, + + /** + * @override + */ + remove: function (ecModel, api) { + unregister(api.getZr(), 'axisPointer'); + AxisPointerView.superApply(this._model, 'remove', arguments); + }, + + /** + * @override + */ + dispose: function (ecModel, api) { + unregister('axisPointer', api); + AxisPointerView.superApply(this._model, 'dispose', arguments); + } + +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var inner$9 = makeInner(); +var clone$4 = clone; +var bind$2 = bind; + +/** + * Base axis pointer class in 2D. + * Implemenents {module:echarts/component/axis/IAxisPointer}. + */ +function BaseAxisPointer () { +} + +BaseAxisPointer.prototype = { + + /** + * @private + */ + _group: null, + + /** + * @private + */ + _lastGraphicKey: null, + + /** + * @private + */ + _handle: null, + + /** + * @private + */ + _dragging: false, + + /** + * @private + */ + _lastValue: null, + + /** + * @private + */ + _lastStatus: null, + + /** + * @private + */ + _payloadInfo: null, + + /** + * In px, arbitrary value. Do not set too small, + * no animation is ok for most cases. + * @protected + */ + animationThreshold: 15, + + /** + * @implement + */ + render: function (axisModel, axisPointerModel, api, forceRender) { + var value = axisPointerModel.get('value'); + var status = axisPointerModel.get('status'); + + // Bind them to `this`, not in closure, otherwise they will not + // be replaced when user calling setOption in not merge mode. + this._axisModel = axisModel; + this._axisPointerModel = axisPointerModel; + this._api = api; + + // Optimize: `render` will be called repeatly during mouse move. + // So it is power consuming if performing `render` each time, + // especially on mobile device. + if (!forceRender + && this._lastValue === value + && this._lastStatus === status + ) { + return; + } + this._lastValue = value; + this._lastStatus = status; + + var group = this._group; + var handle = this._handle; + + if (!status || status === 'hide') { + // Do not clear here, for animation better. + group && group.hide(); + handle && handle.hide(); + return; + } + group && group.show(); + handle && handle.show(); + + // Otherwise status is 'show' + var elOption = {}; + this.makeElOption(elOption, value, axisModel, axisPointerModel, api); + + // Enable change axis pointer type. + var graphicKey = elOption.graphicKey; + if (graphicKey !== this._lastGraphicKey) { + this.clear(api); + } + this._lastGraphicKey = graphicKey; + + var moveAnimation = this._moveAnimation = + this.determineAnimation(axisModel, axisPointerModel); + + if (!group) { + group = this._group = new Group(); + this.createPointerEl(group, elOption, axisModel, axisPointerModel); + this.createLabelEl(group, elOption, axisModel, axisPointerModel); + api.getZr().add(group); + } + else { + var doUpdateProps = curry(updateProps$1, axisPointerModel, moveAnimation); + this.updatePointerEl(group, elOption, doUpdateProps, axisPointerModel); + this.updateLabelEl(group, elOption, doUpdateProps, axisPointerModel); + } + + updateMandatoryProps(group, axisPointerModel, true); + + this._renderHandle(value); + }, + + /** + * @implement + */ + remove: function (api) { + this.clear(api); + }, + + /** + * @implement + */ + dispose: function (api) { + this.clear(api); + }, + + /** + * @protected + */ + determineAnimation: function (axisModel, axisPointerModel) { + var animation = axisPointerModel.get('animation'); + var axis = axisModel.axis; + var isCategoryAxis = axis.type === 'category'; + var useSnap = axisPointerModel.get('snap'); + + // Value axis without snap always do not snap. + if (!useSnap && !isCategoryAxis) { + return false; + } + + if (animation === 'auto' || animation == null) { + var animationThreshold = this.animationThreshold; + if (isCategoryAxis && axis.getBandWidth() > animationThreshold) { + return true; + } + + // It is important to auto animation when snap used. Consider if there is + // a dataZoom, animation will be disabled when too many points exist, while + // it will be enabled for better visual effect when little points exist. + if (useSnap) { + var seriesDataCount = getAxisInfo(axisModel).seriesDataCount; + var axisExtent = axis.getExtent(); + // Approximate band width + return Math.abs(axisExtent[0] - axisExtent[1]) / seriesDataCount > animationThreshold; + } + + return false; + } + + return animation === true; + }, + + /** + * add {pointer, label, graphicKey} to elOption + * @protected + */ + makeElOption: function (elOption, value, axisModel, axisPointerModel, api) { + // Shoule be implemenented by sub-class. + }, + + /** + * @protected + */ + createPointerEl: function (group, elOption, axisModel, axisPointerModel) { + var pointerOption = elOption.pointer; + if (pointerOption) { + var pointerEl = inner$9(group).pointerEl = new graphic[pointerOption.type]( + clone$4(elOption.pointer) + ); + group.add(pointerEl); + } + }, + + /** + * @protected + */ + createLabelEl: function (group, elOption, axisModel, axisPointerModel) { + if (elOption.label) { + var labelEl = inner$9(group).labelEl = new Rect( + clone$4(elOption.label) + ); + + group.add(labelEl); + updateLabelShowHide(labelEl, axisPointerModel); + } + }, + + /** + * @protected + */ + updatePointerEl: function (group, elOption, updateProps$$1) { + var pointerEl = inner$9(group).pointerEl; + if (pointerEl) { + pointerEl.setStyle(elOption.pointer.style); + updateProps$$1(pointerEl, {shape: elOption.pointer.shape}); + } + }, + + /** + * @protected + */ + updateLabelEl: function (group, elOption, updateProps$$1, axisPointerModel) { + var labelEl = inner$9(group).labelEl; + if (labelEl) { + labelEl.setStyle(elOption.label.style); + updateProps$$1(labelEl, { + // Consider text length change in vertical axis, animation should + // be used on shape, otherwise the effect will be weird. + shape: elOption.label.shape, + position: elOption.label.position + }); + + updateLabelShowHide(labelEl, axisPointerModel); + } + }, + + /** + * @private + */ + _renderHandle: function (value) { + if (this._dragging || !this.updateHandleTransform) { + return; + } + + var axisPointerModel = this._axisPointerModel; + var zr = this._api.getZr(); + var handle = this._handle; + var handleModel = axisPointerModel.getModel('handle'); + + var status = axisPointerModel.get('status'); + if (!handleModel.get('show') || !status || status === 'hide') { + handle && zr.remove(handle); + this._handle = null; + return; + } + + var isInit; + if (!this._handle) { + isInit = true; + handle = this._handle = createIcon( + handleModel.get('icon'), + { + cursor: 'move', + draggable: true, + onmousemove: function (e) { + // Fot mobile devicem, prevent screen slider on the button. + stop(e.event); + }, + onmousedown: bind$2(this._onHandleDragMove, this, 0, 0), + drift: bind$2(this._onHandleDragMove, this), + ondragend: bind$2(this._onHandleDragEnd, this) + } + ); + zr.add(handle); + } + + updateMandatoryProps(handle, axisPointerModel, false); + + // update style + var includeStyles = [ + 'color', 'borderColor', 'borderWidth', 'opacity', + 'shadowColor', 'shadowBlur', 'shadowOffsetX', 'shadowOffsetY' + ]; + handle.setStyle(handleModel.getItemStyle(null, includeStyles)); + + // update position + var handleSize = handleModel.get('size'); + if (!isArray(handleSize)) { + handleSize = [handleSize, handleSize]; + } + handle.attr('scale', [handleSize[0] / 2, handleSize[1] / 2]); + + createOrUpdate( + this, + '_doDispatchAxisPointer', + handleModel.get('throttle') || 0, + 'fixRate' + ); + + this._moveHandleToValue(value, isInit); + }, + + /** + * @private + */ + _moveHandleToValue: function (value, isInit) { + updateProps$1( + this._axisPointerModel, + !isInit && this._moveAnimation, + this._handle, + getHandleTransProps(this.getHandleTransform( + value, this._axisModel, this._axisPointerModel + )) + ); + }, + + /** + * @private + */ + _onHandleDragMove: function (dx, dy) { + var handle = this._handle; + if (!handle) { + return; + } + + this._dragging = true; + + // Persistent for throttle. + var trans = this.updateHandleTransform( + getHandleTransProps(handle), + [dx, dy], + this._axisModel, + this._axisPointerModel + ); + this._payloadInfo = trans; + + handle.stopAnimation(); + handle.attr(getHandleTransProps(trans)); + inner$9(handle).lastProp = null; + + this._doDispatchAxisPointer(); + }, + + /** + * Throttled method. + * @private + */ + _doDispatchAxisPointer: function () { + var handle = this._handle; + if (!handle) { + return; + } + + var payloadInfo = this._payloadInfo; + var axisModel = this._axisModel; + this._api.dispatchAction({ + type: 'updateAxisPointer', + x: payloadInfo.cursorPoint[0], + y: payloadInfo.cursorPoint[1], + tooltipOption: payloadInfo.tooltipOption, + axesInfo: [{ + axisDim: axisModel.axis.dim, + axisIndex: axisModel.componentIndex + }] + }); + }, + + /** + * @private + */ + _onHandleDragEnd: function (moveAnimation) { + this._dragging = false; + var handle = this._handle; + if (!handle) { + return; + } + + var value = this._axisPointerModel.get('value'); + // Consider snap or categroy axis, handle may be not consistent with + // axisPointer. So move handle to align the exact value position when + // drag ended. + this._moveHandleToValue(value); + + // For the effect: tooltip will be shown when finger holding on handle + // button, and will be hidden after finger left handle button. + this._api.dispatchAction({ + type: 'hideTip' + }); + }, + + /** + * Should be implemenented by sub-class if support `handle`. + * @protected + * @param {number} value + * @param {module:echarts/model/Model} axisModel + * @param {module:echarts/model/Model} axisPointerModel + * @return {Object} {position: [x, y], rotation: 0} + */ + getHandleTransform: null, + + /** + * * Should be implemenented by sub-class if support `handle`. + * @protected + * @param {Object} transform {position, rotation} + * @param {Array.} delta [dx, dy] + * @param {module:echarts/model/Model} axisModel + * @param {module:echarts/model/Model} axisPointerModel + * @return {Object} {position: [x, y], rotation: 0, cursorPoint: [x, y]} + */ + updateHandleTransform: null, + + /** + * @private + */ + clear: function (api) { + this._lastValue = null; + this._lastStatus = null; + + var zr = api.getZr(); + var group = this._group; + var handle = this._handle; + if (zr && group) { + this._lastGraphicKey = null; + group && zr.remove(group); + handle && zr.remove(handle); + this._group = null; + this._handle = null; + this._payloadInfo = null; + } + }, + + /** + * @protected + */ + doClear: function () { + // Implemented by sub-class if necessary. + }, + + /** + * @protected + * @param {Array.} xy + * @param {Array.} wh + * @param {number} [xDimIndex=0] or 1 + */ + buildLabel: function (xy, wh, xDimIndex) { + xDimIndex = xDimIndex || 0; + return { + x: xy[xDimIndex], + y: xy[1 - xDimIndex], + width: wh[xDimIndex], + height: wh[1 - xDimIndex] + }; + } +}; + +BaseAxisPointer.prototype.constructor = BaseAxisPointer; + + +function updateProps$1(animationModel, moveAnimation, el, props) { + // Animation optimize. + if (!propsEqual(inner$9(el).lastProp, props)) { + inner$9(el).lastProp = props; + moveAnimation + ? updateProps(el, props, animationModel) + : (el.stopAnimation(), el.attr(props)); + } +} + +function propsEqual(lastProps, newProps) { + if (isObject$1(lastProps) && isObject$1(newProps)) { + var equals = true; + each$1(newProps, function (item, key) { + equals = equals && propsEqual(lastProps[key], item); + }); + return !!equals; + } + else { + return lastProps === newProps; + } +} + +function updateLabelShowHide(labelEl, axisPointerModel) { + labelEl[axisPointerModel.get('label.show') ? 'show' : 'hide'](); +} + +function getHandleTransProps(trans) { + return { + position: trans.position.slice(), + rotation: trans.rotation || 0 + }; +} + +function updateMandatoryProps(group, axisPointerModel, silent) { + var z = axisPointerModel.get('z'); + var zlevel = axisPointerModel.get('zlevel'); + + group && group.traverse(function (el) { + if (el.type !== 'group') { + z != null && (el.z = z); + zlevel != null && (el.zlevel = zlevel); + el.silent = silent; + } + }); +} + +enableClassExtend(BaseAxisPointer); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * @param {module:echarts/model/Model} axisPointerModel + */ +function buildElStyle(axisPointerModel) { + var axisPointerType = axisPointerModel.get('type'); + var styleModel = axisPointerModel.getModel(axisPointerType + 'Style'); + var style; + if (axisPointerType === 'line') { + style = styleModel.getLineStyle(); + style.fill = null; + } + else if (axisPointerType === 'shadow') { + style = styleModel.getAreaStyle(); + style.stroke = null; + } + return style; +} + +/** + * @param {Function} labelPos {align, verticalAlign, position} + */ +function buildLabelElOption( + elOption, axisModel, axisPointerModel, api, labelPos +) { + var value = axisPointerModel.get('value'); + var text = getValueLabel( + value, axisModel.axis, axisModel.ecModel, + axisPointerModel.get('seriesDataIndices'), + { + precision: axisPointerModel.get('label.precision'), + formatter: axisPointerModel.get('label.formatter') + } + ); + var labelModel = axisPointerModel.getModel('label'); + var paddings = normalizeCssArray$1(labelModel.get('padding') || 0); + + var font = labelModel.getFont(); + var textRect = getBoundingRect(text, font); + + var position = labelPos.position; + var width = textRect.width + paddings[1] + paddings[3]; + var height = textRect.height + paddings[0] + paddings[2]; + + // Adjust by align. + var align = labelPos.align; + align === 'right' && (position[0] -= width); + align === 'center' && (position[0] -= width / 2); + var verticalAlign = labelPos.verticalAlign; + verticalAlign === 'bottom' && (position[1] -= height); + verticalAlign === 'middle' && (position[1] -= height / 2); + + // Not overflow ec container + confineInContainer(position, width, height, api); + + var bgColor = labelModel.get('backgroundColor'); + if (!bgColor || bgColor === 'auto') { + bgColor = axisModel.get('axisLine.lineStyle.color'); + } + + elOption.label = { + shape: {x: 0, y: 0, width: width, height: height, r: labelModel.get('borderRadius')}, + position: position.slice(), + // TODO: rich + style: { + text: text, + textFont: font, + textFill: labelModel.getTextColor(), + textPosition: 'inside', + fill: bgColor, + stroke: labelModel.get('borderColor') || 'transparent', + lineWidth: labelModel.get('borderWidth') || 0, + shadowBlur: labelModel.get('shadowBlur'), + shadowColor: labelModel.get('shadowColor'), + shadowOffsetX: labelModel.get('shadowOffsetX'), + shadowOffsetY: labelModel.get('shadowOffsetY') + }, + // Lable should be over axisPointer. + z2: 10 + }; +} + +// Do not overflow ec container +function confineInContainer(position, width, height, api) { + var viewWidth = api.getWidth(); + var viewHeight = api.getHeight(); + position[0] = Math.min(position[0] + width, viewWidth) - width; + position[1] = Math.min(position[1] + height, viewHeight) - height; + position[0] = Math.max(position[0], 0); + position[1] = Math.max(position[1], 0); +} + +/** + * @param {number} value + * @param {module:echarts/coord/Axis} axis + * @param {module:echarts/model/Global} ecModel + * @param {Object} opt + * @param {Array.} seriesDataIndices + * @param {number|string} opt.precision 'auto' or a number + * @param {string|Function} opt.formatter label formatter + */ +function getValueLabel(value, axis, ecModel, seriesDataIndices, opt) { + value = axis.scale.parse(value); + var text = axis.scale.getLabel( + // If `precision` is set, width can be fixed (like '12.00500'), which + // helps to debounce when when moving label. + value, {precision: opt.precision} + ); + var formatter = opt.formatter; + + if (formatter) { + var params = { + value: getAxisRawValue(axis, value), + seriesData: [] + }; + each$1(seriesDataIndices, function (idxItem) { + var series = ecModel.getSeriesByIndex(idxItem.seriesIndex); + var dataIndex = idxItem.dataIndexInside; + var dataParams = series && series.getDataParams(dataIndex); + dataParams && params.seriesData.push(dataParams); + }); + + if (isString(formatter)) { + text = formatter.replace('{value}', text); + } + else if (isFunction$1(formatter)) { + text = formatter(params); + } + } + + return text; +} + +/** + * @param {module:echarts/coord/Axis} axis + * @param {number} value + * @param {Object} layoutInfo { + * rotation, position, labelOffset, labelDirection, labelMargin + * } + */ +function getTransformedPosition (axis, value, layoutInfo) { + var transform = create$1(); + rotate(transform, transform, layoutInfo.rotation); + translate(transform, transform, layoutInfo.position); + + return applyTransform$1([ + axis.dataToCoord(value), + (layoutInfo.labelOffset || 0) + + (layoutInfo.labelDirection || 1) * (layoutInfo.labelMargin || 0) + ], transform); +} + +function buildCartesianSingleLabelElOption( + value, elOption, layoutInfo, axisModel, axisPointerModel, api +) { + var textLayout = AxisBuilder.innerTextLayout( + layoutInfo.rotation, 0, layoutInfo.labelDirection + ); + layoutInfo.labelMargin = axisPointerModel.get('label.margin'); + buildLabelElOption(elOption, axisModel, axisPointerModel, api, { + position: getTransformedPosition(axisModel.axis, value, layoutInfo), + align: textLayout.textAlign, + verticalAlign: textLayout.textVerticalAlign + }); +} + +/** + * @param {Array.} p1 + * @param {Array.} p2 + * @param {number} [xDimIndex=0] or 1 + */ +function makeLineShape(p1, p2, xDimIndex) { + xDimIndex = xDimIndex || 0; + return { + x1: p1[xDimIndex], + y1: p1[1 - xDimIndex], + x2: p2[xDimIndex], + y2: p2[1 - xDimIndex] + }; +} + +/** + * @param {Array.} xy + * @param {Array.} wh + * @param {number} [xDimIndex=0] or 1 + */ +function makeRectShape(xy, wh, xDimIndex) { + xDimIndex = xDimIndex || 0; + return { + x: xy[xDimIndex], + y: xy[1 - xDimIndex], + width: wh[xDimIndex], + height: wh[1 - xDimIndex] + }; +} + +function makeSectorShape(cx, cy, r0, r, startAngle, endAngle) { + return { + cx: cx, + cy: cy, + r0: r0, + r: r, + startAngle: startAngle, + endAngle: endAngle, + clockwise: true + }; +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var CartesianAxisPointer = BaseAxisPointer.extend({ + + /** + * @override + */ + makeElOption: function (elOption, value, axisModel, axisPointerModel, api) { + var axis = axisModel.axis; + var grid = axis.grid; + var axisPointerType = axisPointerModel.get('type'); + var otherExtent = getCartesian(grid, axis).getOtherAxis(axis).getGlobalExtent(); + var pixelValue = axis.toGlobalCoord(axis.dataToCoord(value, true)); + + if (axisPointerType && axisPointerType !== 'none') { + var elStyle = buildElStyle(axisPointerModel); + var pointerOption = pointerShapeBuilder[axisPointerType]( + axis, pixelValue, otherExtent, elStyle + ); + pointerOption.style = elStyle; + elOption.graphicKey = pointerOption.type; + elOption.pointer = pointerOption; + } + + var layoutInfo = layout$1(grid.model, axisModel); + buildCartesianSingleLabelElOption( + value, elOption, layoutInfo, axisModel, axisPointerModel, api + ); + }, + + /** + * @override + */ + getHandleTransform: function (value, axisModel, axisPointerModel) { + var layoutInfo = layout$1(axisModel.axis.grid.model, axisModel, { + labelInside: false + }); + layoutInfo.labelMargin = axisPointerModel.get('handle.margin'); + return { + position: getTransformedPosition(axisModel.axis, value, layoutInfo), + rotation: layoutInfo.rotation + (layoutInfo.labelDirection < 0 ? Math.PI : 0) + }; + }, + + /** + * @override + */ + updateHandleTransform: function (transform, delta, axisModel, axisPointerModel) { + var axis = axisModel.axis; + var grid = axis.grid; + var axisExtent = axis.getGlobalExtent(true); + var otherExtent = getCartesian(grid, axis).getOtherAxis(axis).getGlobalExtent(); + var dimIndex = axis.dim === 'x' ? 0 : 1; + + var currPosition = transform.position; + currPosition[dimIndex] += delta[dimIndex]; + currPosition[dimIndex] = Math.min(axisExtent[1], currPosition[dimIndex]); + currPosition[dimIndex] = Math.max(axisExtent[0], currPosition[dimIndex]); + + var cursorOtherValue = (otherExtent[1] + otherExtent[0]) / 2; + var cursorPoint = [cursorOtherValue, cursorOtherValue]; + cursorPoint[dimIndex] = currPosition[dimIndex]; + + // Make tooltip do not overlap axisPointer and in the middle of the grid. + var tooltipOptions = [{verticalAlign: 'middle'}, {align: 'center'}]; + + return { + position: currPosition, + rotation: transform.rotation, + cursorPoint: cursorPoint, + tooltipOption: tooltipOptions[dimIndex] + }; + } + +}); + +function getCartesian(grid, axis) { + var opt = {}; + opt[axis.dim + 'AxisIndex'] = axis.index; + return grid.getCartesian(opt); +} + +var pointerShapeBuilder = { + + line: function (axis, pixelValue, otherExtent, elStyle) { + var targetShape = makeLineShape( + [pixelValue, otherExtent[0]], + [pixelValue, otherExtent[1]], + getAxisDimIndex(axis) + ); + subPixelOptimizeLine({ + shape: targetShape, + style: elStyle + }); + return { + type: 'Line', + shape: targetShape + }; + }, + + shadow: function (axis, pixelValue, otherExtent, elStyle) { + var bandWidth = Math.max(1, axis.getBandWidth()); + var span = otherExtent[1] - otherExtent[0]; + return { + type: 'Rect', + shape: makeRectShape( + [pixelValue - bandWidth / 2, otherExtent[0]], + [bandWidth, span], + getAxisDimIndex(axis) + ) + }; + } +}; + +function getAxisDimIndex(axis) { + return axis.dim === 'x' ? 0 : 1; +} + +AxisView.registerAxisPointerClass('CartesianAxisPointer', CartesianAxisPointer); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +// CartesianAxisPointer is not supposed to be required here. But consider +// echarts.simple.js and online build tooltip, which only require gridSimple, +// CartesianAxisPointer should be able to required somewhere. +registerPreprocessor(function (option) { + // Always has a global axisPointerModel for default setting. + if (option) { + (!option.axisPointer || option.axisPointer.length === 0) + && (option.axisPointer = {}); + + var link = option.axisPointer.link; + // Normalize to array to avoid object mergin. But if link + // is not set, remain null/undefined, otherwise it will + // override existent link setting. + if (link && !isArray(link)) { + option.axisPointer.link = [link]; + } + } +}); + +// This process should proformed after coordinate systems created +// and series data processed. So put it on statistic processing stage. +registerProcessor(PRIORITY.PROCESSOR.STATISTIC, function (ecModel, api) { + // Build axisPointerModel, mergin tooltip.axisPointer model for each axis. + // allAxesInfo should be updated when setOption performed. + ecModel.getComponent('axisPointer').coordSysAxesInfo + = collect(ecModel, api); +}); + +// Broadcast to all views. +registerAction({ + type: 'updateAxisPointer', + event: 'updateAxisPointer', + update: ':updateAxisPointer' +}, axisTrigger); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var XY = ['x', 'y']; +var WH = ['width', 'height']; + +var SingleAxisPointer = BaseAxisPointer.extend({ + + /** + * @override + */ + makeElOption: function (elOption, value, axisModel, axisPointerModel, api) { + var axis = axisModel.axis; + var coordSys = axis.coordinateSystem; + var otherExtent = getGlobalExtent(coordSys, 1 - getPointDimIndex(axis)); + var pixelValue = coordSys.dataToPoint(value)[0]; + + var axisPointerType = axisPointerModel.get('type'); + if (axisPointerType && axisPointerType !== 'none') { + var elStyle = buildElStyle(axisPointerModel); + var pointerOption = pointerShapeBuilder$1[axisPointerType]( + axis, pixelValue, otherExtent, elStyle + ); + pointerOption.style = elStyle; + + elOption.graphicKey = pointerOption.type; + elOption.pointer = pointerOption; + } + + var layoutInfo = layout$2(axisModel); + buildCartesianSingleLabelElOption( + value, elOption, layoutInfo, axisModel, axisPointerModel, api + ); + }, + + /** + * @override + */ + getHandleTransform: function (value, axisModel, axisPointerModel) { + var layoutInfo = layout$2(axisModel, {labelInside: false}); + layoutInfo.labelMargin = axisPointerModel.get('handle.margin'); + return { + position: getTransformedPosition(axisModel.axis, value, layoutInfo), + rotation: layoutInfo.rotation + (layoutInfo.labelDirection < 0 ? Math.PI : 0) + }; + }, + + /** + * @override + */ + updateHandleTransform: function (transform, delta, axisModel, axisPointerModel) { + var axis = axisModel.axis; + var coordSys = axis.coordinateSystem; + var dimIndex = getPointDimIndex(axis); + var axisExtent = getGlobalExtent(coordSys, dimIndex); + var currPosition = transform.position; + currPosition[dimIndex] += delta[dimIndex]; + currPosition[dimIndex] = Math.min(axisExtent[1], currPosition[dimIndex]); + currPosition[dimIndex] = Math.max(axisExtent[0], currPosition[dimIndex]); + var otherExtent = getGlobalExtent(coordSys, 1 - dimIndex); + var cursorOtherValue = (otherExtent[1] + otherExtent[0]) / 2; + var cursorPoint = [cursorOtherValue, cursorOtherValue]; + cursorPoint[dimIndex] = currPosition[dimIndex]; + + return { + position: currPosition, + rotation: transform.rotation, + cursorPoint: cursorPoint, + tooltipOption: { + verticalAlign: 'middle' + } + }; + } +}); + +var pointerShapeBuilder$1 = { + + line: function (axis, pixelValue, otherExtent, elStyle) { + var targetShape = makeLineShape( + [pixelValue, otherExtent[0]], + [pixelValue, otherExtent[1]], + getPointDimIndex(axis) + ); + subPixelOptimizeLine({ + shape: targetShape, + style: elStyle + }); + return { + type: 'Line', + shape: targetShape + }; + }, + + shadow: function (axis, pixelValue, otherExtent, elStyle) { + var bandWidth = axis.getBandWidth(); + var span = otherExtent[1] - otherExtent[0]; + return { + type: 'Rect', + shape: makeRectShape( + [pixelValue - bandWidth / 2, otherExtent[0]], + [bandWidth, span], + getPointDimIndex(axis) + ) + }; + } +}; + +function getPointDimIndex(axis) { + return axis.isHorizontal() ? 0 : 1; +} + +function getGlobalExtent(coordSys, dimIndex) { + var rect = coordSys.getRect(); + return [rect[XY[dimIndex]], rect[XY[dimIndex]] + rect[WH[dimIndex]]]; +} + +AxisView.registerAxisPointerClass('SingleAxisPointer', SingleAxisPointer); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +extendComponentView({ + type: 'single' +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * @file Define the themeRiver view's series model + * @author Deqing Li(annong035@gmail.com) + */ + +var DATA_NAME_INDEX = 2; + +var ThemeRiverSeries = SeriesModel.extend({ + + type: 'series.themeRiver', + + dependencies: ['singleAxis'], + + /** + * @readOnly + * @type {module:zrender/core/util#HashMap} + */ + nameMap: null, + + /** + * @override + */ + init: function (option) { + ThemeRiverSeries.superApply(this, 'init', arguments); + + // Put this function here is for the sake of consistency of code style. + // Enable legend selection for each data item + // Use a function instead of direct access because data reference may changed + this.legendDataProvider = function () { + return this.getRawData(); + }; + }, + + /** + * If there is no value of a certain point in the time for some event,set it value to 0. + * + * @param {Array} data initial data in the option + * @return {Array} + */ + fixData: function (data) { + var rawDataLength = data.length; + + // grouped data by name + var dataByName = nest() + .key(function (dataItem) { + return dataItem[2]; + }) + .entries(data); + + // data group in each layer + var layData = map(dataByName, function (d) { + return { + name: d.key, + dataList: d.values + }; + }); + + var layerNum = layData.length; + var largestLayer = -1; + var index = -1; + for (var i = 0; i < layerNum; ++i) { + var len = layData[i].dataList.length; + if (len > largestLayer) { + largestLayer = len; + index = i; + } + } + + for (var k = 0; k < layerNum; ++k) { + if (k === index) { + continue; + } + var name = layData[k].name; + for (var j = 0; j < largestLayer; ++j) { + var timeValue = layData[index].dataList[j][0]; + var length = layData[k].dataList.length; + var keyIndex = -1; + for (var l = 0; l < length; ++l) { + var value = layData[k].dataList[l][0]; + if (value === timeValue) { + keyIndex = l; + break; + } + } + if (keyIndex === -1) { + data[rawDataLength] = []; + data[rawDataLength][0] = timeValue; + data[rawDataLength][1] = 0; + data[rawDataLength][2] = name; + rawDataLength++; + + } + } + } + return data; + }, + + /** + * @override + * @param {Object} option the initial option that user gived + * @param {module:echarts/model/Model} ecModel the model object for themeRiver option + * @return {module:echarts/data/List} + */ + getInitialData: function (option, ecModel) { + + var singleAxisModel = ecModel.queryComponents({ + mainType: 'singleAxis', + index: this.get('singleAxisIndex'), + id: this.get('singleAxisId') + })[0]; + + var axisType = singleAxisModel.get('type'); + + // filter the data item with the value of label is undefined + var filterData = filter(option.data, function (dataItem) { + return dataItem[2] !== undefined; + }); + + // ??? TODO design a stage to transfer data for themeRiver and lines? + var data = this.fixData(filterData || []); + var nameList = []; + var nameMap = this.nameMap = createHashMap(); + var count = 0; + + for (var i = 0; i < data.length; ++i) { + nameList.push(data[i][DATA_NAME_INDEX]); + if (!nameMap.get(data[i][DATA_NAME_INDEX])) { + nameMap.set(data[i][DATA_NAME_INDEX], count); + count++; + } + } + + var dimensionsInfo = createDimensions(data, { + coordDimensions: ['single'], + dimensionsDefine: [ + { + name: 'time', + type: getDimensionTypeByAxis(axisType) + }, + { + name: 'value', + type: 'float' + }, + { + name: 'name', + type: 'ordinal' + } + ], + encodeDefine: { + single: 0, + value: 1, + itemName: 2 + } + }); + + var list = new List(dimensionsInfo, this); + list.initData(data); + + return list; + }, + + /** + * The raw data is divided into multiple layers and each layer + * has same name. + * + * @return {Array.>} + */ + getLayerSeries: function () { + var data = this.getData(); + var lenCount = data.count(); + var indexArr = []; + + for (var i = 0; i < lenCount; ++i) { + indexArr[i] = i; + } + // data group by name + var dataByName = nest() + .key(function (index) { + return data.get('name', index); + }) + .entries(indexArr); + + var layerSeries = map(dataByName, function (d) { + return { + name: d.key, + indices: d.values + }; + }); + + var timeDim = data.mapDimension('single'); + + for (var j = 0; j < layerSeries.length; ++j) { + layerSeries[j].indices.sort(comparer); + } + + function comparer(index1, index2) { + return data.get(timeDim, index1) - data.get(timeDim, index2); + } + + return layerSeries; + }, + + /** + * Get data indices for show tooltip content + * + * @param {Array.|string} dim single coordinate dimension + * @param {number} value axis value + * @param {module:echarts/coord/single/SingleAxis} baseAxis single Axis used + * the themeRiver. + * @return {Object} {dataIndices, nestestValue} + */ + getAxisTooltipData: function (dim, value, baseAxis) { + if (!isArray(dim)) { + dim = dim ? [dim] : []; + } + + var data = this.getData(); + var layerSeries = this.getLayerSeries(); + var indices = []; + var layerNum = layerSeries.length; + var nestestValue; + + for (var i = 0; i < layerNum; ++i) { + var minDist = Number.MAX_VALUE; + var nearestIdx = -1; + var pointNum = layerSeries[i].indices.length; + for (var j = 0; j < pointNum; ++j) { + var theValue = data.get(dim[0], layerSeries[i].indices[j]); + var dist = Math.abs(theValue - value); + if (dist <= minDist) { + nestestValue = theValue; + minDist = dist; + nearestIdx = layerSeries[i].indices[j]; + } + } + indices.push(nearestIdx); + } + + return {dataIndices: indices, nestestValue: nestestValue}; + }, + + /** + * @override + * @param {number} dataIndex index of data + */ + formatTooltip: function (dataIndex) { + var data = this.getData(); + var htmlName = data.getName(dataIndex); + var htmlValue = data.get(data.mapDimension('value'), dataIndex); + if (isNaN(htmlValue) || htmlValue == null) { + htmlValue = '-'; + } + return encodeHTML(htmlName + ' : ' + htmlValue); + }, + + defaultOption: { + zlevel: 0, + z: 2, + + coordinateSystem: 'singleAxis', + + // gap in axis's orthogonal orientation + boundaryGap: ['10%', '10%'], + + // legendHoverLink: true, + + singleAxisIndex: 0, + + animationEasing: 'linear', + + label: { + margin: 4, + show: true, + position: 'left', + color: '#000', + fontSize: 11 + }, + + emphasis: { + label: { + show: true + } + } + } +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * @file The file used to draw themeRiver view + * @author Deqing Li(annong035@gmail.com) + */ + +extendChartView({ + + type: 'themeRiver', + + init: function () { + this._layers = []; + }, + + render: function (seriesModel, ecModel, api) { + var data = seriesModel.getData(); + + var group = this.group; + + var layerSeries = seriesModel.getLayerSeries(); + + var layoutInfo = data.getLayout('layoutInfo'); + var rect = layoutInfo.rect; + var boundaryGap = layoutInfo.boundaryGap; + + group.attr('position', [0, rect.y + boundaryGap[0]]); + + function keyGetter(item) { + return item.name; + } + var dataDiffer = new DataDiffer( + this._layersSeries || [], layerSeries, + keyGetter, keyGetter + ); + + var newLayersGroups = {}; + + dataDiffer + .add(bind(process, this, 'add')) + .update(bind(process, this, 'update')) + .remove(bind(process, this, 'remove')) + .execute(); + + function process(status, idx, oldIdx) { + var oldLayersGroups = this._layers; + if (status === 'remove') { + group.remove(oldLayersGroups[idx]); + return; + } + var points0 = []; + var points1 = []; + var color; + var indices = layerSeries[idx].indices; + for (var j = 0; j < indices.length; j++) { + var layout = data.getItemLayout(indices[j]); + var x = layout.x; + var y0 = layout.y0; + var y = layout.y; + + points0.push([x, y0]); + points1.push([x, y0 + y]); + + color = data.getItemVisual(indices[j], 'color'); + } + + var polygon; + var text; + var textLayout = data.getItemLayout(indices[0]); + var itemModel = data.getItemModel(indices[j - 1]); + var labelModel = itemModel.getModel('label'); + var margin = labelModel.get('margin'); + if (status === 'add') { + var layerGroup = newLayersGroups[idx] = new Group(); + polygon = new Polygon$1({ + shape: { + points: points0, + stackedOnPoints: points1, + smooth: 0.4, + stackedOnSmooth: 0.4, + smoothConstraint: false + }, + z2: 0 + }); + text = new Text({ + style: { + x: textLayout.x - margin, + y: textLayout.y0 + textLayout.y / 2 + } + }); + layerGroup.add(polygon); + layerGroup.add(text); + group.add(layerGroup); + + polygon.setClipPath(createGridClipShape$3(polygon.getBoundingRect(), seriesModel, function () { + polygon.removeClipPath(); + })); + } + else { + var layerGroup = oldLayersGroups[oldIdx]; + polygon = layerGroup.childAt(0); + text = layerGroup.childAt(1); + group.add(layerGroup); + + newLayersGroups[idx] = layerGroup; + + updateProps(polygon, { + shape: { + points: points0, + stackedOnPoints: points1 + } + }, seriesModel); + + updateProps(text, { + style: { + x: textLayout.x - margin, + y: textLayout.y0 + textLayout.y / 2 + } + }, seriesModel); + } + + var hoverItemStyleModel = itemModel.getModel('emphasis.itemStyle'); + var itemStyleModel = itemModel.getModel('itemStyle'); + + setTextStyle(text.style, labelModel, { + text: labelModel.get('show') + ? seriesModel.getFormattedLabel(indices[j - 1], 'normal') + || data.getName(indices[j - 1]) + : null, + textVerticalAlign: 'middle' + }); + + polygon.setStyle(extend({ + fill: color + }, itemStyleModel.getItemStyle(['color']))); + + setHoverStyle(polygon, hoverItemStyleModel.getItemStyle()); + } + + this._layersSeries = layerSeries; + this._layers = newLayersGroups; + }, + + dispose: function () {} +}); + +// add animation to the view +function createGridClipShape$3(rect, seriesModel, cb) { + var rectEl = new Rect({ + shape: { + x: rect.x - 10, + y: rect.y - 10, + width: 0, + height: rect.height + 20 + } + }); + initProps(rectEl, { + shape: { + width: rect.width + 20, + height: rect.height + 20 + } + }, seriesModel, cb); + + return rectEl; +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * @file Using layout algorithm transform the raw data to layout information. + * @author Deqing Li(annong035@gmail.com) + */ + +var themeRiverLayout = function (ecModel, api) { + + ecModel.eachSeriesByType('themeRiver', function (seriesModel) { + + var data = seriesModel.getData(); + + var single = seriesModel.coordinateSystem; + + var layoutInfo = {}; + + // use the axis boundingRect for view + var rect = single.getRect(); + + layoutInfo.rect = rect; + + var boundaryGap = seriesModel.get('boundaryGap'); + + var axis = single.getAxis(); + + layoutInfo.boundaryGap = boundaryGap; + + if (axis.orient === 'horizontal') { + boundaryGap[0] = parsePercent$1(boundaryGap[0], rect.height); + boundaryGap[1] = parsePercent$1(boundaryGap[1], rect.height); + var height = rect.height - boundaryGap[0] - boundaryGap[1]; + themeRiverLayout$1(data, seriesModel, height); + } + else { + boundaryGap[0] = parsePercent$1(boundaryGap[0], rect.width); + boundaryGap[1] = parsePercent$1(boundaryGap[1], rect.width); + var width = rect.width - boundaryGap[0] - boundaryGap[1]; + themeRiverLayout$1(data, seriesModel, width); + } + + data.setLayout('layoutInfo', layoutInfo); + }); +}; + +/** + * The layout information about themeriver + * + * @param {module:echarts/data/List} data data in the series + * @param {module:echarts/model/Series} seriesModel the model object of themeRiver series + * @param {number} height value used to compute every series height + */ +function themeRiverLayout$1(data, seriesModel, height) { + if (!data.count()) { + return; + } + var coordSys = seriesModel.coordinateSystem; + // the data in each layer are organized into a series. + var layerSeries = seriesModel.getLayerSeries(); + + // the points in each layer. + var timeDim = data.mapDimension('single'); + var valueDim = data.mapDimension('value'); + var layerPoints = map(layerSeries, function (singleLayer) { + return map(singleLayer.indices, function (idx) { + var pt = coordSys.dataToPoint(data.get(timeDim, idx)); + pt[1] = data.get(valueDim, idx); + return pt; + }); + }); + + var base = computeBaseline(layerPoints); + var baseLine = base.y0; + var ky = height / base.max; + + // set layout information for each item. + var n = layerSeries.length; + var m = layerSeries[0].indices.length; + var baseY0; + for (var j = 0; j < m; ++j) { + baseY0 = baseLine[j] * ky; + data.setItemLayout(layerSeries[0].indices[j], { + layerIndex: 0, + x: layerPoints[0][j][0], + y0: baseY0, + y: layerPoints[0][j][1] * ky + }); + for (var i = 1; i < n; ++i) { + baseY0 += layerPoints[i - 1][j][1] * ky; + data.setItemLayout(layerSeries[i].indices[j], { + layerIndex: i, + x: layerPoints[i][j][0], + y0: baseY0, + y: layerPoints[i][j][1] * ky + }); + } + } +} + +/** + * Compute the baseLine of the rawdata + * Inspired by Lee Byron's paper Stacked Graphs - Geometry & Aesthetics + * + * @param {Array.} data the points in each layer + * @return {Object} + */ +function computeBaseline(data) { + var layerNum = data.length; + var pointNum = data[0].length; + var sums = []; + var y0 = []; + var max = 0; + var temp; + var base = {}; + + for (var i = 0; i < pointNum; ++i) { + for (var j = 0, temp = 0; j < layerNum; ++j) { + temp += data[j][i][1]; + } + if (temp > max) { + max = temp; + } + sums.push(temp); + } + + for (var k = 0; k < pointNum; ++k) { + y0[k] = (max - sums[k]) / 2; + } + max = 0; + + for (var l = 0; l < pointNum; ++l) { + var sum = sums[l] + y0[l]; + if (sum > max) { + max = sum; + } + } + base.y0 = y0; + base.max = max; + + return base; +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * @file Visual encoding for themeRiver view + * @author Deqing Li(annong035@gmail.com) + */ + +var themeRiverVisual = function (ecModel) { + ecModel.eachSeriesByType('themeRiver', function (seriesModel) { + var data = seriesModel.getData(); + var rawData = seriesModel.getRawData(); + var colorList = seriesModel.get('color'); + var idxMap = createHashMap(); + + data.each(function (idx) { + idxMap.set(data.getRawIndex(idx), idx); + }); + + rawData.each(function (rawIndex) { + var name = rawData.getName(rawIndex); + var color = colorList[(seriesModel.nameMap.get(name) - 1) % colorList.length]; + + rawData.setItemVisual(rawIndex, 'color', color); + + var idx = idxMap.get(rawIndex); + + if (idx != null) { + data.setItemVisual(idx, 'color', color); + } + }); + }); +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +registerLayout(themeRiverLayout); +registerVisual(themeRiverVisual); +registerProcessor(dataFilter('themeRiver')); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +SeriesModel.extend({ + + type: 'series.sunburst', + + /** + * @type {module:echarts/data/Tree~Node} + */ + _viewRoot: null, + + getInitialData: function (option, ecModel) { + // Create a virtual root. + var root = { name: option.name, children: option.data }; + + completeTreeValue$1(root); + + var levels = option.levels || []; + + // levels = option.levels = setDefault(levels, ecModel); + + var treeOption = {}; + + treeOption.levels = levels; + + // 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, treeOption).data; + }, + + optionUpdated: function () { + this.resetViewRoot(); + }, + + /* + * @override + */ + getDataParams: function (dataIndex) { + var params = SeriesModel.prototype.getDataParams.apply(this, arguments); + + var node = this.getData().tree.getNodeByDataIndex(dataIndex); + params.treePathInfo = wrapTreePathInfo(node, this); + + return params; + }, + + defaultOption: { + zlevel: 0, + z: 2, + + // 默认全局居中 + center: ['50%', '50%'], + radius: [0, '75%'], + // 默认顺时针 + clockwise: true, + startAngle: 90, + // 最小角度改为0 + minAngle: 0, + + percentPrecision: 2, + + // If still show when all data zero. + stillShowZeroSum: true, + + // Policy of highlighting pieces when hover on one + // Valid values: 'none' (for not downplay others), 'descendant', + // 'ancestor', 'self' + highlightPolicy: 'descendant', + + // 'rootToNode', 'link', or false + nodeClick: 'rootToNode', + + renderLabelForZeroData: false, + + label: { + // could be: 'radial', 'tangential', or 'none' + rotate: 'radial', + show: true, + opacity: 1, + // 'left' is for inner side of inside, and 'right' is for outter + // side for inside + align: 'center', + position: 'inside', + distance: 5, + silent: true, + emphasis: {} + }, + itemStyle: { + borderWidth: 1, + borderColor: 'white', + opacity: 1, + emphasis: {}, + highlight: { + opacity: 1 + }, + downplay: { + opacity: 0.9 + } + }, + + // Animation type canbe expansion, scale + animationType: 'expansion', + animationDuration: 1000, + animationDurationUpdate: 500, + animationEasing: 'cubicOut', + + data: [], + + levels: [], + + /** + * Sort order. + * + * Valid values: 'desc', 'asc', null, or callback function. + * 'desc' and 'asc' for descend and ascendant order; + * null for not sorting; + * example of callback function: + * function(nodeA, nodeB) { + * return nodeA.getValue() - nodeB.getValue(); + * } + */ + sort: 'desc' + }, + + 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.getRawData().tree.root; + + if (!viewRoot + || (viewRoot !== root && !root.contains(viewRoot)) + ) { + this._viewRoot = root; + } + } +}); + + + +/** + * @param {Object} dataNode + */ +function completeTreeValue$1(dataNode) { + // 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; + + each$1(dataNode.children, function (child) { + + completeTreeValue$1(child); + + var childValue = child.value; + isArray(childValue) && (childValue = childValue[0]); + + sum += childValue; + }); + + var thisValue = dataNode.value; + if (isArray(thisValue)) { + thisValue = thisValue[0]; + } + + if (thisValue == null || isNaN(thisValue)) { + thisValue = sum; + } + // Value should not less than 0. + if (thisValue < 0) { + thisValue = 0; + } + + isArray(dataNode.value) + ? (dataNode.value[0] = thisValue) + : (dataNode.value = thisValue); +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var NodeHighlightPolicy = { + NONE: 'none', // not downplay others + DESCENDANT: 'descendant', + ANCESTOR: 'ancestor', + SELF: 'self' +}; + +var DEFAULT_SECTOR_Z = 2; +var DEFAULT_TEXT_Z = 4; + +/** + * Sunburstce of Sunburst including Sector, Label, LabelLine + * @constructor + * @extends {module:zrender/graphic/Group} + */ +function SunburstPiece(node, seriesModel, ecModel) { + + Group.call(this); + + var sector = new Sector({ + z2: DEFAULT_SECTOR_Z + }); + sector.seriesIndex = seriesModel.seriesIndex; + + var text = new Text({ + z2: DEFAULT_TEXT_Z, + silent: node.getModel('label').get('silent') + }); + this.add(sector); + this.add(text); + + this.updateData(true, node, 'normal', seriesModel, ecModel); + + // Hover to change label and labelLine + function onEmphasis() { + text.ignore = text.hoverIgnore; + } + function onNormal() { + text.ignore = text.normalIgnore; + } + this.on('emphasis', onEmphasis) + .on('normal', onNormal) + .on('mouseover', onEmphasis) + .on('mouseout', onNormal); +} + +var SunburstPieceProto = SunburstPiece.prototype; + +SunburstPieceProto.updateData = function ( + firstCreate, + node, + state, + seriesModel, + ecModel +) { + this.node = node; + node.piece = this; + + seriesModel = seriesModel || this._seriesModel; + ecModel = ecModel || this._ecModel; + + var sector = this.childAt(0); + sector.dataIndex = node.dataIndex; + + var itemModel = node.getModel(); + var layout = node.getLayout(); + if (!layout) { + console.log(node.getLayout()); + } + var sectorShape = extend({}, layout); + sectorShape.label = null; + + var visualColor = getNodeColor(node, seriesModel, ecModel); + + var normalStyle = itemModel.getModel('itemStyle').getItemStyle(); + var style; + if (state === 'normal') { + style = normalStyle; + } + else { + var stateStyle = itemModel.getModel(state + '.itemStyle') + .getItemStyle(); + style = merge(stateStyle, normalStyle); + } + style = defaults( + { + lineJoin: 'bevel', + fill: style.fill || visualColor + }, + style + ); + + if (firstCreate) { + sector.setShape(sectorShape); + sector.shape.r = layout.r0; + updateProps( + sector, + { + shape: { + r: layout.r + } + }, + seriesModel, + node.dataIndex + ); + sector.useStyle(style); + } + else if (typeof style.fill === 'object' && style.fill.type + || typeof sector.style.fill === 'object' && sector.style.fill.type + ) { + // Disable animation for gradient since no interpolation method + // is supported for gradient + updateProps(sector, { + shape: sectorShape + }, seriesModel); + sector.useStyle(style); + } + else { + updateProps(sector, { + shape: sectorShape, + style: style + }, seriesModel); + } + + this._updateLabel(seriesModel, visualColor, state); + + var cursorStyle = itemModel.getShallow('cursor'); + cursorStyle && sector.attr('cursor', cursorStyle); + + if (firstCreate) { + var highlightPolicy = seriesModel.getShallow('highlightPolicy'); + this._initEvents(sector, node, seriesModel, highlightPolicy); + } + + this._seriesModel = seriesModel || this._seriesModel; + this._ecModel = ecModel || this._ecModel; +}; + +SunburstPieceProto.onEmphasis = function (highlightPolicy) { + var that = this; + this.node.hostTree.root.eachNode(function (n) { + if (n.piece) { + if (that.node === n) { + n.piece.updateData(false, n, 'emphasis'); + } + else if (isNodeHighlighted(n, that.node, highlightPolicy)) { + n.piece.childAt(0).trigger('highlight'); + } + else if (highlightPolicy !== NodeHighlightPolicy.NONE) { + n.piece.childAt(0).trigger('downplay'); + } + } + }); +}; + +SunburstPieceProto.onNormal = function () { + this.node.hostTree.root.eachNode(function (n) { + if (n.piece) { + n.piece.updateData(false, n, 'normal'); + } + }); +}; + +SunburstPieceProto.onHighlight = function () { + this.updateData(false, this.node, 'highlight'); +}; + +SunburstPieceProto.onDownplay = function () { + this.updateData(false, this.node, 'downplay'); +}; + +SunburstPieceProto._updateLabel = function (seriesModel, visualColor, state) { + var itemModel = this.node.getModel(); + var normalModel = itemModel.getModel('label'); + var labelModel = state === 'normal' || state === 'emphasis' + ? normalModel + : itemModel.getModel(state + '.label'); + var labelHoverModel = itemModel.getModel('emphasis.label'); + + var text = retrieve( + seriesModel.getFormattedLabel( + this.node.dataIndex, 'normal', null, null, 'label' + ), + this.node.name + ); + if (getLabelAttr('show') === false) { + text = ''; + } + + var layout = this.node.getLayout(); + var labelMinAngle = labelModel.get('minAngle'); + if (labelMinAngle == null) { + labelMinAngle = normalModel.get('minAngle'); + } + labelMinAngle = labelMinAngle / 180 * Math.PI; + var angle = layout.endAngle - layout.startAngle; + if (labelMinAngle != null && Math.abs(angle) < labelMinAngle) { + // Not displaying text when angle is too small + text = ''; + } + + var label = this.childAt(1); + + setLabelStyle( + label.style, label.hoverStyle || {}, normalModel, labelHoverModel, + { + defaultText: labelModel.getShallow('show') ? text : null, + autoColor: visualColor, + useInsideStyle: true + } + ); + + var midAngle = (layout.startAngle + layout.endAngle) / 2; + var dx = Math.cos(midAngle); + var dy = Math.sin(midAngle); + + var r; + var labelPosition = getLabelAttr('position'); + var labelPadding = getLabelAttr('distance') || 0; + var textAlign = getLabelAttr('align'); + if (labelPosition === 'outside') { + r = layout.r + labelPadding; + textAlign = midAngle > Math.PI / 2 ? 'right' : 'left'; + } + else { + if (!textAlign || textAlign === 'center') { + r = (layout.r + layout.r0) / 2; + textAlign = 'center'; + } + else if (textAlign === 'left') { + r = layout.r0 + labelPadding; + if (midAngle > Math.PI / 2) { + textAlign = 'right'; + } + } + else if (textAlign === 'right') { + r = layout.r - labelPadding; + if (midAngle > Math.PI / 2) { + textAlign = 'left'; + } + } + } + + label.attr('style', { + text: text, + textAlign: textAlign, + textVerticalAlign: getLabelAttr('verticalAlign') || 'middle', + opacity: getLabelAttr('opacity') + }); + + var textX = r * dx + layout.cx; + var textY = r * dy + layout.cy; + label.attr('position', [textX, textY]); + + var rotateType = getLabelAttr('rotate'); + var rotate = 0; + if (rotateType === 'radial') { + rotate = -midAngle; + if (rotate < -Math.PI / 2) { + rotate += Math.PI; + } + } + else if (rotateType === 'tangential') { + rotate = Math.PI / 2 - midAngle; + if (rotate > Math.PI / 2) { + rotate -= Math.PI; + } + else if (rotate < -Math.PI / 2) { + rotate += Math.PI; + } + } else if (typeof rotateType === 'number') { + rotate = rotateType * Math.PI / 180; + } + label.attr('rotation', rotate); + + function getLabelAttr(name) { + var stateAttr = labelModel.get(name); + if (stateAttr == null) { + return normalModel.get(name); + } + else { + return stateAttr; + } + } +}; + +SunburstPieceProto._initEvents = function ( + sector, + node, + seriesModel, + highlightPolicy +) { + sector.off('mouseover').off('mouseout').off('emphasis').off('normal'); + + var that = this; + var onEmphasis = function () { + that.onEmphasis(highlightPolicy); + }; + var onNormal = function () { + that.onNormal(); + }; + var onDownplay = function () { + that.onDownplay(); + }; + var onHighlight = function () { + that.onHighlight(); + }; + + if (seriesModel.isAnimationEnabled()) { + sector + .on('mouseover', onEmphasis) + .on('mouseout', onNormal) + .on('emphasis', onEmphasis) + .on('normal', onNormal) + .on('downplay', onDownplay) + .on('highlight', onHighlight); + } +}; + +inherits(SunburstPiece, Group); + +/** + * Get node color + * + * @param {TreeNode} node the node to get color + * @param {module:echarts/model/Series} seriesModel series + * @param {module:echarts/model/Global} ecModel echarts defaults + */ +function getNodeColor(node, seriesModel, ecModel) { + // Color from visualMap + var visualColor = node.getVisual('color'); + var visualMetaList = node.getVisual('visualMeta'); + if (!visualMetaList || visualMetaList.length === 0) { + // Use first-generation color if has no visualMap + visualColor = null; + } + + // Self color or level color + var color = node.getModel('itemStyle').get('color'); + if (color) { + return color; + } + else if (visualColor) { + // Color mapping + return visualColor; + } + else if (node.depth === 0) { + // Virtual root node + return ecModel.option.color[0]; + } + else { + // First-generation color + var length = ecModel.option.color.length; + color = ecModel.option.color[getRootId(node) % length]; + } + return color; +} + +/** + * Get index of root in sorted order + * + * @param {TreeNode} node current node + * @return {number} index in root + */ +function getRootId(node) { + var ancestor = node; + while (ancestor.depth > 1) { + ancestor = ancestor.parentNode; + } + + var virtualRoot = node.getAncestors()[0]; + return indexOf(virtualRoot.children, ancestor); +} + +function isNodeHighlighted(node, activeNode, policy) { + if (policy === NodeHighlightPolicy.NONE) { + return false; + } + else if (policy === NodeHighlightPolicy.SELF) { + return node === activeNode; + } + else if (policy === NodeHighlightPolicy.ANCESTOR) { + return node === activeNode || node.isAncestorOf(activeNode); + } + else { + return node === activeNode || node.isDescendantOf(activeNode); + } +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var ROOT_TO_NODE_ACTION = 'sunburstRootToNode'; + +var SunburstView = Chart.extend({ + + type: 'sunburst', + + init: function () { + }, + + render: function (seriesModel, ecModel, api, payload) { + var that = this; + + this.seriesModel = seriesModel; + this.api = api; + this.ecModel = ecModel; + + var data = seriesModel.getData(); + var virtualRoot = data.tree.root; + + var newRoot = seriesModel.getViewRoot(); + + var group = this.group; + + var renderLabelForZeroData = seriesModel.get('renderLabelForZeroData'); + + var newChildren = []; + newRoot.eachNode(function (node) { + newChildren.push(node); + }); + var oldChildren = this._oldChildren || []; + + dualTravel(newChildren, oldChildren); + + renderRollUp(virtualRoot, newRoot); + + if (payload && payload.highlight && payload.highlight.piece) { + var highlightPolicy = seriesModel.getShallow('highlightPolicy'); + payload.highlight.piece.onEmphasis(highlightPolicy); + } + else if (payload && payload.unhighlight) { + var piece = this.virtualPiece; + if (!piece && virtualRoot.children.length) { + piece = virtualRoot.children[0].piece; + } + if (piece) { + piece.onNormal(); + } + } + + this._initEvents(); + + this._oldChildren = newChildren; + + function dualTravel(newChildren, oldChildren) { + if (newChildren.length === 0 && oldChildren.length === 0) { + return; + } + + new DataDiffer(oldChildren, newChildren, getKey, getKey) + .add(processNode) + .update(processNode) + .remove(curry(processNode, null)) + .execute(); + + function getKey(node) { + return node.getId(); + } + + function processNode(newId, oldId) { + var newNode = newId == null ? null : newChildren[newId]; + var oldNode = oldId == null ? null : oldChildren[oldId]; + + doRenderNode(newNode, oldNode); + } + } + + function doRenderNode(newNode, oldNode) { + if (!renderLabelForZeroData && newNode && !newNode.getValue()) { + // Not render data with value 0 + newNode = null; + } + + if (newNode !== virtualRoot && oldNode !== virtualRoot) { + if (oldNode && oldNode.piece) { + if (newNode) { + // Update + oldNode.piece.updateData( + false, newNode, 'normal', seriesModel, ecModel); + + // For tooltip + data.setItemGraphicEl(newNode.dataIndex, oldNode.piece); + } + else { + // Remove + removeNode(oldNode); + } + } + else if (newNode) { + // Add + var piece = new SunburstPiece( + newNode, + seriesModel, + ecModel + ); + group.add(piece); + + // For tooltip + data.setItemGraphicEl(newNode.dataIndex, piece); + } + } + } + + function removeNode(node) { + if (!node) { + return; + } + + if (node.piece) { + group.remove(node.piece); + node.piece = null; + } + } + + function renderRollUp(virtualRoot, viewRoot) { + if (viewRoot.depth > 0) { + // Render + if (that.virtualPiece) { + // Update + that.virtualPiece.updateData( + false, virtualRoot, 'normal', seriesModel, ecModel); + } + else { + // Add + that.virtualPiece = new SunburstPiece( + virtualRoot, + seriesModel, + ecModel + ); + group.add(that.virtualPiece); + } + + if (viewRoot.piece._onclickEvent) { + viewRoot.piece.off('click', viewRoot.piece._onclickEvent); + } + var event = function (e) { + that._rootToNode(viewRoot.parentNode); + }; + viewRoot.piece._onclickEvent = event; + that.virtualPiece.on('click', event); + } + else if (that.virtualPiece) { + // Remove + group.remove(that.virtualPiece); + that.virtualPiece = null; + } + } + }, + + dispose: function () { + }, + + /** + * @private + */ + _initEvents: function () { + var that = this; + + var event = function (e) { + var targetFound = false; + var viewRoot = that.seriesModel.getViewRoot(); + viewRoot.eachNode(function (node) { + if (!targetFound + && node.piece && node.piece.childAt(0) === e.target + ) { + var nodeClick = node.getModel().get('nodeClick'); + if (nodeClick === 'rootToNode') { + that._rootToNode(node); + } + else if (nodeClick === 'link') { + var itemModel = node.getModel(); + var link = itemModel.get('link'); + if (link) { + var linkTarget = itemModel.get('target', true) + || '_blank'; + window.open(link, linkTarget); + } + } + targetFound = true; + } + }); + }; + + if (this.group._onclickEvent) { + this.group.off('click', this.group._onclickEvent); + } + this.group.on('click', event); + this.group._onclickEvent = event; + }, + + /** + * @private + */ + _rootToNode: function (node) { + if (node !== this.seriesModel.getViewRoot()) { + this.api.dispatchAction({ + type: ROOT_TO_NODE_ACTION, + from: this.uid, + seriesId: this.seriesModel.id, + targetNode: node + }); + } + }, + + /** + * @implement + */ + containPoint: function (point, seriesModel) { + var treeRoot = seriesModel.getData(); + var itemLayout = treeRoot.getItemLayout(0); + if (itemLayout) { + var dx = point[0] - itemLayout.cx; + var dy = point[1] - itemLayout.cy; + var radius = Math.sqrt(dx * dx + dy * dy); + return radius <= itemLayout.r && radius >= itemLayout.r0; + } + } + +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * @file Sunburst action + */ + +var ROOT_TO_NODE_ACTION$1 = 'sunburstRootToNode'; + +registerAction( + {type: ROOT_TO_NODE_ACTION$1, update: 'updateView'}, + function (payload, ecModel) { + + ecModel.eachComponent( + {mainType: 'series', subType: 'sunburst', query: payload}, + handleRootToNode + ); + + function handleRootToNode(model, index) { + var targetInfo = retrieveTargetInfo(payload, [ROOT_TO_NODE_ACTION$1], model); + + if (targetInfo) { + var originViewRoot = model.getViewRoot(); + if (originViewRoot) { + payload.direction = aboveViewRoot(originViewRoot, targetInfo.node) + ? 'rollUp' : 'drillDown'; + } + model.resetViewRoot(targetInfo.node); + } + } + } +); + + +var HIGHLIGHT_ACTION = 'sunburstHighlight'; + +registerAction( + {type: HIGHLIGHT_ACTION, update: 'updateView'}, + function (payload, ecModel) { + + ecModel.eachComponent( + {mainType: 'series', subType: 'sunburst', query: payload}, + handleHighlight + ); + + function handleHighlight(model, index) { + var targetInfo = retrieveTargetInfo(payload, [HIGHLIGHT_ACTION], model); + + if (targetInfo) { + payload.highlight = targetInfo.node; + } + } + } +); + + +var UNHIGHLIGHT_ACTION = 'sunburstUnhighlight'; + +registerAction( + {type: UNHIGHLIGHT_ACTION, update: 'updateView'}, + function (payload, ecModel) { + + ecModel.eachComponent( + {mainType: 'series', subType: 'sunburst', query: payload}, + handleUnhighlight + ); + + function handleUnhighlight(model, index) { + payload.unhighlight = true; + } + } +); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + + +var RADIAN$1 = Math.PI / 180; + +var sunburstLayout = function (seriesType, ecModel, api, payload) { + ecModel.eachSeriesByType(seriesType, function (seriesModel) { + var center = seriesModel.get('center'); + var radius = seriesModel.get('radius'); + + if (!isArray(radius)) { + radius = [0, radius]; + } + if (!isArray(center)) { + center = [center, center]; + } + + var width = api.getWidth(); + var height = api.getHeight(); + var size = Math.min(width, height); + var cx = parsePercent$1(center[0], width); + var cy = parsePercent$1(center[1], height); + var r0 = parsePercent$1(radius[0], size / 2); + var r = parsePercent$1(radius[1], size / 2); + + var startAngle = -seriesModel.get('startAngle') * RADIAN$1; + var minAngle = seriesModel.get('minAngle') * RADIAN$1; + + var virtualRoot = seriesModel.getData().tree.root; + var treeRoot = seriesModel.getViewRoot(); + var rootDepth = treeRoot.depth; + + var sort = seriesModel.get('sort'); + if (sort != null) { + initChildren$1(treeRoot, sort); + } + + var validDataCount = 0; + each$1(treeRoot.children, function (child) { + !isNaN(child.getValue()) && validDataCount++; + }); + + var sum = treeRoot.getValue(); + // Sum may be 0 + var unitRadian = Math.PI / (sum || validDataCount) * 2; + + var renderRollupNode = treeRoot.depth > 0; + var levels = treeRoot.height - (renderRollupNode ? -1 : 1); + var rPerLevel = (r - r0) / (levels || 1); + + var clockwise = seriesModel.get('clockwise'); + + var stillShowZeroSum = seriesModel.get('stillShowZeroSum'); + + // In the case some sector angle is smaller than minAngle + var dir = clockwise ? 1 : -1; + + /** + * Render a tree + * @return increased angle + */ + var renderNode = function (node, startAngle) { + if (!node) { + return; + } + + var endAngle = startAngle; + + // Render self + if (node !== virtualRoot) { + // Tree node is virtual, so it doesn't need to be drawn + var value = node.getValue(); + + var angle = (sum === 0 && stillShowZeroSum) + ? unitRadian : (value * unitRadian); + if (angle < minAngle) { + angle = minAngle; + + } + else { + + } + + endAngle = startAngle + dir * angle; + + var depth = node.depth - rootDepth + - (renderRollupNode ? -1 : 1); + var rStart = r0 + rPerLevel * depth; + var rEnd = r0 + rPerLevel * (depth + 1); + + var itemModel = node.getModel(); + if (itemModel.get('r0') != null) { + rStart = parsePercent$1(itemModel.get('r0'), size / 2); + } + if (itemModel.get('r') != null) { + rEnd = parsePercent$1(itemModel.get('r'), size / 2); + } + + node.setLayout({ + angle: angle, + startAngle: startAngle, + endAngle: endAngle, + clockwise: clockwise, + cx: cx, + cy: cy, + r0: rStart, + r: rEnd + }); + } + + // Render children + if (node.children && node.children.length) { + // currentAngle = startAngle; + var siblingAngle = 0; + each$1(node.children, function (node) { + siblingAngle += renderNode(node, startAngle + siblingAngle); + }); + } + + return endAngle - startAngle; + }; + + // Virtual root node for roll up + if (renderRollupNode) { + var rStart = r0; + var rEnd = r0 + rPerLevel; + + var angle = Math.PI * 2; + virtualRoot.setLayout({ + angle: angle, + startAngle: startAngle, + endAngle: startAngle + angle, + clockwise: clockwise, + cx: cx, + cy: cy, + r0: rStart, + r: rEnd + }); + } + + renderNode(treeRoot, startAngle); + }); +}; + +/** + * Init node children by order and update visual + * + * @param {TreeNode} node root node + * @param {boolean} isAsc if is in ascendant order + */ +function initChildren$1(node, isAsc) { + var children = node.children || []; + + node.children = sort$2(children, isAsc); + + // Init children recursively + if (children.length) { + each$1(node.children, function (child) { + initChildren$1(child, isAsc); + }); + } +} + +/** + * Sort children nodes + * + * @param {TreeNode[]} children children of node to be sorted + * @param {string | function | null} sort sort method + * See SunburstSeries.js for details. + */ +function sort$2(children, sortOrder) { + if (typeof sortOrder === 'function') { + return children.sort(sortOrder); + } + else { + var isAsc = sortOrder === 'asc'; + return children.sort(function (a, b) { + var diff = (a.getValue() - b.getValue()) * (isAsc ? 1 : -1); + return diff === 0 + ? (a.dataIndex - b.dataIndex) * (isAsc ? -1 : 1) + : diff; + }); + } +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +registerVisual(curry(dataColor, 'sunburst')); +registerLayout(curry(sunburstLayout, 'sunburst')); +registerProcessor(curry(dataFilter, 'sunburst')); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +function dataToCoordSize(dataSize, dataItem) { + // dataItem is necessary in log axis. + dataItem = dataItem || [0, 0]; + return map(['x', 'y'], function (dim, dimIdx) { + var axis = this.getAxis(dim); + var val = dataItem[dimIdx]; + var halfSize = dataSize[dimIdx] / 2; + return axis.type === 'category' + ? axis.getBandWidth() + : Math.abs(axis.dataToCoord(val - halfSize) - axis.dataToCoord(val + halfSize)); + }, this); +} + +var prepareCartesian2d = function (coordSys) { + var rect = coordSys.grid.getRect(); + return { + coordSys: { + // The name exposed to user is always 'cartesian2d' but not 'grid'. + type: 'cartesian2d', + x: rect.x, + y: rect.y, + width: rect.width, + height: rect.height + }, + api: { + coord: function (data) { + // do not provide "out" param + return coordSys.dataToPoint(data); + }, + size: bind(dataToCoordSize, coordSys) + } + }; +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +function dataToCoordSize$1(dataSize, dataItem) { + dataItem = dataItem || [0, 0]; + return map([0, 1], function (dimIdx) { + var val = dataItem[dimIdx]; + var halfSize = dataSize[dimIdx] / 2; + var p1 = []; + var p2 = []; + p1[dimIdx] = val - halfSize; + p2[dimIdx] = val + halfSize; + p1[1 - dimIdx] = p2[1 - dimIdx] = dataItem[1 - dimIdx]; + return Math.abs(this.dataToPoint(p1)[dimIdx] - this.dataToPoint(p2)[dimIdx]); + }, this); +} + +var prepareGeo = function (coordSys) { + var rect = coordSys.getBoundingRect(); + return { + coordSys: { + type: 'geo', + x: rect.x, + y: rect.y, + width: rect.width, + height: rect.height + }, + api: { + coord: function (data) { + // do not provide "out" and noRoam param, + // Compatible with this usage: + // echarts.util.map(item.points, api.coord) + return coordSys.dataToPoint(data); + }, + size: bind(dataToCoordSize$1, coordSys) + } + }; +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +function dataToCoordSize$2(dataSize, dataItem) { + // dataItem is necessary in log axis. + var axis = this.getAxis(); + var val = dataItem instanceof Array ? dataItem[0] : dataItem; + var halfSize = (dataSize instanceof Array ? dataSize[0] : dataSize) / 2; + return axis.type === 'category' + ? axis.getBandWidth() + : Math.abs(axis.dataToCoord(val - halfSize) - axis.dataToCoord(val + halfSize)); +} + +var prepareSingleAxis = function (coordSys) { + var rect = coordSys.getRect(); + + return { + coordSys: { + type: 'singleAxis', + x: rect.x, + y: rect.y, + width: rect.width, + height: rect.height + }, + api: { + coord: function (val) { + // do not provide "out" param + return coordSys.dataToPoint(val); + }, + size: bind(dataToCoordSize$2, coordSys) + } + }; +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +function dataToCoordSize$3(dataSize, dataItem) { + // dataItem is necessary in log axis. + return map(['Radius', 'Angle'], function (dim, dimIdx) { + var axis = this['get' + dim + 'Axis'](); + var val = dataItem[dimIdx]; + var halfSize = dataSize[dimIdx] / 2; + var method = 'dataTo' + dim; + + var result = axis.type === 'category' + ? axis.getBandWidth() + : Math.abs(axis[method](val - halfSize) - axis[method](val + halfSize)); + + if (dim === 'Angle') { + result = result * Math.PI / 180; + } + + return result; + + }, this); +} + +var preparePolar = function (coordSys) { + var radiusAxis = coordSys.getRadiusAxis(); + var angleAxis = coordSys.getAngleAxis(); + var radius = radiusAxis.getExtent(); + radius[0] > radius[1] && radius.reverse(); + + return { + coordSys: { + type: 'polar', + cx: coordSys.cx, + cy: coordSys.cy, + r: radius[1], + r0: radius[0] + }, + api: { + coord: bind(function (data) { + var radius = radiusAxis.dataToRadius(data[0]); + var angle = angleAxis.dataToAngle(data[1]); + var coord = coordSys.coordToPoint([radius, angle]); + coord.push(radius, angle * Math.PI / 180); + return coord; + }), + size: bind(dataToCoordSize$3, coordSys) + } + }; +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var prepareCalendar = function (coordSys) { + var rect = coordSys.getRect(); + var rangeInfo = coordSys.getRangeInfo(); + + return { + coordSys: { + type: 'calendar', + x: rect.x, + y: rect.y, + width: rect.width, + height: rect.height, + cellWidth: coordSys.getCellWidth(), + cellHeight: coordSys.getCellHeight(), + rangeInfo: { + start: rangeInfo.start, + end: rangeInfo.end, + weeks: rangeInfo.weeks, + dayCount: rangeInfo.allDay + } + }, + api: { + coord: function (data, clamp) { + return coordSys.dataToPoint(data, clamp); + } + } + }; +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var ITEM_STYLE_NORMAL_PATH = ['itemStyle']; +var ITEM_STYLE_EMPHASIS_PATH = ['emphasis', 'itemStyle']; +var LABEL_NORMAL = ['label']; +var LABEL_EMPHASIS = ['emphasis', 'label']; +// Use prefix to avoid index to be the same as el.name, +// which will cause weird udpate animation. +var GROUP_DIFF_PREFIX = 'e\0\0'; + +/** + * To reduce total package size of each coordinate systems, the modules `prepareCustom` + * of each coordinate systems are not required by each coordinate systems directly, but + * required by the module `custom`. + * + * prepareInfoForCustomSeries {Function}: optional + * @return {Object} {coordSys: {...}, api: { + * coord: function (data, clamp) {}, // return point in global. + * size: function (dataSize, dataItem) {} // return size of each axis in coordSys. + * }} + */ +var prepareCustoms = { + cartesian2d: prepareCartesian2d, + geo: prepareGeo, + singleAxis: prepareSingleAxis, + polar: preparePolar, + calendar: prepareCalendar +}; + +// ------ +// Model +// ------ + +extendSeriesModel({ + + type: 'series.custom', + + dependencies: ['grid', 'polar', 'geo', 'singleAxis', 'calendar'], + + defaultOption: { + coordinateSystem: 'cartesian2d', // Can be set as 'none' + zlevel: 0, + z: 2, + legendHoverLink: true + + // Cartesian coordinate system + // xAxisIndex: 0, + // yAxisIndex: 0, + + // Polar coordinate system + // polarIndex: 0, + + // Geo coordinate system + // geoIndex: 0, + + // label: {} + // itemStyle: {} + }, + + getInitialData: function (option, ecModel) { + return createListFromArray(this.getSource(), this); + } +}); + +// ----- +// View +// ----- + +extendChartView({ + + type: 'custom', + + /** + * @private + * @type {module:echarts/data/List} + */ + _data: null, + + /** + * @override + */ + render: function (customSeries, ecModel, api) { + var oldData = this._data; + var data = customSeries.getData(); + var group = this.group; + var renderItem = makeRenderItem(customSeries, data, ecModel, api); + + this.group.removeAll(); + + data.diff(oldData) + .add(function (newIdx) { + createOrUpdate$1( + null, newIdx, renderItem(newIdx), customSeries, group, data + ); + }) + .update(function (newIdx, oldIdx) { + var el = oldData.getItemGraphicEl(oldIdx); + createOrUpdate$1( + el, newIdx, renderItem(newIdx), customSeries, group, data + ); + }) + .remove(function (oldIdx) { + var el = oldData.getItemGraphicEl(oldIdx); + el && group.remove(el); + }) + .execute(); + + this._data = data; + }, + + incrementalPrepareRender: function (customSeries, ecModel, api) { + this.group.removeAll(); + this._data = null; + }, + + incrementalRender: function (params, customSeries, ecModel, api) { + var data = customSeries.getData(); + var renderItem = makeRenderItem(customSeries, data, ecModel, api); + function setIncrementalAndHoverLayer(el) { + if (!el.isGroup) { + el.incremental = true; + el.useHoverLayer = true; + } + } + for (var idx = params.start; idx < params.end; idx++) { + var el = createOrUpdate$1(null, idx, renderItem(idx), customSeries, this.group, data); + el.traverse(setIncrementalAndHoverLayer); + } + }, + + /** + * @override + */ + dispose: noop +}); + + +function createEl(elOption) { + var graphicType = elOption.type; + var el; + + if (graphicType === 'path') { + var shape = elOption.shape; + el = makePath( + shape.pathData, + null, + { + x: shape.x || 0, + y: shape.y || 0, + width: shape.width || 0, + height: shape.height || 0 + }, + 'center' + ); + el.__customPathData = elOption.pathData; + } + else if (graphicType === 'image') { + el = new ZImage({ + }); + el.__customImagePath = elOption.style.image; + } + else if (graphicType === 'text') { + el = new Text({ + }); + el.__customText = elOption.style.text; + } + else { + var Clz = graphic[graphicType.charAt(0).toUpperCase() + graphicType.slice(1)]; + + if (__DEV__) { + assert$1(Clz, 'graphic type "' + graphicType + '" can not be found.'); + } + + el = new Clz(); + } + + el.__customGraphicType = graphicType; + el.name = elOption.name; + + return el; +} + +function updateEl(el, dataIndex, elOption, animatableModel, data, isInit) { + var targetProps = {}; + var elOptionStyle = elOption.style || {}; + + elOption.shape && (targetProps.shape = clone(elOption.shape)); + elOption.position && (targetProps.position = elOption.position.slice()); + elOption.scale && (targetProps.scale = elOption.scale.slice()); + elOption.origin && (targetProps.origin = elOption.origin.slice()); + elOption.rotation && (targetProps.rotation = elOption.rotation); + + if (el.type === 'image' && elOption.style) { + var targetStyle = targetProps.style = {}; + each$1(['x', 'y', 'width', 'height'], function (prop) { + prepareStyleTransition(prop, targetStyle, elOptionStyle, el.style, isInit); + }); + } + + if (el.type === 'text' && elOption.style) { + var targetStyle = targetProps.style = {}; + each$1(['x', 'y'], function (prop) { + prepareStyleTransition(prop, targetStyle, elOptionStyle, el.style, isInit); + }); + // Compatible with previous: both support + // textFill and fill, textStroke and stroke in 'text' element. + !elOptionStyle.hasOwnProperty('textFill') && elOptionStyle.fill && ( + elOptionStyle.textFill = elOptionStyle.fill + ); + !elOptionStyle.hasOwnProperty('textStroke') && elOptionStyle.stroke && ( + elOptionStyle.textStroke = elOptionStyle.stroke + ); + } + + if (el.type !== 'group') { + el.useStyle(elOptionStyle); + + // Init animation. + if (isInit) { + el.style.opacity = 0; + var targetOpacity = elOptionStyle.opacity; + targetOpacity == null && (targetOpacity = 1); + initProps(el, {style: {opacity: targetOpacity}}, animatableModel, dataIndex); + } + } + + if (isInit) { + el.attr(targetProps); + } + else { + updateProps(el, targetProps, animatableModel, dataIndex); + } + + // z2 must not be null/undefined, otherwise sort error may occur. + el.attr({z2: elOption.z2 || 0, silent: elOption.silent}); + + elOption.styleEmphasis !== false && setHoverStyle(el, elOption.styleEmphasis); +} + +function prepareStyleTransition(prop, targetStyle, elOptionStyle, oldElStyle, isInit) { + if (elOptionStyle[prop] != null && !isInit) { + targetStyle[prop] = elOptionStyle[prop]; + elOptionStyle[prop] = oldElStyle[prop]; + } +} + +function makeRenderItem(customSeries, data, ecModel, api) { + var renderItem = customSeries.get('renderItem'); + var coordSys = customSeries.coordinateSystem; + var prepareResult = {}; + + if (coordSys) { + if (__DEV__) { + assert$1(renderItem, 'series.render is required.'); + assert$1( + coordSys.prepareCustoms || prepareCustoms[coordSys.type], + 'This coordSys does not support custom series.' + ); + } + + prepareResult = coordSys.prepareCustoms + ? coordSys.prepareCustoms() + : prepareCustoms[coordSys.type](coordSys); + } + + var userAPI = defaults({ + getWidth: api.getWidth, + getHeight: api.getHeight, + getZr: api.getZr, + getDevicePixelRatio: api.getDevicePixelRatio, + value: value, + style: style, + styleEmphasis: styleEmphasis, + visual: visual, + barLayout: barLayout, + currentSeriesIndices: currentSeriesIndices, + font: font + }, prepareResult.api || {}); + + var userParams = { + context: {}, + seriesId: customSeries.id, + seriesName: customSeries.name, + seriesIndex: customSeries.seriesIndex, + coordSys: prepareResult.coordSys, + dataInsideLength: data.count(), + encode: wrapEncodeDef(customSeries.getData()) + }; + + // Do not support call `api` asynchronously without dataIndexInside input. + var currDataIndexInside; + var currDirty = true; + var currItemModel; + var currLabelNormalModel; + var currLabelEmphasisModel; + var currVisualColor; + + return function (dataIndexInside) { + currDataIndexInside = dataIndexInside; + currDirty = true; + return renderItem && renderItem( + defaults({ + dataIndexInside: dataIndexInside, + dataIndex: data.getRawIndex(dataIndexInside) + }, userParams), + userAPI + ) || {}; + }; + + // Do not update cache until api called. + function updateCache(dataIndexInside) { + dataIndexInside == null && (dataIndexInside = currDataIndexInside); + if (currDirty) { + currItemModel = data.getItemModel(dataIndexInside); + currLabelNormalModel = currItemModel.getModel(LABEL_NORMAL); + currLabelEmphasisModel = currItemModel.getModel(LABEL_EMPHASIS); + currVisualColor = data.getItemVisual(dataIndexInside, 'color'); + + currDirty = false; + } + } + + /** + * @public + * @param {number|string} dim + * @param {number} [dataIndexInside=currDataIndexInside] + * @return {number|string} value + */ + function value(dim, dataIndexInside) { + dataIndexInside == null && (dataIndexInside = currDataIndexInside); + return data.get(data.getDimension(dim || 0), dataIndexInside); + } + + /** + * By default, `visual` is applied to style (to support visualMap). + * `visual.color` is applied at `fill`. If user want apply visual.color on `stroke`, + * it can be implemented as: + * `api.style({stroke: api.visual('color'), fill: null})`; + * @public + * @param {Object} [extra] + * @param {number} [dataIndexInside=currDataIndexInside] + */ + function style(extra, dataIndexInside) { + dataIndexInside == null && (dataIndexInside = currDataIndexInside); + updateCache(dataIndexInside); + + var itemStyle = currItemModel.getModel(ITEM_STYLE_NORMAL_PATH).getItemStyle(); + + currVisualColor != null && (itemStyle.fill = currVisualColor); + var opacity = data.getItemVisual(dataIndexInside, 'opacity'); + opacity != null && (itemStyle.opacity = opacity); + + setTextStyle(itemStyle, currLabelNormalModel, null, { + autoColor: currVisualColor, + isRectText: true + }); + + itemStyle.text = currLabelNormalModel.getShallow('show') + ? retrieve2( + customSeries.getFormattedLabel(dataIndexInside, 'normal'), + getDefaultLabel(data, dataIndexInside) + ) + : null; + + extra && extend(itemStyle, extra); + return itemStyle; + } + + /** + * @public + * @param {Object} [extra] + * @param {number} [dataIndexInside=currDataIndexInside] + */ + function styleEmphasis(extra, dataIndexInside) { + dataIndexInside == null && (dataIndexInside = currDataIndexInside); + updateCache(dataIndexInside); + + var itemStyle = currItemModel.getModel(ITEM_STYLE_EMPHASIS_PATH).getItemStyle(); + + setTextStyle(itemStyle, currLabelEmphasisModel, null, { + isRectText: true + }, true); + + itemStyle.text = currLabelEmphasisModel.getShallow('show') + ? retrieve3( + customSeries.getFormattedLabel(dataIndexInside, 'emphasis'), + customSeries.getFormattedLabel(dataIndexInside, 'normal'), + getDefaultLabel(data, dataIndexInside) + ) + : null; + + extra && extend(itemStyle, extra); + return itemStyle; + } + + /** + * @public + * @param {string} visualType + * @param {number} [dataIndexInside=currDataIndexInside] + */ + function visual(visualType, dataIndexInside) { + dataIndexInside == null && (dataIndexInside = currDataIndexInside); + return data.getItemVisual(dataIndexInside, visualType); + } + + /** + * @public + * @param {number} opt.count Positive interger. + * @param {number} [opt.barWidth] + * @param {number} [opt.barMaxWidth] + * @param {number} [opt.barGap] + * @param {number} [opt.barCategoryGap] + * @return {Object} {width, offset, offsetCenter} is not support, return undefined. + */ + function barLayout(opt) { + if (coordSys.getBaseAxis) { + var baseAxis = coordSys.getBaseAxis(); + return getLayoutOnAxis(defaults({axis: baseAxis}, opt), api); + } + } + + /** + * @public + * @return {Array.} + */ + function currentSeriesIndices() { + return ecModel.getCurrentSeriesIndices(); + } + + /** + * @public + * @param {Object} opt + * @param {string} [opt.fontStyle] + * @param {number} [opt.fontWeight] + * @param {number} [opt.fontSize] + * @param {string} [opt.fontFamily] + * @return {string} font string + */ + function font(opt) { + return getFont(opt, ecModel); + } +} + +function wrapEncodeDef(data) { + var encodeDef = {}; + each$1(data.dimensions, function (dimName, dataDimIndex) { + var dimInfo = data.getDimensionInfo(dimName); + if (!dimInfo.isExtraCoord) { + var coordDim = dimInfo.coordDim; + var dataDims = encodeDef[coordDim] = encodeDef[coordDim] || []; + dataDims[dimInfo.coordDimIndex] = dataDimIndex; + } + }); + return encodeDef; +} + +function createOrUpdate$1(el, dataIndex, elOption, animatableModel, group, data) { + el = doCreateOrUpdate(el, dataIndex, elOption, animatableModel, group, data); + el && data.setItemGraphicEl(dataIndex, el); + + return el; +} + +function doCreateOrUpdate(el, dataIndex, elOption, animatableModel, group, data) { + var elOptionType = elOption.type; + if (el + && elOptionType !== el.__customGraphicType + && (elOptionType !== 'path' || elOption.pathData !== el.__customPathData) + && (elOptionType !== 'image' || elOption.style.image !== el.__customImagePath) + && (elOptionType !== 'text' || elOption.style.text !== el.__customText) + ) { + group.remove(el); + el = null; + } + + // `elOption.type` is undefined when `renderItem` returns nothing. + if (elOptionType == null) { + return; + } + + var isInit = !el; + !el && (el = createEl(elOption)); + updateEl(el, dataIndex, elOption, animatableModel, data, isInit); + + if (elOptionType === 'group') { + var oldChildren = el.children() || []; + var newChildren = elOption.children || []; + + if (elOption.diffChildrenByName) { + // lower performance. + diffGroupChildren({ + oldChildren: oldChildren, + newChildren: newChildren, + dataIndex: dataIndex, + animatableModel: animatableModel, + group: el, + data: data + }); + } + else { + // better performance. + var index = 0; + for (; index < newChildren.length; index++) { + doCreateOrUpdate( + el.childAt(index), + dataIndex, + newChildren[index], + animatableModel, + el, + data + ); + } + for (; index < oldChildren.length; index++) { + oldChildren[index] && el.remove(oldChildren[index]); + } + } + } + + group.add(el); + + return el; +} + +function diffGroupChildren(context) { + (new DataDiffer( + context.oldChildren, + context.newChildren, + getKey, + getKey, + context + )) + .add(processAddUpdate) + .update(processAddUpdate) + .remove(processRemove) + .execute(); +} + +function getKey(item, idx) { + var name = item && item.name; + return name != null ? name : GROUP_DIFF_PREFIX + idx; +} + +function processAddUpdate(newIndex, oldIndex) { + var context = this.context; + var childOption = newIndex != null ? context.newChildren[newIndex] : null; + var child = oldIndex != null ? context.oldChildren[oldIndex] : null; + + doCreateOrUpdate( + child, + context.dataIndex, + childOption, + context.animatableModel, + context.group, + context.data + ); +} + +function processRemove(oldIndex) { + var context = this.context; + var child = context.oldChildren[oldIndex]; + child && context.group.remove(child); +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +// ------------- +// Preprocessor +// ------------- + +registerPreprocessor(function (option) { + var graphicOption = option.graphic; + + // Convert + // {graphic: [{left: 10, type: 'circle'}, ...]} + // or + // {graphic: {left: 10, type: 'circle'}} + // to + // {graphic: [{elements: [{left: 10, type: 'circle'}, ...]}]} + if (isArray(graphicOption)) { + if (!graphicOption[0] || !graphicOption[0].elements) { + option.graphic = [{elements: graphicOption}]; + } + else { + // Only one graphic instance can be instantiated. (We dont + // want that too many views are created in echarts._viewMap) + option.graphic = [option.graphic[0]]; + } + } + else if (graphicOption && !graphicOption.elements) { + option.graphic = [{elements: [graphicOption]}]; + } +}); + +// ------ +// Model +// ------ + +var GraphicModel = extendComponentModel({ + + type: 'graphic', + + defaultOption: { + + // Extra properties for each elements: + // + // left/right/top/bottom: (like 12, '22%', 'center', default undefined) + // If left/rigth is set, shape.x/shape.cx/position will not be used. + // If top/bottom is set, shape.y/shape.cy/position will not be used. + // This mechanism is useful when you want to position a group/element + // against the right side or the center of this container. + // + // width/height: (can only be pixel value, default 0) + // Only be used to specify contianer(group) size, if needed. And + // can not be percentage value (like '33%'). See the reason in the + // layout algorithm below. + // + // bounding: (enum: 'all' (default) | 'raw') + // Specify how to calculate boundingRect when locating. + // 'all': Get uioned and transformed boundingRect + // from both itself and its descendants. + // This mode simplies confining a group of elements in the bounding + // of their ancester container (e.g., using 'right: 0'). + // 'raw': Only use the boundingRect of itself and before transformed. + // This mode is similar to css behavior, which is useful when you + // want an element to be able to overflow its container. (Consider + // a rotated circle needs to be located in a corner.) + + // Note: elements is always behind its ancestors in this elements array. + elements: [], + parentId: null + }, + + /** + * Save el options for the sake of the performance (only update modified graphics). + * The order is the same as those in option. (ancesters -> descendants) + * + * @private + * @type {Array.} + */ + _elOptionsToUpdate: null, + + /** + * @override + */ + mergeOption: function (option) { + // Prevent default merge to elements + var elements = this.option.elements; + this.option.elements = null; + + GraphicModel.superApply(this, 'mergeOption', arguments); + + this.option.elements = elements; + }, + + /** + * @override + */ + optionUpdated: function (newOption, isInit) { + var thisOption = this.option; + var newList = (isInit ? thisOption : newOption).elements; + var existList = thisOption.elements = isInit ? [] : thisOption.elements; + + var flattenedList = []; + this._flatten(newList, flattenedList); + + var mappingResult = mappingToExists(existList, flattenedList); + makeIdAndName(mappingResult); + + // Clear elOptionsToUpdate + var elOptionsToUpdate = this._elOptionsToUpdate = []; + + each$1(mappingResult, function (resultItem, index) { + var newElOption = resultItem.option; + + if (__DEV__) { + assert$1( + isObject$1(newElOption) || resultItem.exist, + 'Empty graphic option definition' + ); + } + + if (!newElOption) { + return; + } + + elOptionsToUpdate.push(newElOption); + + setKeyInfoToNewElOption(resultItem, newElOption); + + mergeNewElOptionToExist(existList, index, newElOption); + + setLayoutInfoToExist(existList[index], newElOption); + + }, this); + + // Clean + for (var i = existList.length - 1; i >= 0; i--) { + if (existList[i] == null) { + existList.splice(i, 1); + } + else { + // $action should be volatile, otherwise option gotten from + // `getOption` will contain unexpected $action. + delete existList[i].$action; + } + } + }, + + /** + * Convert + * [{ + * type: 'group', + * id: 'xx', + * children: [{type: 'circle'}, {type: 'polygon'}] + * }] + * to + * [ + * {type: 'group', id: 'xx'}, + * {type: 'circle', parentId: 'xx'}, + * {type: 'polygon', parentId: 'xx'} + * ] + * + * @private + * @param {Array.} optionList option list + * @param {Array.} result result of flatten + * @param {Object} parentOption parent option + */ + _flatten: function (optionList, result, parentOption) { + each$1(optionList, function (option) { + if (!option) { + return; + } + + if (parentOption) { + option.parentOption = parentOption; + } + + result.push(option); + + var children = option.children; + if (option.type === 'group' && children) { + this._flatten(children, result, option); + } + // Deleting for JSON output, and for not affecting group creation. + delete option.children; + }, this); + }, + + // FIXME + // Pass to view using payload? setOption has a payload? + useElOptionsToUpdate: function () { + var els = this._elOptionsToUpdate; + // Clear to avoid render duplicately when zooming. + this._elOptionsToUpdate = null; + return els; + } +}); + +// ----- +// View +// ----- + +extendComponentView({ + + type: 'graphic', + + /** + * @override + */ + init: function (ecModel, api) { + + /** + * @private + * @type {module:zrender/core/util.HashMap} + */ + this._elMap = createHashMap(); + + /** + * @private + * @type {module:echarts/graphic/GraphicModel} + */ + this._lastGraphicModel; + }, + + /** + * @override + */ + render: function (graphicModel, ecModel, api) { + + // Having leveraged between use cases and algorithm complexity, a very + // simple layout mechanism is used: + // The size(width/height) can be determined by itself or its parent (not + // implemented yet), but can not by its children. (Top-down travel) + // The location(x/y) can be determined by the bounding rect of itself + // (can including its descendants or not) and the size of its parent. + // (Bottom-up travel) + + // When `chart.clear()` or `chart.setOption({...}, true)` with the same id, + // view will be reused. + if (graphicModel !== this._lastGraphicModel) { + this._clear(); + } + this._lastGraphicModel = graphicModel; + + this._updateElements(graphicModel, api); + this._relocate(graphicModel, api); + }, + + /** + * Update graphic elements. + * + * @private + * @param {Object} graphicModel graphic model + * @param {module:echarts/ExtensionAPI} api extension API + */ + _updateElements: function (graphicModel, api) { + var elOptionsToUpdate = graphicModel.useElOptionsToUpdate(); + + if (!elOptionsToUpdate) { + return; + } + + var elMap = this._elMap; + var rootGroup = this.group; + + // Top-down tranverse to assign graphic settings to each elements. + each$1(elOptionsToUpdate, function (elOption) { + var $action = elOption.$action; + var id = elOption.id; + var existEl = elMap.get(id); + var parentId = elOption.parentId; + var targetElParent = parentId != null ? elMap.get(parentId) : rootGroup; + + if (elOption.type === 'text') { + var elOptionStyle = elOption.style; + + // In top/bottom mode, textVerticalAlign should not be used, which cause + // inaccurately locating. + if (elOption.hv && elOption.hv[1]) { + elOptionStyle.textVerticalAlign = elOptionStyle.textBaseline = null; + } + + // Compatible with previous setting: both support fill and textFill, + // stroke and textStroke. + !elOptionStyle.hasOwnProperty('textFill') && elOptionStyle.fill && ( + elOptionStyle.textFill = elOptionStyle.fill + ); + !elOptionStyle.hasOwnProperty('textStroke') && elOptionStyle.stroke && ( + elOptionStyle.textStroke = elOptionStyle.stroke + ); + } + + // Remove unnecessary props to avoid potential problems. + var elOptionCleaned = getCleanedElOption(elOption); + + // For simple, do not support parent change, otherwise reorder is needed. + if (__DEV__) { + existEl && assert$1( + targetElParent === existEl.parent, + 'Changing parent is not supported.' + ); + } + + if (!$action || $action === 'merge') { + existEl + ? existEl.attr(elOptionCleaned) + : createEl$1(id, targetElParent, elOptionCleaned, elMap); + } + else if ($action === 'replace') { + removeEl(existEl, elMap); + createEl$1(id, targetElParent, elOptionCleaned, elMap); + } + else if ($action === 'remove') { + removeEl(existEl, elMap); + } + + var el = elMap.get(id); + if (el) { + el.__ecGraphicWidth = elOption.width; + el.__ecGraphicHeight = elOption.height; + } + }); + }, + + /** + * Locate graphic elements. + * + * @private + * @param {Object} graphicModel graphic model + * @param {module:echarts/ExtensionAPI} api extension API + */ + _relocate: function (graphicModel, api) { + var elOptions = graphicModel.option.elements; + var rootGroup = this.group; + var elMap = this._elMap; + + // Bottom-up tranvese all elements (consider ec resize) to locate elements. + for (var i = elOptions.length - 1; i >= 0; i--) { + var elOption = elOptions[i]; + var el = elMap.get(elOption.id); + + if (!el) { + continue; + } + + var parentEl = el.parent; + var containerInfo = parentEl === rootGroup + ? { + width: api.getWidth(), + height: api.getHeight() + } + : { // Like 'position:absolut' in css, default 0. + width: parentEl.__ecGraphicWidth || 0, + height: parentEl.__ecGraphicHeight || 0 + }; + + positionElement( + el, elOption, containerInfo, null, + {hv: elOption.hv, boundingMode: elOption.bounding} + ); + } + }, + + /** + * Clear all elements. + * + * @private + */ + _clear: function () { + var elMap = this._elMap; + elMap.each(function (el) { + removeEl(el, elMap); + }); + this._elMap = createHashMap(); + }, + + /** + * @override + */ + dispose: function () { + this._clear(); + } +}); + +function createEl$1(id, targetElParent, elOption, elMap) { + var graphicType = elOption.type; + + if (__DEV__) { + assert$1(graphicType, 'graphic type MUST be set'); + } + + var Clz = graphic[graphicType.charAt(0).toUpperCase() + graphicType.slice(1)]; + + if (__DEV__) { + assert$1(Clz, 'graphic type can not be found'); + } + + var el = new Clz(elOption); + targetElParent.add(el); + elMap.set(id, el); + el.__ecGraphicId = id; +} + +function removeEl(existEl, elMap) { + var existElParent = existEl && existEl.parent; + if (existElParent) { + existEl.type === 'group' && existEl.traverse(function (el) { + removeEl(el, elMap); + }); + elMap.removeKey(existEl.__ecGraphicId); + existElParent.remove(existEl); + } +} + +// Remove unnecessary props to avoid potential problems. +function getCleanedElOption(elOption) { + elOption = extend({}, elOption); + each$1( + ['id', 'parentId', '$action', 'hv', 'bounding'].concat(LOCATION_PARAMS), + function (name) { + delete elOption[name]; + } + ); + return elOption; +} + +function isSetLoc(obj, props) { + var isSet; + each$1(props, function (prop) { + obj[prop] != null && obj[prop] !== 'auto' && (isSet = true); + }); + return isSet; +} + +function setKeyInfoToNewElOption(resultItem, newElOption) { + var existElOption = resultItem.exist; + + // Set id and type after id assigned. + newElOption.id = resultItem.keyInfo.id; + !newElOption.type && existElOption && (newElOption.type = existElOption.type); + + // Set parent id if not specified + if (newElOption.parentId == null) { + var newElParentOption = newElOption.parentOption; + if (newElParentOption) { + newElOption.parentId = newElParentOption.id; + } + else if (existElOption) { + newElOption.parentId = existElOption.parentId; + } + } + + // Clear + newElOption.parentOption = null; +} + +function mergeNewElOptionToExist(existList, index, newElOption) { + // Update existing options, for `getOption` feature. + var newElOptCopy = extend({}, newElOption); + var existElOption = existList[index]; + + var $action = newElOption.$action || 'merge'; + if ($action === 'merge') { + if (existElOption) { + + if (__DEV__) { + var newType = newElOption.type; + assert$1( + !newType || existElOption.type === newType, + 'Please set $action: "replace" to change `type`' + ); + } + + // We can ensure that newElOptCopy and existElOption are not + // the same object, so `merge` will not change newElOptCopy. + merge(existElOption, newElOptCopy, true); + // Rigid body, use ignoreSize. + mergeLayoutParam(existElOption, newElOptCopy, {ignoreSize: true}); + // Will be used in render. + copyLayoutParams(newElOption, existElOption); + } + else { + existList[index] = newElOptCopy; + } + } + else if ($action === 'replace') { + existList[index] = newElOptCopy; + } + else if ($action === 'remove') { + // null will be cleaned later. + existElOption && (existList[index] = null); + } +} + +function setLayoutInfoToExist(existItem, newElOption) { + if (!existItem) { + return; + } + existItem.hv = newElOption.hv = [ + // Rigid body, dont care `width`. + isSetLoc(newElOption, ['left', 'right']), + // Rigid body, dont care `height`. + isSetLoc(newElOption, ['top', 'bottom']) + ]; + // Give default group size. Otherwise layout error may occur. + if (existItem.type === 'group') { + existItem.width == null && (existItem.width = newElOption.width = 0); + existItem.height == null && (existItem.height = newElOption.height = 0); + } +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var LegendModel = extendComponentModel({ + + type: 'legend.plain', + + dependencies: ['series'], + + layoutMode: { + type: 'box', + // legend.width/height are maxWidth/maxHeight actually, + // whereas realy width/height is calculated by its content. + // (Setting {left: 10, right: 10} does not make sense). + // So consider the case: + // `setOption({legend: {left: 10});` + // then `setOption({legend: {right: 10});` + // The previous `left` should be cleared by setting `ignoreSize`. + 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 potentialData = []; + var availableNames = []; + + ecModel.eachRawSeries(function (seriesModel) { + var seriesName = seriesModel.name; + availableNames.push(seriesName); + var isPotential; + + if (seriesModel.legendDataProvider) { + var data = seriesModel.legendDataProvider(); + var names = data.mapArray(data.getName); + + if (!ecModel.isSeriesFiltered(seriesModel)) { + availableNames = availableNames.concat(names); + } + + if (names.length) { + potentialData = potentialData.concat(names); + } + else { + isPotential = true; + } + } + else { + isPotential = true; + } + + if (isPotential && isNameSpecified(seriesModel)) { + potentialData.push(seriesModel.name); + } + }); + + /** + * @type {Array.} + * @private + */ + this._availableNames = availableNames; + + // If legend.data not specified in option, use availableNames as data, + // which is convinient for user preparing option. + var rawData = this.get('data') || potentialData; + + var legendData = map(rawData, 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); + + /** + * @type {Array.} + * @private + */ + this._data = legendData; + }, + + /** + * @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; + each$1(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 (!selected.hasOwnProperty(name)) { + selected[name] = true; + } + this[selected[name] ? 'unSelect' : 'select'](name); + }, + + /** + * @param {string} name + */ + isSelected: function (name) { + var selected = this.option.selected; + return !(selected.hasOwnProperty(name) && !selected[name]) + && indexOf(this._availableNames, name) >= 0; + }, + + defaultOption: { + // 一级层叠 + zlevel: 0, + // 二级层叠 + z: 4, + show: true, + + // 布局方式,默认为水平布局,可选为: + // 'horizontal' | 'vertical' + orient: 'horizontal', + + left: 'center', + // right: 'center', + + top: 0, + // bottom: null, + + // 水平对齐 + // 'auto' | 'left' | 'right' + // 默认为 'auto', 根据 x 的位置判断是左对齐还是右对齐 + align: 'auto', + + backgroundColor: 'rgba(0,0,0,0)', + // 图例边框颜色 + borderColor: '#ccc', + borderRadius: 0, + // 图例边框线宽,单位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 + } + } +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +function 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(); + each$1(legendData, function (model) { + var name = model.get('name'); + // Wrap element + if (name === '\n' || name === '') { + return; + } + var isItemSelected = legendModel.isSelected(name); + if (selectedMap.hasOwnProperty(name)) { + // 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 + */ +registerAction( + 'legendToggleSelect', 'legendselectchanged', + curry(legendSelectActionHandler, 'toggleSelected') +); + +/** + * @event legendSelect + * @type {Object} + * @property {string} type 'legendSelect' + * @property {string} name Series name or data item name + */ +registerAction( + 'legendSelect', 'legendselected', + curry(legendSelectActionHandler, 'select') +); + +/** + * @event legendUnSelect + * @type {Object} + * @property {string} type 'legendUnSelect' + * @property {string} name Series name or data item name + */ +registerAction( + 'legendUnSelect', 'legendunselected', + curry(legendSelectActionHandler, 'unSelect') +); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * 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} + */ +function layout$3(group, componentModel, api) { + var boxLayoutParams = componentModel.getBoxLayoutParams(); + var padding = componentModel.get('padding'); + var viewportSize = {width: api.getWidth(), height: api.getHeight()}; + + var rect = getLayoutRect( + boxLayoutParams, + viewportSize, + padding + ); + + box( + componentModel.get('orient'), + group, + componentModel.get('itemGap'), + rect.width, + rect.height + ); + + positionElement( + group, + boxLayoutParams, + viewportSize, + padding + ); +} + +function makeBackground(rect, componentModel) { + var padding = normalizeCssArray$1( + componentModel.get('padding') + ); + var style = componentModel.getItemStyle(['color', 'opacity']); + style.fill = componentModel.get('backgroundColor'); + var rect = new Rect({ + shape: { + x: rect.x - padding[3], + y: rect.y - padding[0], + width: rect.width + padding[1] + padding[3], + height: rect.height + padding[0] + padding[2], + r: componentModel.get('borderRadius') + }, + style: style, + silent: true, + z2: -1 + }); + // FIXME + // `subPixelOptimizeRect` may bring some gap between edge of viewpart + // and background rect when setting like `left: 0`, `top: 0`. + // graphic.subPixelOptimizeRect(rect); + + return rect; +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var curry$4 = curry; +var each$16 = each$1; +var Group$3 = Group; + +var LegendView = extendComponentView({ + + type: 'legend.plain', + + newlineDisabled: false, + + /** + * @override + */ + init: function () { + + /** + * @private + * @type {module:zrender/container/Group} + */ + this.group.add(this._contentGroup = new Group$3()); + + /** + * @private + * @type {module:zrender/Element} + */ + this._backgroundEl; + }, + + /** + * @protected + */ + getContentGroup: function () { + return this._contentGroup; + }, + + /** + * @override + */ + render: function (legendModel, ecModel, api) { + + this.resetInner(); + + if (!legendModel.get('show', true)) { + return; + } + + var itemAlign = legendModel.get('align'); + if (!itemAlign || itemAlign === 'auto') { + itemAlign = ( + legendModel.get('left') === 'right' + && legendModel.get('orient') === 'vertical' + ) ? 'right' : 'left'; + } + + this.renderInner(itemAlign, legendModel, ecModel, api); + + // Perform layout. + var positionInfo = legendModel.getBoxLayoutParams(); + var viewportSize = {width: api.getWidth(), height: api.getHeight()}; + var padding = legendModel.get('padding'); + + var maxSize = getLayoutRect(positionInfo, viewportSize, padding); + var mainRect = this.layoutInner(legendModel, itemAlign, maxSize); + + // Place mainGroup, based on the calculated `mainRect`. + var layoutRect = getLayoutRect( + defaults({width: mainRect.width, height: mainRect.height}, positionInfo), + viewportSize, + padding + ); + this.group.attr('position', [layoutRect.x - mainRect.x, layoutRect.y - mainRect.y]); + + // Render background after group is layout. + this.group.add( + this._backgroundEl = makeBackground(mainRect, legendModel) + ); + }, + + /** + * @protected + */ + resetInner: function () { + this.getContentGroup().removeAll(); + this._backgroundEl && this.group.remove(this._backgroundEl); + }, + + /** + * @protected + */ + renderInner: function (itemAlign, legendModel, ecModel, api) { + var contentGroup = this.getContentGroup(); + var legendDrawnMap = createHashMap(); + var selectMode = legendModel.get('selectedMode'); + + var excludeSeriesId = []; + ecModel.eachRawSeries(function (seriesModel) { + !seriesModel.get('legendHoverLink') && excludeSeriesId.push(seriesModel.id); + }); + + each$16(legendModel.getData(), function (itemModel, dataIndex) { + var name = itemModel.get('name'); + + // Use empty string or \n as a newline string + if (!this.newlineDisabled && (name === '' || name === '\n')) { + contentGroup.add(new Group$3({ + newline: true + })); + return; + } + + // Representitive series. + var seriesModel = ecModel.getSeriesByName(name)[0]; + + if (legendDrawnMap.get(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, dataIndex, itemModel, legendModel, + legendSymbolType, symbolType, + itemAlign, color, + selectMode + ); + + itemGroup.on('click', curry$4(dispatchSelectAction, name, api)) + .on('mouseover', curry$4(dispatchHighlightAction, seriesModel, null, api, excludeSeriesId)) + .on('mouseout', curry$4(dispatchDownplayAction, seriesModel, null, api, excludeSeriesId)); + + legendDrawnMap.set(name, true); + } + else { + // Data legend of pie, funnel + ecModel.eachRawSeries(function (seriesModel) { + // In case multiple series has same data name + if (legendDrawnMap.get(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, dataIndex, itemModel, legendModel, + legendSymbolType, null, + itemAlign, color, + selectMode + ); + + // FIXME: consider different series has items with the same name. + itemGroup.on('click', curry$4(dispatchSelectAction, name, api)) + // FIXME Should not specify the series name + .on('mouseover', curry$4(dispatchHighlightAction, seriesModel, name, api, excludeSeriesId)) + .on('mouseout', curry$4(dispatchDownplayAction, seriesModel, name, api, excludeSeriesId)); + + legendDrawnMap.set(name, true); + } + + }, this); + } + + if (__DEV__) { + if (!legendDrawnMap.get(name)) { + console.warn(name + ' series not exists. Legend data should be same with series name or data name.'); + } + } + }, this); + }, + + _createItem: function ( + name, dataIndex, itemModel, legendModel, + legendSymbolType, symbolType, + itemAlign, color, selectMode + ) { + var itemWidth = legendModel.get('itemWidth'); + var itemHeight = legendModel.get('itemHeight'); + var inactiveColor = legendModel.get('inactiveColor'); + var symbolKeepAspect = legendModel.get('symbolKeepAspect'); + + var isSelected = legendModel.isSelected(name); + var itemGroup = new Group$3(); + + var textStyleModel = itemModel.getModel('textStyle'); + + var itemIcon = itemModel.get('icon'); + + var tooltipModel = itemModel.getModel('tooltip'); + var legendGlobalTooltipModel = tooltipModel.parentModel; + + // Use user given icon first + legendSymbolType = itemIcon || legendSymbolType; + itemGroup.add(createSymbol( + legendSymbolType, + 0, + 0, + itemWidth, + itemHeight, + isSelected ? color : inactiveColor, + // symbolKeepAspect default true for legend + symbolKeepAspect == null ? true : symbolKeepAspect + )); + + // 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(createSymbol( + symbolType, + (itemWidth - size) / 2, + (itemHeight - size) / 2, + size, + size, + isSelected ? color : inactiveColor, + // symbolKeepAspect default true for legend + symbolKeepAspect == null ? true : symbolKeepAspect + )); + } + + 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 != null ? name : ''); + } + else if (typeof formatter === 'function') { + content = formatter(name); + } + + itemGroup.add(new Text({ + style: setTextStyle({}, textStyleModel, { + text: content, + x: textX, + y: itemHeight / 2, + textFill: isSelected ? textStyleModel.getTextColor() : inactiveColor, + textAlign: textAlign, + textVerticalAlign: 'middle' + }) + })); + + // Add a invisible rect to increase the area of mouse hover + var hitRect = new Rect({ + shape: itemGroup.getBoundingRect(), + invisible: true, + tooltip: tooltipModel.get('show') ? extend({ + content: name, + // Defaul formatter + formatter: legendGlobalTooltipModel.get('formatter', true) || 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.getContentGroup().add(itemGroup); + + setHoverStyle(itemGroup); + + itemGroup.__legendDataIndex = dataIndex; + + return itemGroup; + }, + + /** + * @protected + */ + layoutInner: function (legendModel, itemAlign, maxSize) { + var contentGroup = this.getContentGroup(); + + // Place items in contentGroup. + box( + legendModel.get('orient'), + contentGroup, + legendModel.get('itemGap'), + maxSize.width, + maxSize.height + ); + + var contentRect = contentGroup.getBoundingRect(); + contentGroup.attr('position', [-contentRect.x, -contentRect.y]); + + return this.group.getBoundingRect(); + } + +}); + +function dispatchSelectAction(name, api) { + api.dispatchAction({ + type: 'legendToggleSelect', + name: name + }); +} + +function dispatchHighlightAction(seriesModel, dataName, api, excludeSeriesId) { + // If element hover will move to a hoverLayer. + var el = api.getZr().storage.getDisplayList()[0]; + if (!(el && el.useHoverLayer)) { + api.dispatchAction({ + type: 'highlight', + seriesName: seriesModel.name, + name: dataName, + excludeSeriesId: excludeSeriesId + }); + } +} + +function dispatchDownplayAction(seriesModel, dataName, api, excludeSeriesId) { + // If element hover will move to a hoverLayer. + var el = api.getZr().storage.getDisplayList()[0]; + if (!(el && el.useHoverLayer)) { + api.dispatchAction({ + type: 'downplay', + seriesName: seriesModel.name, + name: dataName, + excludeSeriesId: excludeSeriesId + }); + } +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var legendFilter = 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; + }); + } + +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + + +// Do not contain scrollable legend, for sake of file size. + +// Series Filter +registerProcessor(legendFilter); + +ComponentModel.registerSubTypeDefaulter('legend', function () { + // Default 'plain' when no type specified. + return 'plain'; +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var ScrollableLegendModel = LegendModel.extend({ + + type: 'legend.scroll', + + /** + * @param {number} scrollDataIndex + */ + setScrollDataIndex: function (scrollDataIndex) { + this.option.scrollDataIndex = scrollDataIndex; + }, + + defaultOption: { + scrollDataIndex: 0, + pageButtonItemGap: 5, + pageButtonGap: null, + pageButtonPosition: 'end', // 'start' or 'end' + pageFormatter: '{current}/{total}', // If null/undefined, do not show page. + pageIcons: { + horizontal: ['M0,0L12,-10L12,10z', 'M0,0L-12,-10L-12,10z'], + vertical: ['M0,0L20,0L10,-20z', 'M0,0L20,0L10,20z'] + }, + pageIconColor: '#2f4554', + pageIconInactiveColor: '#aaa', + pageIconSize: 15, // Can be [10, 3], which represents [width, height] + pageTextStyle: { + color: '#333' + }, + + animationDurationUpdate: 800 + }, + + /** + * @override + */ + init: function (option, parentModel, ecModel, extraOpt) { + var inputPositionParams = getLayoutParams(option); + + ScrollableLegendModel.superCall(this, 'init', option, parentModel, ecModel, extraOpt); + + mergeAndNormalizeLayoutParams(this, option, inputPositionParams); + }, + + /** + * @override + */ + mergeOption: function (option, extraOpt) { + ScrollableLegendModel.superCall(this, 'mergeOption', option, extraOpt); + + mergeAndNormalizeLayoutParams(this, this.option, option); + }, + + getOrient: function () { + return this.get('orient') === 'vertical' + ? {index: 1, name: 'vertical'} + : {index: 0, name: 'horizontal'}; + } + +}); + +// Do not `ignoreSize` to enable setting {left: 10, right: 10}. +function mergeAndNormalizeLayoutParams(legendModel, target, raw) { + var orient = legendModel.getOrient(); + var ignoreSize = [1, 1]; + ignoreSize[orient.index] = 0; + mergeLayoutParam(target, raw, { + type: 'box', ignoreSize: ignoreSize + }); +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * Separate legend and scrollable legend to reduce package size. + */ + +var Group$4 = Group; + +var WH$1 = ['width', 'height']; +var XY$1 = ['x', 'y']; + +var ScrollableLegendView = LegendView.extend({ + + type: 'legend.scroll', + + newlineDisabled: true, + + init: function () { + + ScrollableLegendView.superCall(this, 'init'); + + /** + * @private + * @type {number} For `scroll`. + */ + this._currentIndex = 0; + + /** + * @private + * @type {module:zrender/container/Group} + */ + this.group.add(this._containerGroup = new Group$4()); + this._containerGroup.add(this.getContentGroup()); + + /** + * @private + * @type {module:zrender/container/Group} + */ + this.group.add(this._controllerGroup = new Group$4()); + + /** + * + * @private + */ + this._showController; + }, + + /** + * @override + */ + resetInner: function () { + ScrollableLegendView.superCall(this, 'resetInner'); + + this._controllerGroup.removeAll(); + this._containerGroup.removeClipPath(); + this._containerGroup.__rectSize = null; + }, + + /** + * @override + */ + renderInner: function (itemAlign, legendModel, ecModel, api) { + var me = this; + + // Render content items. + ScrollableLegendView.superCall(this, 'renderInner', itemAlign, legendModel, ecModel, api); + + var controllerGroup = this._controllerGroup; + + var pageIconSize = legendModel.get('pageIconSize', true); + if (!isArray(pageIconSize)) { + pageIconSize = [pageIconSize, pageIconSize]; + } + + createPageButton('pagePrev', 0); + + var pageTextStyleModel = legendModel.getModel('pageTextStyle'); + controllerGroup.add(new Text({ + name: 'pageText', + style: { + textFill: pageTextStyleModel.getTextColor(), + font: pageTextStyleModel.getFont(), + textVerticalAlign: 'middle', + textAlign: 'center' + }, + silent: true + })); + + createPageButton('pageNext', 1); + + function createPageButton(name, iconIdx) { + var pageDataIndexName = name + 'DataIndex'; + var icon = createIcon( + legendModel.get('pageIcons', true)[legendModel.getOrient().name][iconIdx], + { + // Buttons will be created in each render, so we do not need + // to worry about avoiding using legendModel kept in scope. + onclick: bind( + me._pageGo, me, pageDataIndexName, legendModel, api + ) + }, + { + x: -pageIconSize[0] / 2, + y: -pageIconSize[1] / 2, + width: pageIconSize[0], + height: pageIconSize[1] + } + ); + icon.name = name; + controllerGroup.add(icon); + } + }, + + /** + * @override + */ + layoutInner: function (legendModel, itemAlign, maxSize) { + var contentGroup = this.getContentGroup(); + var containerGroup = this._containerGroup; + var controllerGroup = this._controllerGroup; + + var orientIdx = legendModel.getOrient().index; + var wh = WH$1[orientIdx]; + var hw = WH$1[1 - orientIdx]; + var yx = XY$1[1 - orientIdx]; + + // Place items in contentGroup. + box( + legendModel.get('orient'), + contentGroup, + legendModel.get('itemGap'), + !orientIdx ? null : maxSize.width, + orientIdx ? null : maxSize.height + ); + + box( + // Buttons in controller are layout always horizontally. + 'horizontal', + controllerGroup, + legendModel.get('pageButtonItemGap', true) + ); + + var contentRect = contentGroup.getBoundingRect(); + var controllerRect = controllerGroup.getBoundingRect(); + var showController = this._showController = contentRect[wh] > maxSize[wh]; + + var contentPos = [-contentRect.x, -contentRect.y]; + // Remain contentPos when scroll animation perfroming. + contentPos[orientIdx] = contentGroup.position[orientIdx]; + + // Layout container group based on 0. + var containerPos = [0, 0]; + var controllerPos = [-controllerRect.x, -controllerRect.y]; + var pageButtonGap = retrieve2( + legendModel.get('pageButtonGap', true), legendModel.get('itemGap', true) + ); + + // Place containerGroup and controllerGroup and contentGroup. + if (showController) { + var pageButtonPosition = legendModel.get('pageButtonPosition', true); + // controller is on the right / bottom. + if (pageButtonPosition === 'end') { + controllerPos[orientIdx] += maxSize[wh] - controllerRect[wh]; + } + // controller is on the left / top. + else { + containerPos[orientIdx] += controllerRect[wh] + pageButtonGap; + } + } + + // Always align controller to content as 'middle'. + controllerPos[1 - orientIdx] += contentRect[hw] / 2 - controllerRect[hw] / 2; + + contentGroup.attr('position', contentPos); + containerGroup.attr('position', containerPos); + controllerGroup.attr('position', controllerPos); + + // Calculate `mainRect` and set `clipPath`. + // mainRect should not be calculated by `this.group.getBoundingRect()` + // for sake of the overflow. + var mainRect = this.group.getBoundingRect(); + var mainRect = {x: 0, y: 0}; + // Consider content may be overflow (should be clipped). + mainRect[wh] = showController ? maxSize[wh] : contentRect[wh]; + mainRect[hw] = Math.max(contentRect[hw], controllerRect[hw]); + // `containerRect[yx] + containerPos[1 - orientIdx]` is 0. + mainRect[yx] = Math.min(0, controllerRect[yx] + controllerPos[1 - orientIdx]); + + containerGroup.__rectSize = maxSize[wh]; + if (showController) { + var clipShape = {x: 0, y: 0}; + clipShape[wh] = Math.max(maxSize[wh] - controllerRect[wh] - pageButtonGap, 0); + clipShape[hw] = mainRect[hw]; + containerGroup.setClipPath(new Rect({shape: clipShape})); + // Consider content may be larger than container, container rect + // can not be obtained from `containerGroup.getBoundingRect()`. + containerGroup.__rectSize = clipShape[wh]; + } + else { + // Do not remove or ignore controller. Keep them set as place holders. + controllerGroup.eachChild(function (child) { + child.attr({invisible: true, silent: true}); + }); + } + + // Content translate animation. + var pageInfo = this._getPageInfo(legendModel); + pageInfo.pageIndex != null && updateProps( + contentGroup, + {position: pageInfo.contentPosition}, + // When switch from "show controller" to "not show controller", view should be + // updated immediately without animation, otherwise causes weird efffect. + showController ? legendModel : false + ); + + this._updatePageInfoView(legendModel, pageInfo); + + return mainRect; + }, + + _pageGo: function (to, legendModel, api) { + var scrollDataIndex = this._getPageInfo(legendModel)[to]; + + scrollDataIndex != null && api.dispatchAction({ + type: 'legendScroll', + scrollDataIndex: scrollDataIndex, + legendId: legendModel.id + }); + }, + + _updatePageInfoView: function (legendModel, pageInfo) { + var controllerGroup = this._controllerGroup; + + each$1(['pagePrev', 'pageNext'], function (name) { + var canJump = pageInfo[name + 'DataIndex'] != null; + var icon = controllerGroup.childOfName(name); + if (icon) { + icon.setStyle( + 'fill', + canJump + ? legendModel.get('pageIconColor', true) + : legendModel.get('pageIconInactiveColor', true) + ); + icon.cursor = canJump ? 'pointer' : 'default'; + } + }); + + var pageText = controllerGroup.childOfName('pageText'); + var pageFormatter = legendModel.get('pageFormatter'); + var pageIndex = pageInfo.pageIndex; + var current = pageIndex != null ? pageIndex + 1 : 0; + var total = pageInfo.pageCount; + + pageText && pageFormatter && pageText.setStyle( + 'text', + isString(pageFormatter) + ? pageFormatter.replace('{current}', current).replace('{total}', total) + : pageFormatter({current: current, total: total}) + ); + }, + + /** + * @param {module:echarts/model/Model} legendModel + * @return {Object} { + * contentPosition: Array., null when data item not found. + * pageIndex: number, null when data item not found. + * pageCount: number, always be a number, can be 0. + * pagePrevDataIndex: number, null when no next page. + * pageNextDataIndex: number, null when no previous page. + * } + */ + _getPageInfo: function (legendModel) { + // Align left or top by the current dataIndex. + var currDataIndex = legendModel.get('scrollDataIndex', true); + var contentGroup = this.getContentGroup(); + var contentRect = contentGroup.getBoundingRect(); + var containerRectSize = this._containerGroup.__rectSize; + + var orientIdx = legendModel.getOrient().index; + var wh = WH$1[orientIdx]; + var hw = WH$1[1 - orientIdx]; + var xy = XY$1[orientIdx]; + var contentPos = contentGroup.position.slice(); + + var pageIndex; + var pagePrevDataIndex; + var pageNextDataIndex; + + var targetItemGroup; + if (this._showController) { + contentGroup.eachChild(function (child) { + if (child.__legendDataIndex === currDataIndex) { + targetItemGroup = child; + } + }); + } + else { + targetItemGroup = contentGroup.childAt(0); + } + + var pageCount = containerRectSize ? Math.ceil(contentRect[wh] / containerRectSize) : 0; + + if (targetItemGroup) { + var itemRect = targetItemGroup.getBoundingRect(); + var itemLoc = targetItemGroup.position[orientIdx] + itemRect[xy]; + contentPos[orientIdx] = -itemLoc - contentRect[xy]; + pageIndex = Math.floor( + pageCount * (itemLoc + itemRect[xy] + containerRectSize / 2) / contentRect[wh] + ); + pageIndex = (contentRect[wh] && pageCount) + ? Math.max(0, Math.min(pageCount - 1, pageIndex)) + : -1; + + var winRect = {x: 0, y: 0}; + winRect[wh] = containerRectSize; + winRect[hw] = contentRect[hw]; + winRect[xy] = -contentPos[orientIdx] - contentRect[xy]; + + var startIdx; + var children = contentGroup.children(); + + contentGroup.eachChild(function (child, index) { + var itemRect = getItemRect(child); + + if (itemRect.intersect(winRect)) { + startIdx == null && (startIdx = index); + // It is user-friendly that the last item shown in the + // current window is shown at the begining of next window. + pageNextDataIndex = child.__legendDataIndex; + } + + // If the last item is shown entirely, no next page. + if (index === children.length - 1 + && itemRect[xy] + itemRect[wh] <= winRect[xy] + winRect[wh] + ) { + pageNextDataIndex = null; + } + }); + + // Always align based on the left/top most item, so the left/top most + // item in the previous window is needed to be found here. + if (startIdx != null) { + var startItem = children[startIdx]; + var startRect = getItemRect(startItem); + winRect[xy] = startRect[xy] + startRect[wh] - winRect[wh]; + + // If the first item is shown entirely, no previous page. + if (startIdx <= 0 && startRect[xy] >= winRect[xy]) { + pagePrevDataIndex = null; + } + else { + while (startIdx > 0 && getItemRect(children[startIdx - 1]).intersect(winRect)) { + startIdx--; + } + pagePrevDataIndex = children[startIdx].__legendDataIndex; + } + } + } + + return { + contentPosition: contentPos, + pageIndex: pageIndex, + pageCount: pageCount, + pagePrevDataIndex: pagePrevDataIndex, + pageNextDataIndex: pageNextDataIndex + }; + + function getItemRect(el) { + var itemRect = el.getBoundingRect().clone(); + itemRect[xy] += el.position[orientIdx]; + return itemRect; + } + } + +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * @event legendScroll + * @type {Object} + * @property {string} type 'legendScroll' + * @property {string} scrollDataIndex + */ +registerAction( + 'legendScroll', 'legendscroll', + function (payload, ecModel) { + var scrollDataIndex = payload.scrollDataIndex; + + scrollDataIndex != null && ecModel.eachComponent( + {mainType: 'legend', subType: 'scroll', query: payload}, + function (legendModel) { + legendModel.setScrollDataIndex(scrollDataIndex); + } + ); + } +); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * Legend component entry file8 + */ + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +extendComponentModel({ + + type: 'tooltip', + + dependencies: ['axisPointer'], + + defaultOption: { + zlevel: 0, + + z: 8, + + show: true, + + // tooltip主体内容 + showContent: true, + + // 'trigger' only works on coordinate system. + // 'item' | 'axis' | 'none' + trigger: 'item', + + // 'click' | 'mousemove' | 'none' + triggerOn: 'mousemove|click', + + alwaysShowContent: false, + + displayMode: 'single', // 'single' | 'multipleByCoordSys' + + // 位置 {Array} | {Function} + // position: null + // Consider triggered from axisPointer handle, verticalAlign should be 'middle' + // align: null, + // verticalAlign: null, + + // 是否约束 content 在 viewRect 中。默认 false 是为了兼容以前版本。 + confine: false, + + // 内容格式器:{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',会选择类型为 category 的轴,对于双数值轴,笛卡尔坐标系会默认选择 x 轴 + // 极坐标系会默认选择 angle 轴 + axis: 'auto', + + animation: 'auto', + animationDurationUpdate: 200, + animationEasingUpdate: 'exponentialOut', + + crossStyle: { + color: '#999', + width: 1, + type: 'dashed', + + // TODO formatter + textStyle: {} + } + + // lineStyle and shadowStyle should not be specified here, + // otherwise it will always override those styles on option.axisPointer. + }, + textStyle: { + color: '#fff', + fontSize: 14 + } + } +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var each$18 = each$1; +var toCamelCase$1 = toCamelCase; + +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 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$18(['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) { + + var cssText = []; + + var transitionDuration = tooltipModel.get('transitionDuration'); + var backgroundColor = tooltipModel.get('backgroundColor'); + var textStyleModel = tooltipModel.getModel('textStyle'); + var padding = tooltipModel.get('padding'); + + // Animation transition. Do not animate when transitionDuration is 0. + transitionDuration && + cssText.push(assembleTransition(transitionDuration)); + + if (backgroundColor) { + if (env$1.canvasSupported) { + cssText.push('background-Color:' + backgroundColor); + } + else { + // for ie + cssText.push( + 'background-Color:#' + toHex(backgroundColor) + ); + cssText.push('filter:alpha(opacity=70)'); + } + } + + // Border style + each$18(['width', 'color', 'radius'], function (name) { + var borderName = 'border-' + name; + var camelCase = toCamelCase$1(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:' + normalizeCssArray$1(padding).join('px ') + 'px'); + } + + return cssText.join(';') + ';'; +} + +/** + * @alias module:echarts/component/tooltip/TooltipContent + * @constructor + */ +function TooltipContent(container, api) { + if (env$1.wxa) { + return null; + } + + var el = document.createElement('div'); + var zr = this._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) { + e = e || window.event; + if (!self._enterable) { + // Try trigger zrender event to avoid mouse + // in and out shape too frequently + var handler = zr.handler; + normalizeEvent(container, e, true); + handler.dispatch('mousemove', e); + } + }; + el.onmouseleave = function () { + if (self._enterable) { + if (self._show) { + self.hideLater(self._hideDelay); + } + } + self._inContent = false; + }; +} + +TooltipContent.prototype = { + + constructor: TooltipContent, + + /** + * @private + * @type {boolean} + */ + _enterable: true, + + /** + * Update when tooltip is rendered + */ + update: function () { + // FIXME + // Move this logic to ec main? + 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) { + this.el.innerHTML = content == null ? '' : content; + }, + + setEnterable: function (enterable) { + this._enterable = enterable; + }, + + getSize: function () { + var el = this.el; + return [el.clientWidth, el.clientHeight]; + }, + + moveTo: function (x, y) { + // xy should be based on canvas root. But tooltipContent is + // the sibling of canvas root. So padding of ec container + // should be considered here. + var zr = this._zr; + var viewportRootOffset; + if (zr && zr.painter && (viewportRootOffset = zr.painter.getViewportRootOffset())) { + x += viewportRootOffset.offsetLeft; + y += viewportRootOffset.offsetTop; + } + + 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; + }, + + 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(bind(this.hide, this), time); + } + else { + this.hide(); + } + } + }, + + isShow: function () { + return this._show; + } +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var bind$3 = bind; +var each$17 = each$1; +var parsePercent$2 = parsePercent$1; + +var proxyRect = new Rect({ + shape: {x: -1, y: -1, width: 2, height: 2} +}); + +extendComponentView({ + + type: 'tooltip', + + init: function (ecModel, api) { + if (env$1.node) { + return; + } + var tooltipContent = new TooltipContent(api.getDom(), api); + this._tooltipContent = tooltipContent; + }, + + render: function (tooltipModel, ecModel, api) { + if (env$1.node || env$1.wxa) { + return; + } + + // Reset + this.group.removeAll(); + + /** + * @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; + + /** + * Should be cleaned when render. + * @private + * @type {Array.>} + */ + this._lastDataByCoordSys = null; + + /** + * @private + * @type {boolean} + */ + this._alwaysShowContent = tooltipModel.get('alwaysShowContent'); + + var tooltipContent = this._tooltipContent; + tooltipContent.update(); + tooltipContent.setEnterable(tooltipModel.get('enterable')); + + this._initGlobalListener(); + + this._keepShow(); + }, + + _initGlobalListener: function () { + var tooltipModel = this._tooltipModel; + var triggerOn = tooltipModel.get('triggerOn'); + + register( + 'itemTooltip', + this._api, + bind$3(function (currTrigger, e, dispatchAction) { + // If 'none', it is not controlled by mouse totally. + if (triggerOn !== 'none') { + if (triggerOn.indexOf(currTrigger) >= 0) { + this._tryShow(e, dispatchAction); + } + else if (currTrigger === 'leave') { + this._hide(dispatchAction); + } + } + }, this) + ); + }, + + _keepShow: function () { + var tooltipModel = this._tooltipModel; + var ecModel = this._ecModel; + var api = this._api; + + // Try to keep the tooltip show when refreshing + if (this._lastX != null + && this._lastY != null + // When user is willing to control tooltip totally using API, + // self.manuallyShowTip({x, y}) might cause tooltip hide, + // which is not expected. + && tooltipModel.get('triggerOn') !== 'none' + ) { + 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(tooltipModel, ecModel, api, { + x: self._lastX, + y: self._lastY + }); + }); + } + }, + + /** + * Show tip manually by + * dispatchAction({ + * type: 'showTip', + * x: 10, + * y: 10 + * }); + * Or + * dispatchAction({ + * type: 'showTip', + * seriesIndex: 0, + * dataIndex or dataIndexInside or name + * }); + * + * TODO Batch + */ + manuallyShowTip: function (tooltipModel, ecModel, api, payload) { + if (payload.from === this.uid || env$1.node) { + return; + } + + var dispatchAction = makeDispatchAction$1(payload, api); + + // Reset ticket + this._ticket = ''; + + // When triggered from axisPointer. + var dataByCoordSys = payload.dataByCoordSys; + + if (payload.tooltip && payload.x != null && payload.y != null) { + var el = proxyRect; + el.position = [payload.x, payload.y]; + el.update(); + el.tooltip = payload.tooltip; + // Manually show tooltip while view is not using zrender elements. + this._tryShow({ + offsetX: payload.x, + offsetY: payload.y, + target: el + }, dispatchAction); + } + else if (dataByCoordSys) { + this._tryShow({ + offsetX: payload.x, + offsetY: payload.y, + position: payload.position, + event: {}, + dataByCoordSys: payload.dataByCoordSys, + tooltipOption: payload.tooltipOption + }, dispatchAction); + } + else if (payload.seriesIndex != null) { + + if (this._manuallyAxisShowTip(tooltipModel, ecModel, api, payload)) { + return; + } + + var pointInfo = findPointFromSeries(payload, ecModel); + var cx = pointInfo.point[0]; + var cy = pointInfo.point[1]; + if (cx != null && cy != null) { + this._tryShow({ + offsetX: cx, + offsetY: cy, + position: payload.position, + target: pointInfo.el, + event: {} + }, dispatchAction); + } + } + else if (payload.x != null && payload.y != null) { + // FIXME + // should wrap dispatchAction like `axisPointer/globalListener` ? + api.dispatchAction({ + type: 'updateAxisPointer', + x: payload.x, + y: payload.y + }); + + this._tryShow({ + offsetX: payload.x, + offsetY: payload.y, + position: payload.position, + target: api.getZr().findHover(payload.x, payload.y).target, + event: {} + }, dispatchAction); + } + }, + + manuallyHideTip: function (tooltipModel, ecModel, api, payload) { + var tooltipContent = this._tooltipContent; + + if (!this._alwaysShowContent && this._tooltipModel) { + tooltipContent.hideLater(this._tooltipModel.get('hideDelay')); + } + + this._lastX = this._lastY = null; + + if (payload.from !== this.uid) { + this._hide(makeDispatchAction$1(payload, api)); + } + }, + + // Be compatible with previous design, that is, when tooltip.type is 'axis' and + // dispatchAction 'showTip' with seriesIndex and dataIndex will trigger axis pointer + // and tooltip. + _manuallyAxisShowTip: function (tooltipModel, ecModel, api, payload) { + var seriesIndex = payload.seriesIndex; + var dataIndex = payload.dataIndex; + var coordSysAxesInfo = ecModel.getComponent('axisPointer').coordSysAxesInfo; + + if (seriesIndex == null || dataIndex == null || coordSysAxesInfo == null) { + return; + } + + var seriesModel = ecModel.getSeriesByIndex(seriesIndex); + if (!seriesModel) { + return; + } + + var data = seriesModel.getData(); + var tooltipModel = buildTooltipModel([ + data.getItemModel(dataIndex), + seriesModel, + (seriesModel.coordinateSystem || {}).model, + tooltipModel + ]); + + if (tooltipModel.get('trigger') !== 'axis') { + return; + } + + api.dispatchAction({ + type: 'updateAxisPointer', + seriesIndex: seriesIndex, + dataIndex: dataIndex, + position: payload.position + }); + + return true; + }, + + _tryShow: function (e, dispatchAction) { + var el = e.target; + var tooltipModel = this._tooltipModel; + + 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; + + var dataByCoordSys = e.dataByCoordSys; + if (dataByCoordSys && dataByCoordSys.length) { + this._showAxisTooltip(dataByCoordSys, e); + } + // Always show item tooltip if mouse is on the element with dataIndex + else if (el && el.dataIndex != null) { + this._lastDataByCoordSys = null; + this._showSeriesItemTooltip(e, el, dispatchAction); + } + // Tooltip provided directly. Like legend. + else if (el && el.tooltip) { + this._lastDataByCoordSys = null; + this._showComponentItemTooltip(e, el, dispatchAction); + } + else { + this._lastDataByCoordSys = null; + this._hide(dispatchAction); + } + }, + + _showOrMove: function (tooltipModel, cb) { + // showDelay is used in this case: tooltip.enterable is set + // as true. User intent to move mouse into tooltip and click + // something. `showDelay` makes it easyer to enter the content + // but tooltip do not move immediately. + var delay = tooltipModel.get('showDelay'); + cb = bind(cb, this); + clearTimeout(this._showTimout); + delay > 0 + ? (this._showTimout = setTimeout(cb, delay)) + : cb(); + }, + + _showAxisTooltip: function (dataByCoordSys, e) { + var ecModel = this._ecModel; + var globalTooltipModel = this._tooltipModel; + var point = [e.offsetX, e.offsetY]; + var singleDefaultHTML = []; + var singleParamsList = []; + var singleTooltipModel = buildTooltipModel([ + e.tooltipOption, + globalTooltipModel + ]); + + each$17(dataByCoordSys, function (itemCoordSys) { + // var coordParamList = []; + // var coordDefaultHTML = []; + // var coordTooltipModel = buildTooltipModel([ + // e.tooltipOption, + // itemCoordSys.tooltipOption, + // ecModel.getComponent(itemCoordSys.coordSysMainType, itemCoordSys.coordSysIndex), + // globalTooltipModel + // ]); + // var displayMode = coordTooltipModel.get('displayMode'); + // var paramsList = displayMode === 'single' ? singleParamsList : []; + + each$17(itemCoordSys.dataByAxis, function (item) { + var axisModel = ecModel.getComponent(item.axisDim + 'Axis', item.axisIndex); + var axisValue = item.value; + var seriesDefaultHTML = []; + + if (!axisModel || axisValue == null) { + return; + } + + var valueLabel = getValueLabel( + axisValue, axisModel.axis, ecModel, + item.seriesDataIndices, + item.valueLabelOpt + ); + + each$1(item.seriesDataIndices, function (idxItem) { + var series = ecModel.getSeriesByIndex(idxItem.seriesIndex); + var dataIndex = idxItem.dataIndexInside; + var dataParams = series && series.getDataParams(dataIndex); + dataParams.axisDim = item.axisDim; + dataParams.axisIndex = item.axisIndex; + dataParams.axisType = item.axisType; + dataParams.axisId = item.axisId; + dataParams.axisValue = getAxisRawValue(axisModel.axis, axisValue); + dataParams.axisValueLabel = valueLabel; + + if (dataParams) { + singleParamsList.push(dataParams); + seriesDefaultHTML.push(series.formatTooltip(dataIndex, true)); + } + }); + + // 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 = valueLabel; + singleDefaultHTML.push( + (firstLine ? encodeHTML(firstLine) + '
' : '') + + seriesDefaultHTML.join('
') + ); + }); + }, this); + + // In most case, the second axis is shown upper than the first one. + singleDefaultHTML.reverse(); + singleDefaultHTML = singleDefaultHTML.join('

'); + + var positionExpr = e.position; + this._showOrMove(singleTooltipModel, function () { + if (this._updateContentNotChangedOnAxis(dataByCoordSys)) { + this._updatePosition( + singleTooltipModel, + positionExpr, + point[0], point[1], + this._tooltipContent, + singleParamsList + ); + } + else { + this._showTooltipContent( + singleTooltipModel, singleDefaultHTML, singleParamsList, Math.random(), + point[0], point[1], positionExpr + ); + } + }); + + // Do not trigger events here, because this branch only be entered + // from dispatchAction. + }, + + _showSeriesItemTooltip: function (e, el, dispatchAction) { + var ecModel = this._ecModel; + // 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 seriesIndex = el.seriesIndex; + var seriesModel = ecModel.getSeriesByIndex(seriesIndex); + + // For example, graph link. + var dataModel = el.dataModel || seriesModel; + var dataIndex = el.dataIndex; + var dataType = el.dataType; + var data = dataModel.getData(); + + var tooltipModel = buildTooltipModel([ + data.getItemModel(dataIndex), + dataModel, + seriesModel && (seriesModel.coordinateSystem || {}).model, + this._tooltipModel + ]); + + var tooltipTrigger = tooltipModel.get('trigger'); + if (tooltipTrigger != null && tooltipTrigger !== 'item') { + return; + } + + var params = dataModel.getDataParams(dataIndex, dataType); + var defaultHtml = dataModel.formatTooltip(dataIndex, false, dataType); + var asyncTicket = 'item_' + dataModel.name + '_' + dataIndex; + + this._showOrMove(tooltipModel, function () { + this._showTooltipContent( + tooltipModel, defaultHtml, params, asyncTicket, + e.offsetX, e.offsetY, e.position, e.target + ); + }); + + // FIXME + // duplicated showtip if manuallyShowTip is called from dispatchAction. + dispatchAction({ + type: 'showTip', + dataIndexInside: dataIndex, + dataIndex: data.getRawIndex(dataIndex), + seriesIndex: seriesIndex, + from: this.uid + }); + }, + + _showComponentItemTooltip: function (e, el, dispatchAction) { + var tooltipOpt = el.tooltip; + if (typeof tooltipOpt === 'string') { + var content = tooltipOpt; + tooltipOpt = { + content: content, + // Fixed formatter + formatter: content + }; + } + var subTooltipModel = new Model(tooltipOpt, this._tooltipModel, this._ecModel); + var defaultHtml = subTooltipModel.get('content'); + var asyncTicket = Math.random(); + + // Do not check whether `trigger` is 'none' here, because `trigger` + // only works on cooridinate system. In fact, we have not found case + // that requires setting `trigger` nothing on component yet. + + this._showOrMove(subTooltipModel, function () { + this._showTooltipContent( + subTooltipModel, defaultHtml, subTooltipModel.get('formatterParams') || {}, + asyncTicket, e.offsetX, e.offsetY, e.position, el + ); + }); + + // If not dispatch showTip, tip may be hide triggered by axis. + dispatchAction({ + type: 'showTip', + from: this.uid + }); + }, + + _showTooltipContent: function ( + tooltipModel, defaultHtml, params, asyncTicket, x, y, positionExpr, el + ) { + // Reset ticket + this._ticket = ''; + + if (!tooltipModel.get('showContent') || !tooltipModel.get('show')) { + return; + } + + var tooltipContent = this._tooltipContent; + + var formatter = tooltipModel.get('formatter'); + positionExpr = positionExpr || tooltipModel.get('position'); + var html = defaultHtml; + + if (formatter && typeof formatter === 'string') { + html = formatTpl(formatter, params, true); + } + else if (typeof formatter === 'function') { + var callback = bind$3(function (cbTicket, html) { + if (cbTicket === this._ticket) { + tooltipContent.setContent(html); + this._updatePosition( + tooltipModel, positionExpr, x, y, tooltipContent, params, el + ); + } + }, this); + this._ticket = asyncTicket; + html = formatter(params, asyncTicket, callback); + } + + tooltipContent.setContent(html); + tooltipContent.show(tooltipModel); + + this._updatePosition( + tooltipModel, positionExpr, x, y, tooltipContent, params, el + ); + }, + + /** + * @param {string|Function|Array.|Object} positionExpr + * @param {number} x Mouse x + * @param {number} y Mouse y + * @param {boolean} confine Whether confine tooltip content in view rect. + * @param {Object|} params + * @param {module:zrender/Element} el target element + * @param {module:echarts/ExtensionAPI} api + * @return {Array.} + */ + _updatePosition: function (tooltipModel, positionExpr, x, y, content, params, el) { + var viewWidth = this._api.getWidth(); + var viewHeight = this._api.getHeight(); + positionExpr = positionExpr || tooltipModel.get('position'); + + var contentSize = content.getSize(); + var align = tooltipModel.get('align'); + var vAlign = tooltipModel.get('verticalAlign'); + 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, { + viewSize: [viewWidth, viewHeight], + contentSize: contentSize.slice() + }); + } + + if (isArray(positionExpr)) { + x = parsePercent$2(positionExpr[0], viewWidth); + y = parsePercent$2(positionExpr[1], viewHeight); + } + else if (isObject$1(positionExpr)) { + positionExpr.width = contentSize[0]; + positionExpr.height = contentSize[1]; + var layoutRect = getLayoutRect( + positionExpr, {width: viewWidth, height: viewHeight} + ); + x = layoutRect.x; + y = layoutRect.y; + align = null; + // When positionExpr is left/top/right/bottom, + // align and verticalAlign will not work. + vAlign = null; + } + // Specify tooltip position by string 'top' 'bottom' 'left' 'right' around graphic element + else if (typeof positionExpr === 'string' && el) { + var pos = calcTooltipPosition( + positionExpr, rect, contentSize + ); + x = pos[0]; + y = pos[1]; + } + else { + var pos = refixTooltipPosition( + x, y, content.el, viewWidth, viewHeight, align ? null : 20, vAlign ? null : 20 + ); + x = pos[0]; + y = pos[1]; + } + + align && (x -= isCenterAlign(align) ? contentSize[0] / 2 : align === 'right' ? contentSize[0] : 0); + vAlign && (y -= isCenterAlign(vAlign) ? contentSize[1] / 2 : vAlign === 'bottom' ? contentSize[1] : 0); + + if (tooltipModel.get('confine')) { + var pos = confineTooltipPosition( + x, y, content.el, viewWidth, viewHeight + ); + x = pos[0]; + y = pos[1]; + } + + content.moveTo(x, y); + }, + + // FIXME + // Should we remove this but leave this to user? + _updateContentNotChangedOnAxis: function (dataByCoordSys) { + var lastCoordSys = this._lastDataByCoordSys; + var contentNotChanged = !!lastCoordSys + && lastCoordSys.length === dataByCoordSys.length; + + contentNotChanged && each$17(lastCoordSys, function (lastItemCoordSys, indexCoordSys) { + var lastDataByAxis = lastItemCoordSys.dataByAxis || {}; + var thisItemCoordSys = dataByCoordSys[indexCoordSys] || {}; + var thisDataByAxis = thisItemCoordSys.dataByAxis || []; + contentNotChanged &= lastDataByAxis.length === thisDataByAxis.length; + + contentNotChanged && each$17(lastDataByAxis, function (lastItem, indexAxis) { + var thisItem = thisDataByAxis[indexAxis] || {}; + var lastIndices = lastItem.seriesDataIndices || []; + var newIndices = thisItem.seriesDataIndices || []; + + contentNotChanged &= + lastItem.value === thisItem.value + && lastItem.axisType === thisItem.axisType + && lastItem.axisId === thisItem.axisId + && lastIndices.length === newIndices.length; + + contentNotChanged && each$17(lastIndices, function (lastIdxItem, j) { + var newIdxItem = newIndices[j]; + contentNotChanged &= + lastIdxItem.seriesIndex === newIdxItem.seriesIndex + && lastIdxItem.dataIndex === newIdxItem.dataIndex; + }); + }); + }); + + this._lastDataByCoordSys = dataByCoordSys; + + return !!contentNotChanged; + }, + + _hide: function (dispatchAction) { + // Do not directly hideLater here, because this behavior may be prevented + // in dispatchAction when showTip is dispatched. + + // FIXME + // duplicated hideTip if manuallyHideTip is called from dispatchAction. + this._lastDataByCoordSys = null; + dispatchAction({ + type: 'hideTip', + from: this.uid + }); + }, + + dispose: function (ecModel, api) { + if (env$1.node || env$1.wxa) { + return; + } + this._tooltipContent.hide(); + unregister('itemTooltip', api); + } +}); + + +/** + * @param {Array.} modelCascade + * From top to bottom. (the last one should be globalTooltipModel); + */ +function buildTooltipModel(modelCascade) { + var resultModel = modelCascade.pop(); + while (modelCascade.length) { + var tooltipOpt = modelCascade.pop(); + if (tooltipOpt) { + if (Model.isInstance(tooltipOpt)) { + tooltipOpt = tooltipOpt.get('tooltip', true); + } + // In each data item tooltip can be simply write: + // { + // value: 10, + // tooltip: 'Something you need to know' + // } + if (typeof tooltipOpt === 'string') { + tooltipOpt = {formatter: tooltipOpt}; + } + resultModel = new Model(tooltipOpt, resultModel, resultModel.ecModel); + } + } + return resultModel; +} + +function makeDispatchAction$1(payload, api) { + return payload.dispatchAction || bind(api.dispatchAction, api); +} + +function refixTooltipPosition(x, y, el, viewWidth, viewHeight, gapH, gapV) { + var size = getOuterSize(el); + var width = size.width; + var height = size.height; + + if (gapH != null) { + if (x + width + gapH > viewWidth) { + x -= width + gapH; + } + else { + x += gapH; + } + } + if (gapV != null) { + if (y + height + gapV > viewHeight) { + y -= height + gapV; + } + else { + y += gapV; + } + } + return [x, y]; +} + +function confineTooltipPosition(x, y, el, viewWidth, viewHeight) { + var size = getOuterSize(el); + var width = size.width; + var height = size.height; + + x = Math.min(x + width, viewWidth) - width; + y = Math.min(y + height, viewHeight) - height; + x = Math.max(x, 0); + y = Math.max(y, 0); + + return [x, y]; +} + +function getOuterSize(el) { + var width = el.clientWidth; + var height = el.clientHeight; + + // Consider browser compatibility. + // IE8 does not support getComputedStyle. + if (document.defaultView && document.defaultView.getComputedStyle) { + var stl = document.defaultView.getComputedStyle(el); + if (stl) { + width += parseInt(stl.paddingLeft, 10) + parseInt(stl.paddingRight, 10) + + parseInt(stl.borderLeftWidth, 10) + parseInt(stl.borderRightWidth, 10); + height += parseInt(stl.paddingTop, 10) + parseInt(stl.paddingBottom, 10) + + parseInt(stl.borderTopWidth, 10) + parseInt(stl.borderBottomWidth, 10); + } + } + + return {width: width, height: height}; +} + +function calcTooltipPosition(position, rect, contentSize) { + var domWidth = contentSize[0]; + var domHeight = contentSize[1]; + 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]; +} + +function isCenterAlign(align) { + return align === 'center' || align === 'middle'; +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +// FIXME Better way to pack data in graphic element + +/** + * @action + * @property {string} type + * @property {number} seriesIndex + * @property {number} dataIndex + * @property {number} [x] + * @property {number} [y] + */ +registerAction( + { + type: 'showTip', + event: 'showTip', + update: 'tooltip:manuallyShowTip' + }, + // noop + function () {} +); + +registerAction( + { + type: 'hideTip', + event: 'hideTip', + update: 'tooltip:manuallyHideTip' + }, + // noop + function () {} +); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +function getSeriesStackId$1(seriesModel) { + return seriesModel.get('stack') + || '__ec_stack_' + seriesModel.seriesIndex; +} + +function getAxisKey$1(axis) { + return axis.dim; +} + +/** + * @param {string} seriesType + * @param {module:echarts/model/Global} ecModel + * @param {module:echarts/ExtensionAPI} api + */ +function barLayoutPolar(seriesType, ecModel, api) { + + var width = api.getWidth(); + var height = api.getHeight(); + + var lastStackCoords = {}; + + var barWidthAndOffset = calRadialBar( + filter( + ecModel.getSeriesByType(seriesType), + function (seriesModel) { + return !ecModel.isSeriesFiltered(seriesModel) + && seriesModel.coordinateSystem + && seriesModel.coordinateSystem.type === 'polar'; + } + ) + ); + + ecModel.eachSeriesByType(seriesType, function (seriesModel) { + // Check series coordinate, do layout for polar only + if (seriesModel.coordinateSystem.type !== 'polar') { + return; + } + + var data = seriesModel.getData(); + var polar = seriesModel.coordinateSystem; + var baseAxis = polar.getBaseAxis(); + + var stackId = getSeriesStackId$1(seriesModel); + var columnLayoutInfo + = barWidthAndOffset[getAxisKey$1(baseAxis)][stackId]; + var columnOffset = columnLayoutInfo.offset; + var columnWidth = columnLayoutInfo.width; + var valueAxis = polar.getOtherAxis(baseAxis); + + var center = seriesModel.get('center') || ['50%', '50%']; + var cx = parsePercent$1(center[0], width); + var cy = parsePercent$1(center[1], height); + + var barMinHeight = seriesModel.get('barMinHeight') || 0; + var barMinAngle = seriesModel.get('barMinAngle') || 0; + + lastStackCoords[stackId] = lastStackCoords[stackId] || []; + + var valueDim = data.mapDimension(valueAxis.dim); + var baseDim = data.mapDimension(baseAxis.dim); + var stacked = isDimensionStacked(data, valueDim /*, baseDim*/); + + var valueAxisStart = valueAxis.getExtent()[0]; + + for (var idx = 0, len = data.count(); idx < len; idx++) { + var value = data.get(valueDim, idx); + var baseValue = data.get(baseDim, idx); + + if (isNaN(value)) { + continue; + } + + var sign = value >= 0 ? 'p' : 'n'; + var baseCoord = valueAxisStart; + + // Because of the barMinHeight, we can not use the value in + // stackResultDimension directly. + // Only ordinal axis can be stacked. + if (stacked) { + if (!lastStackCoords[stackId][baseValue]) { + lastStackCoords[stackId][baseValue] = { + p: valueAxisStart, // Positive stack + n: valueAxisStart // Negative stack + }; + } + // Should also consider #4243 + baseCoord = lastStackCoords[stackId][baseValue][sign]; + } + + var r0; + var r; + var startAngle; + var endAngle; + + // radial sector + if (valueAxis.dim === 'radius') { + var radiusSpan = valueAxis.dataToRadius(value) - valueAxisStart; + var angle = baseAxis.dataToAngle(baseValue); + + if (Math.abs(radiusSpan) < barMinHeight) { + radiusSpan = (radiusSpan < 0 ? -1 : 1) * barMinHeight; + } + + r0 = baseCoord; + r = baseCoord + radiusSpan; + startAngle = angle - columnOffset; + endAngle = startAngle - columnWidth; + + stacked && (lastStackCoords[stackId][baseValue][sign] = r); + } + // tangential sector + else { + // angleAxis must be clamped. + var angleSpan = valueAxis.dataToAngle(value, true) - valueAxisStart; + var radius = baseAxis.dataToRadius(baseValue); + + if (Math.abs(angleSpan) < barMinAngle) { + angleSpan = (angleSpan < 0 ? -1 : 1) * barMinAngle; + } + + r0 = radius + columnOffset; + r = r0 + columnWidth; + startAngle = baseCoord; + endAngle = baseCoord + angleSpan; + + // if the previous stack is at the end of the ring, + // add a round to differentiate it from origin + // var extent = angleAxis.getExtent(); + // var stackCoord = angle; + // if (stackCoord === extent[0] && value > 0) { + // stackCoord = extent[1]; + // } + // else if (stackCoord === extent[1] && value < 0) { + // stackCoord = extent[0]; + // } + stacked && (lastStackCoords[stackId][baseValue][sign] = endAngle); + } + + data.setItemLayout(idx, { + cx: cx, + cy: cy, + r0: r0, + r: r, + // Consider that positive angle is anti-clockwise, + // while positive radian of sector is clockwise + startAngle: -startAngle * Math.PI / 180, + endAngle: -endAngle * Math.PI / 180 + }); + + } + + }, this); + +} + +/** + * Calculate bar width and offset for radial bar charts + */ +function calRadialBar(barSeries, api) { + // Columns info on each category axis. Key is polar name + var columnsMap = {}; + + each$1(barSeries, function (seriesModel, idx) { + var data = seriesModel.getData(); + var polar = seriesModel.coordinateSystem; + + var baseAxis = polar.getBaseAxis(); + + var axisExtent = baseAxis.getExtent(); + var bandWidth = baseAxis.type === 'category' + ? baseAxis.getBandWidth() + : (Math.abs(axisExtent[1] - axisExtent[0]) / data.count()); + + var columnsOnAxis = columnsMap[getAxisKey$1(baseAxis)] || { + bandWidth: bandWidth, + remainedWidth: bandWidth, + autoWidthCount: 0, + categoryGap: '20%', + gap: '30%', + stacks: {} + }; + var stacks = columnsOnAxis.stacks; + columnsMap[getAxisKey$1(baseAxis)] = columnsOnAxis; + + var stackId = getSeriesStackId$1(seriesModel); + + if (!stacks[stackId]) { + columnsOnAxis.autoWidthCount++; + } + stacks[stackId] = stacks[stackId] || { + width: 0, + maxWidth: 0 + }; + + var barWidth = parsePercent$1( + seriesModel.get('barWidth'), + bandWidth + ); + var barMaxWidth = parsePercent$1( + seriesModel.get('barMaxWidth'), + bandWidth + ); + var barGap = seriesModel.get('barGap'); + var barCategoryGap = seriesModel.get('barCategoryGap'); + + 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 = {}; + + each$1(columnsMap, function (columnsOnAxis, coordSysName) { + + result[coordSysName] = {}; + + var stacks = columnsOnAxis.stacks; + var bandWidth = columnsOnAxis.bandWidth; + var categoryGap = parsePercent$1(columnsOnAxis.categoryGap, bandWidth); + var barGapPercent = parsePercent$1(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 + each$1(stacks, function (column, stack) { + var maxWidth = column.maxWidth; + if (maxWidth && maxWidth < autoWidth) { + maxWidth = Math.min(maxWidth, remainedWidth); + if (column.width) { + maxWidth = Math.min(maxWidth, column.width); + } + 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; + each$1(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; + each$1(stacks, function (column, stackId) { + result[coordSysName][stackId] = result[coordSysName][stackId] || { + offset: offset, + width: column.width + }; + + offset += column.width * (1 + barGapPercent); + }); + }); + + return result; +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +function RadiusAxis(scale, radiusExtent) { + + Axis.call(this, 'radius', scale, radiusExtent); + + /** + * Axis type + * - 'category' + * - 'value' + * - 'time' + * - 'log' + * @type {string} + */ + this.type = 'category'; +} + +RadiusAxis.prototype = { + + constructor: RadiusAxis, + + /** + * @override + */ + pointToData: function (point, clamp) { + return this.polar.pointToData(point, clamp)[this.dim === 'radius' ? 0 : 1]; + }, + + dataToRadius: Axis.prototype.dataToCoord, + + radiusToData: Axis.prototype.coordToData +}; + +inherits(RadiusAxis, Axis); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +function 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, + + /** + * @override + */ + pointToData: function (point, clamp) { + return this.polar.pointToData(point, clamp)[this.dim === 'radius' ? 0 : 1]; + }, + + dataToAngle: Axis.prototype.dataToCoord, + + angleToData: Axis.prototype.coordToData +}; + +inherits(AngleAxis, Axis); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * @module echarts/coord/polar/Polar + */ + +/** + * @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(); + + this._radiusAxis.polar = this._angleAxis.polar = this; +}; + +Polar.prototype = { + + type: 'polar', + + axisPointerEnabled: true, + + constructor: Polar, + + /** + * @param {Array.} + * @readOnly + */ + dimensions: ['radius', 'angle'], + + /** + * @type {module:echarts/coord/PolarModel} + */ + model: null, + + /** + * 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} dim + * @return {module:echarts/coord/polar/AngleAxis|module:echarts/coord/polar/RadiusAxis} + */ + getAxis: function (dim) { + return this['_' + dim + 'Axis']; + }, + + /** + * @return {Array.} + */ + getAxes: function () { + return [this._radiusAxis, this._angleAxis]; + }, + + /** + * 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(); + }, + + /** + * @param {string} [dim] 'radius' or 'angle' or 'auto' or null/undefined + * @return {Object} {baseAxes: [], otherAxes: []} + */ + getTooltipAxes: function (dim) { + var baseAxis = (dim != null && dim !== 'auto') + ? this.getAxis(dim) : this.getBaseAxis(); + return { + baseAxes: [baseAxis], + otherAxes: [this.getOtherAxis(baseAxis)] + }; + }, + + /** + * 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]; + } + +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var PolarAxisModel = ComponentModel.extend({ + + type: 'polarAxis', + + /** + * @type {module:echarts/coord/polar/AngleAxis|module:echarts/coord/polar/RadiusAxis} + */ + axis: null, + + /** + * @override + */ + getCoordSysModel: function () { + return this.ecModel.queryComponents({ + mainType: 'polar', + index: this.option.polarIndex, + id: this.option.polarId + })[0]; + } + +}); + +merge(PolarAxisModel.prototype, axisModelCommonMixin); + +var polarAxisDefaultExtendedOption = { + angle: { + // polarIndex: 0, + // polarId: '', + + startAngle: 90, + + clockwise: true, + + splitNumber: 12, + + axisLabel: { + rotate: false + } + }, + radius: { + // polarIndex: 0, + // polarId: '', + + splitNumber: 5 + } +}; + +function getAxisType$3(axisDim, option) { + // Default axis with data is category axis + return option.type || (option.data ? 'category' : 'value'); +} + +axisModelCreator('angle', PolarAxisModel, getAxisType$3, polarAxisDefaultExtendedOption.angle); +axisModelCreator('radius', PolarAxisModel, getAxisType$3, polarAxisDefaultExtendedOption.radius); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +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) { + if (axisModel.getCoordSysModel() === this) { + foundAxisModel = axisModel; + } + }, this); + return foundAxisModel; + }, + + defaultOption: { + + zlevel: 0, + + z: 0, + + center: ['50%', '50%'], + + radius: '80%' + } +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +// TODO Axis scale + +/** + * Resize method bound to the polar + * @param {module:echarts/coord/polar/PolarModel} polarModel + * @param {module:echarts/ExtensionAPI} api + */ +function resizePolar(polar, polarModel, api) { + var center = polarModel.get('center'); + var width = api.getWidth(); + var height = api.getHeight(); + + polar.cx = parsePercent$1(center[0], width); + polar.cy = parsePercent$1(center[1], height); + + var radiusAxis = polar.getRadiusAxis(); + var size = Math.min(width, height) / 2; + var radius = parsePercent$1(polarModel.get('radius'), size); + radiusAxis.inverse + ? radiusAxis.setExtent(radius, 0) + : radiusAxis.setExtent(0, radius); +} + +/** + * 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(); + each$1(data.mapDimension('radius', true), function (dim) { + radiusAxis.scale.unionExtentFromData( + data, getStackedDimension(data, dim) + ); + }); + each$1(data.mapDimension('angle', true), function (dim) { + angleAxis.scale.unionExtentFromData( + data, getStackedDimension(data, dim) + ); + }); + } + }); + + niceScaleExtent(angleAxis.scale, angleAxis.model); + niceScaleExtent(radiusAxis.scale, 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 = createScaleByModel(axisModel); + axis.onBand = axisModel.get('boundaryGap') && axis.type === 'category'; + axis.inverse = axisModel.get('inverse'); + + if (axisModel.mainType === 'angleAxis') { + axis.inverse ^= axisModel.get('clockwise'); + var startAngle = axisModel.get('startAngle'); + 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.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); + + resizePolar(polar, polarModel, api); + + polarList.push(polar); + + polarModel.coordinateSystem = polar; + polar.model = polarModel; + }); + // 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 (__DEV__) { + if (!polarModel) { + throw new Error( + 'Polar "' + retrieve( + seriesModel.get('polarIndex'), + seriesModel.get('polarId'), + 0 + ) + '" not found' + ); + } + } + seriesModel.coordinateSystem = polarModel.coordinateSystem; + } + }); + + return polarList; + } +}; + +CoordinateSystemManager.register('polar', polarCreator); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var elementList$1 = ['axisLine', 'axisLabel', 'axisTick', 'splitLine', 'splitArea']; + +function getAxisLineShape(polar, rExtent, angle) { + rExtent[1] > rExtent[0] && (rExtent = rExtent.slice().reverse()); + var start = polar.coordToPoint([rExtent[0], angle]); + var end = polar.coordToPoint([rExtent[1], angle]); + + return { + x1: start[0], + y1: start[1], + x2: end[0], + y2: end[1] + }; +} + +function getRadiusIdx(polar) { + var radiusAxis = polar.getRadiusAxis(); + return radiusAxis.inverse ? 0 : 1; +} + +// Remove the last tick which will overlap the first tick +function fixAngleOverlap(list) { + var firstItem = list[0]; + var lastItem = list[list.length - 1]; + if (firstItem + && lastItem + && Math.abs(Math.abs(firstItem.coord - lastItem.coord) - 360) < 1e-4 + ) { + list.pop(); + } +} + +AxisView.extend({ + + type: 'angleAxis', + + axisPointerClass: 'PolarAxisPointer', + + render: function (angleAxisModel, ecModel) { + this.group.removeAll(); + if (!angleAxisModel.get('show')) { + return; + } + + var angleAxis = angleAxisModel.axis; + var polar = angleAxis.polar; + var radiusExtent = polar.getRadiusAxis().getExtent(); + + var ticksAngles = angleAxis.getTicksCoords(); + var labels = map(angleAxis.getViewLabels(), function (labelItem) { + var labelItem = clone(labelItem); + labelItem.coord = angleAxis.dataToCoord(labelItem.tickValue); + return labelItem; + }); + + fixAngleOverlap(labels); + fixAngleOverlap(ticksAngles); + + each$1(elementList$1, function (name) { + if (angleAxisModel.get(name +'.show') + && (!angleAxis.scale.isBlank() || name === 'axisLine') + ) { + this['_' + name](angleAxisModel, polar, ticksAngles, radiusExtent, labels); + } + }, this); + }, + + /** + * @private + */ + _axisLine: function (angleAxisModel, polar, ticksAngles, radiusExtent) { + var lineStyleModel = angleAxisModel.getModel('axisLine.lineStyle'); + + var circle = new Circle({ + shape: { + cx: polar.cx, + cy: polar.cy, + r: radiusExtent[getRadiusIdx(polar)] + }, + 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 radius = radiusExtent[getRadiusIdx(polar)]; + + var lines = map(ticksAngles, function (tickAngleItem) { + return new Line({ + shape: getAxisLineShape(polar, [radius, radius + tickLen], tickAngleItem.coord) + }); + }); + this.group.add(mergePath( + lines, { + style: defaults( + tickModel.getModel('lineStyle').getLineStyle(), + { + stroke: angleAxisModel.get('axisLine.lineStyle.color') + } + ) + } + )); + }, + + /** + * @private + */ + _axisLabel: function (angleAxisModel, polar, ticksAngles, radiusExtent, labels) { + var rawCategoryData = angleAxisModel.getCategories(true); + + var commonLabelModel = angleAxisModel.getModel('axisLabel'); + + var labelMargin = commonLabelModel.get('margin'); + + // Use length of ticksAngles because it may remove the last tick to avoid overlapping + each$1(labels, function (labelItem, idx) { + var labelModel = commonLabelModel; + var tickValue = labelItem.tickValue; + + var r = radiusExtent[getRadiusIdx(polar)]; + var p = polar.coordToPoint([r + labelMargin, labelItem.coord]); + 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 labelTextVerticalAlign = Math.abs(p[1] - cy) / r < 0.3 + ? 'middle' : (p[1] > cy ? 'top' : 'bottom'); + + if (rawCategoryData && rawCategoryData[tickValue] && rawCategoryData[tickValue].textStyle) { + labelModel = new Model(rawCategoryData[tickValue].textStyle, commonLabelModel, commonLabelModel.ecModel); + } + + var textEl = new Text({silent: true}); + this.group.add(textEl); + setTextStyle(textEl.style, labelModel, { + x: p[0], + y: p[1], + textFill: labelModel.getTextColor() || angleAxisModel.get('axisLine.lineStyle.color'), + text: labelItem.formattedLabel, + textAlign: labelTextAlign, + textVerticalAlign: labelTextVerticalAlign + }); + }, this); + }, + + /** + * @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 Line({ + shape: getAxisLineShape(polar, radiusExtent, ticksAngles[i].coord) + })); + } + + // Simple optimization + // Batching the lines if color are the same + for (var i = 0; i < splitLines.length; i++) { + this.group.add(mergePath(splitLines[i], { + style: defaults({ + stroke: lineColors[i % lineColors.length] + }, lineStyleModel.getLineStyle()), + silent: true, + z: angleAxisModel.get('z') + })); + } + }, + + /** + * @private + */ + _splitArea: function (angleAxisModel, polar, ticksAngles, radiusExtent) { + if (!ticksAngles.length) { + return; + } + + 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].coord * 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 Sector({ + shape: { + cx: polar.cx, + cy: polar.cy, + r0: r0, + r: r1, + startAngle: prevAngle, + endAngle: -ticksAngles[i].coord * RADIAN, + clockwise: clockwise + }, + silent: true + })); + prevAngle = -ticksAngles[i].coord * RADIAN; + } + + // Simple optimization + // Batching the lines if color are the same + for (var i = 0; i < splitAreas.length; i++) { + this.group.add(mergePath(splitAreas[i], { + style: defaults({ + fill: areaColors[i % areaColors.length] + }, areaStyleModel.getAreaStyle()), + silent: true + })); + } + } +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var axisBuilderAttrs$3 = [ + 'axisLine', 'axisTickLabel', 'axisName' +]; +var selfBuilderAttrs$1 = [ + 'splitLine', 'splitArea' +]; + +AxisView.extend({ + + type: 'radiusAxis', + + axisPointerClass: 'PolarAxisPointer', + + render: function (radiusAxisModel, ecModel) { + this.group.removeAll(); + if (!radiusAxisModel.get('show')) { + return; + } + var radiusAxis = radiusAxisModel.axis; + var polar = radiusAxis.polar; + var angleAxis = polar.getAngleAxis(); + 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); + each$1(axisBuilderAttrs$3, axisBuilder.add, axisBuilder); + this.group.add(axisBuilder.getGroup()); + + each$1(selfBuilderAttrs$1, function (name) { + if (radiusAxisModel.get(name +'.show') && !radiusAxis.scale.isBlank()) { + 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 Circle({ + shape: { + cx: polar.cx, + cy: polar.cy, + r: ticksCoords[i].coord + }, + silent: true + })); + } + + // Simple optimization + // Batching the lines if color are the same + for (var i = 0; i < splitLines.length; i++) { + this.group.add(mergePath(splitLines[i], { + style: defaults({ + stroke: lineColors[i % lineColors.length], + fill: null + }, lineStyleModel.getLineStyle()), + silent: true + })); + } + }, + + /** + * @private + */ + _splitArea: function (radiusAxisModel, polar, axisAngle, radiusExtent, ticksCoords) { + if (!ticksCoords.length) { + return; + } + + 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].coord; + for (var i = 1; i < ticksCoords.length; i++) { + var colorIndex = (lineCount++) % areaColors.length; + splitAreas[colorIndex] = splitAreas[colorIndex] || []; + splitAreas[colorIndex].push(new Sector({ + shape: { + cx: polar.cx, + cy: polar.cy, + r0: prevRadius, + r: ticksCoords[i].coord, + startAngle: 0, + endAngle: Math.PI * 2 + }, + silent: true + })); + prevRadius = ticksCoords[i].coord; + } + + // Simple optimization + // Batching the lines if color are the same + for (var i = 0; i < splitAreas.length; i++) { + this.group.add(mergePath(splitAreas[i], { + style: 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, + labelRotate: radiusAxisModel.getModel('axisLabel').get('rotate'), + // Over splitLine and splitArea + z2: 1 + }; +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var PolarAxisPointer = BaseAxisPointer.extend({ + + /** + * @override + */ + makeElOption: function (elOption, value, axisModel, axisPointerModel, api) { + var axis = axisModel.axis; + + if (axis.dim === 'angle') { + this.animationThreshold = Math.PI / 18; + } + + var polar = axis.polar; + var otherAxis = polar.getOtherAxis(axis); + var otherExtent = otherAxis.getExtent(); + + var coordValue; + coordValue = axis['dataTo' + capitalFirst(axis.dim)](value); + + var axisPointerType = axisPointerModel.get('type'); + if (axisPointerType && axisPointerType !== 'none') { + var elStyle = buildElStyle(axisPointerModel); + var pointerOption = pointerShapeBuilder$2[axisPointerType]( + axis, polar, coordValue, otherExtent, elStyle + ); + pointerOption.style = elStyle; + elOption.graphicKey = pointerOption.type; + elOption.pointer = pointerOption; + } + + var labelMargin = axisPointerModel.get('label.margin'); + var labelPos = getLabelPosition(value, axisModel, axisPointerModel, polar, labelMargin); + buildLabelElOption(elOption, axisModel, axisPointerModel, api, labelPos); + } + + // Do not support handle, utill any user requires it. + +}); + +function getLabelPosition(value, axisModel, axisPointerModel, polar, labelMargin) { + var axis = axisModel.axis; + var coord = axis.dataToCoord(value); + var axisAngle = polar.getAngleAxis().getExtent()[0]; + axisAngle = axisAngle / 180 * Math.PI; + var radiusExtent = polar.getRadiusAxis().getExtent(); + var position; + var align; + var verticalAlign; + + if (axis.dim === 'radius') { + var transform = create$1(); + rotate(transform, transform, axisAngle); + translate(transform, transform, [polar.cx, polar.cy]); + position = applyTransform$1([coord, -labelMargin], transform); + + var labelRotation = axisModel.getModel('axisLabel').get('rotate') || 0; + var labelLayout = AxisBuilder.innerTextLayout( + axisAngle, labelRotation * Math.PI / 180, -1 + ); + align = labelLayout.textAlign; + verticalAlign = labelLayout.textVerticalAlign; + } + else { // angle axis + var r = radiusExtent[1]; + position = polar.coordToPoint([r + labelMargin, coord]); + var cx = polar.cx; + var cy = polar.cy; + align = Math.abs(position[0] - cx) / r < 0.3 + ? 'center' : (position[0] > cx ? 'left' : 'right'); + verticalAlign = Math.abs(position[1] - cy) / r < 0.3 + ? 'middle' : (position[1] > cy ? 'top' : 'bottom'); + } + + return { + position: position, + align: align, + verticalAlign: verticalAlign + }; +} + + +var pointerShapeBuilder$2 = { + + line: function (axis, polar, coordValue, otherExtent, elStyle) { + return axis.dim === 'angle' + ? { + type: 'Line', + shape: makeLineShape( + polar.coordToPoint([otherExtent[0], coordValue]), + polar.coordToPoint([otherExtent[1], coordValue]) + ) + } + : { + type: 'Circle', + shape: { + cx: polar.cx, + cy: polar.cy, + r: coordValue + } + }; + }, + + shadow: function (axis, polar, coordValue, otherExtent, elStyle) { + var bandWidth = Math.max(1, axis.getBandWidth()); + var radian = Math.PI / 180; + + return axis.dim === 'angle' + ? { + type: 'Sector', + shape: makeSectorShape( + polar.cx, polar.cy, + otherExtent[0], otherExtent[1], + // In ECharts y is negative if angle is positive + (-coordValue - bandWidth / 2) * radian, + (-coordValue + bandWidth / 2) * radian + ) + } + : { + type: 'Sector', + shape: makeSectorShape( + polar.cx, polar.cy, + coordValue - bandWidth / 2, + coordValue + bandWidth / 2, + 0, Math.PI * 2 + ) + }; + } +}; + +AxisView.registerAxisPointerClass('PolarAxisPointer', PolarAxisPointer); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +// For reducing size of echarts.min, barLayoutPolar is required by polar. +registerLayout(curry(barLayoutPolar, 'bar')); + +// Polar view +extendComponentView({ + type: 'polar' +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +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 `show` + defaultEmphasis(option, 'label', ['show']); + }, + + optionUpdated: function () { + var option = this.option; + var self = this; + + option.regions = geoCreator.getFilledRegions(option.regions, option.map, option.nameMap); + + this._optionModelMap = reduce(option.regions || [], function (optionModelMap, regionOpt) { + if (regionOpt.name) { + optionModelMap.set(regionOpt.name, new Model(regionOpt, self)); + } + return optionModelMap; + }, createHashMap()); + + 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: '', + + // Define left-top, right-bottom coords to control view + // For example, [ [180, 90], [-180, -90] ] + boundingCoords: null, + + // Default on center of map + center: null, + + zoom: 1, + + scaleLimit: null, + + // selectedMode: false + + label: { + show: false, + color: '#000' + }, + + itemStyle: { + // color: 各异, + borderWidth: 0.5, + borderColor: '#444', + color: '#eee' + }, + + emphasis: { + label: { + show: true, + color: 'rgb(100,0,0)' + }, + itemStyle: { + 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.get(name) || new Model(null, this, this.ecModel); + }, + + /** + * Format label + * @param {string} name Region name + * @param {string} [status='normal'] 'normal' or 'emphasis' + * @return {string} + */ + getFormattedLabel: function (name, status) { + var regionModel = this.getRegionModel(name); + var formatter = regionModel.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}', name != null ? name : ''); + } + }, + + setZoom: function (zoom) { + this.option.zoom = zoom; + }, + + setCenter: function (center) { + this.option.center = center; + } +}); + +mixin(GeoModel, selectableMixin); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +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'); + }, + + dispose: function () { + this._mapDraw && this._mapDraw.remove(); + } + +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +function makeAction(method, actionInfo) { + actionInfo.update = 'updateView'; + registerAction(actionInfo, function (payload, ecModel) { + var selected = {}; + + ecModel.eachComponent( + { mainType: 'geo', query: payload}, + function (geoModel) { + geoModel[method](payload.name); + var geo = geoModel.coordinateSystem; + each$1(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' +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var DEFAULT_TOOLBOX_BTNS = ['rect', 'polygon', 'keep', 'clear']; + +var preprocessor$1 = function (option, isNew) { + var brushComponents = option && option.brush; + if (!isArray(brushComponents)) { + brushComponents = brushComponents ? [brushComponents] : []; + } + + if (!brushComponents.length) { + return; + } + + var brushComponentSpecifiedBtns = []; + + each$1(brushComponents, function (brushOpt) { + var tbs = brushOpt.hasOwnProperty('toolbox') + ? brushOpt.toolbox : []; + + if (tbs instanceof Array) { + brushComponentSpecifiedBtns = brushComponentSpecifiedBtns.concat(tbs); + } + }); + + var toolbox = option && option.toolbox; + + if (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$$1 = {}; + each$1(arr, function (val) { + map$$1[val] = 1; + }); + arr.length = 0; + each$1(map$$1, function (flag, val) { + arr.push(val); + }); +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * @file Visual solution, for consistent option specification. + */ + +var each$19 = each$1; + +function hasKeys(obj) { + if (obj) { + for (var name in obj){ + if (obj.hasOwnProperty(name)) { + return true; + } + } + } +} + +/** + * @param {Object} option + * @param {Array.} stateList + * @param {Function} [supplementVisualOption] + * @return {Object} visualMappings > + */ +function createVisualMappings(option, stateList, supplementVisualOption) { + var visualMappings = {}; + + each$19(stateList, function (state) { + var mappings = visualMappings[state] = createMappings(); + + each$19(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 = 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 + */ +function replaceVisualOption(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; + each$1(keys, function (key) { + if (newOption.hasOwnProperty(key) && hasKeys(newOption[key])) { + has = true; + } + }); + has && each$1(keys, function (key) { + if (newOption.hasOwnProperty(key) && hasKeys(newOption[key])) { + thisOption[key] = 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. + */ +// ???! handle brush? +function applyVisual(stateList, visualMappings, data, getValueState, scope, dimension) { + var visualTypesMap = {}; + each$1(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); + } + else { + data.each([dimension], eachItem); + } + + function eachItem(valueOrIndex, index) { + dataIndex = dimension == null ? valueOrIndex : index; + + var rawDataItem = data.getRawDataItem(dataIndex); + // Consider performance + if (rawDataItem && rawDataItem.visualMap === false) { + return; + } + + 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 + ); + } + } +} + +/** + * @param {module:echarts/data/List} data + * @param {Array.} stateList + * @param {Object} visualMappings > + * @param {Function} getValueState param: valueOrIndex, return: state. + * @param {number} [dim] dimension or dimension index. + */ +function incrementalApplyVisual(stateList, visualMappings, getValueState, dim) { + var visualTypesMap = {}; + each$1(stateList, function (state) { + var visualTypes = VisualMapping.prepareVisualTypes(visualMappings[state]); + visualTypesMap[state] = visualTypes; + }); + + function progress(params, data) { + if (dim != null) { + dim = data.getDimension(dim); + } + + function getVisual(key) { + return data.getItemVisual(dataIndex, key); + } + + function setVisual(key, value) { + data.setItemVisual(dataIndex, key, value); + } + + var dataIndex; + while ((dataIndex = params.next()) != null) { + var rawDataItem = data.getRawDataItem(dataIndex); + + // Consider performance + if (rawDataItem && rawDataItem.visualMap === false) { + return; + } + + var value = dim != null + ? data.get(dim, dataIndex, true) + : dataIndex; + + var valueState = getValueState(value); + 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(value, getVisual, setVisual); + } + } + } + + return {progress: progress}; +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +// 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 itemLayout && area.boundingRect.contain(itemLayout[0], itemLayout[1]); + }, + rect: function (itemLayout, selectors, area) { + return itemLayout && area.boundingRect.intersect(itemLayout); + } + }, + polygon: { + point: function (itemLayout, selectors, area) { + return itemLayout + && area.boundingRect.contain(itemLayout[0], itemLayout[1]) + && contain$1(area.range, itemLayout[0], itemLayout[1]); + }, + rect: function (itemLayout, selectors, area) { + var points = area.range; + + if (!itemLayout || 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 (contain$1(points, x, y) + || contain$1(points, x + width, y) + || contain$1(points, x, y + height) + || contain$1(points, x + width, y + height) + || BoundingRect.create(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) { + if (itemLayout) { + var range = area.range; + var p = itemLayout[xyIndex]; + return inLineRange(p, range); + } + }, + rect: function (itemLayout, selectors, area) { + if (itemLayout) { + var range = area.range; + var layoutRange = [ + itemLayout[xy[xyIndex]], + itemLayout[xy[xyIndex]] + itemLayout[wh[xyIndex]] + ]; + layoutRange[1] < layoutRange[0] && layoutRange.reverse(); + return inLineRange(layoutRange[0], range) + || inLineRange(layoutRange[1], range) + || inLineRange(range[0], layoutRange) + || inLineRange(range[1], layoutRange); + } + } + }; +} + +function inLineRange(p, range) { + return range[0] <= p && p <= range[1]; +} + +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; +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var each$20 = each$1; +var indexOf$1 = indexOf; +var curry$5 = curry; + +var COORD_CONVERTS = ['dataToPoint', 'pointToData']; + +// FIXME +// how to genarialize to more coordinate systems. +var INCLUDE_FINDER_MAIN_TYPES = [ + 'grid', 'xAxis', 'yAxis', 'geo', 'graph', + 'polar', 'radiusAxis', 'angleAxis', 'bmap' +]; + +/** + * [option in constructor]: + * { + * Index/Id/Name of geo, xAxis, yAxis, grid: See util/model#parseFinder. + * } + * + * + * [targetInfo]: + * + * There can be multiple axes in a single targetInfo. Consider the case + * of `grid` component, a targetInfo represents a grid which contains one or more + * cartesian and one or more axes. And consider the case of parallel system, + * which has multiple axes in a coordinate system. + * Can be { + * panelId: ..., + * coordSys: , + * coordSyses: all cartesians. + * gridModel: + * xAxes: correspond to coordSyses on index + * yAxes: correspond to coordSyses on index + * } + * or { + * panelId: ..., + * coordSys: + * coordSyses: [] + * geoModel: + * } + * + * + * [panelOpt]: + * + * Make from targetInfo. Input to BrushController. + * { + * panelId: ..., + * rect: ... + * } + * + * + * [area]: + * + * Generated by BrushController or user input. + * { + * panelId: Used to locate coordInfo directly. If user inpput, no panelId. + * brushType: determine how to convert to/from coord('rect' or 'polygon' or 'lineX/Y'). + * Index/Id/Name of geo, xAxis, yAxis, grid: See util/model#parseFinder. + * range: pixel range. + * coordRange: representitive coord range (the first one of coordRanges). + * coordRanges: coord ranges, used in multiple cartesian in one grid. + * } + */ + +/** + * @param {Object} option contains Index/Id/Name of xAxis/yAxis/geo/grid + * Each can be {number|Array.}. like: {xAxisIndex: [3, 4]} + * @param {module:echarts/model/Global} ecModel + * @param {Object} [opt] + * @param {Array.} [opt.include] include coordinate system types. + */ +function BrushTargetManager(option, ecModel, opt) { + /** + * @private + * @type {Array.} + */ + var targetInfoList = this._targetInfoList = []; + var info = {}; + var foundCpts = parseFinder$1(ecModel, option); + + each$20(targetInfoBuilders, function (builder, type) { + if (!opt || !opt.include || indexOf$1(opt.include, type) >= 0) { + builder(foundCpts, targetInfoList, info); + } + }); +} + +var proto$2 = BrushTargetManager.prototype; + +proto$2.setOutputRanges = function (areas, ecModel) { + this.matchOutputRanges(areas, ecModel, function (area, coordRange, coordSys) { + (area.coordRanges || (area.coordRanges = [])).push(coordRange); + // area.coordRange is the first of area.coordRanges + if (!area.coordRange) { + area.coordRange = coordRange; + // In 'category' axis, coord to pixel is not reversible, so we can not + // rebuild range by coordRange accrately, which may bring trouble when + // brushing only one item. So we use __rangeOffset to rebuilding range + // by coordRange. And this it only used in brush component so it is no + // need to be adapted to coordRanges. + var result = coordConvert[area.brushType](0, coordSys, coordRange); + area.__rangeOffset = { + offset: diffProcessor[area.brushType](result.values, area.range, [1, 1]), + xyMinMax: result.xyMinMax + }; + } + }); +}; + +proto$2.matchOutputRanges = function (areas, ecModel, cb) { + each$20(areas, function (area) { + var targetInfo = this.findTargetInfo(area, ecModel); + + if (targetInfo && targetInfo !== true) { + each$1( + targetInfo.coordSyses, + function (coordSys) { + var result = coordConvert[area.brushType](1, coordSys, area.range); + cb(area, result.values, coordSys, ecModel); + } + ); + } + }, this); +}; + +proto$2.setInputRanges = function (areas, ecModel) { + each$20(areas, function (area) { + var targetInfo = this.findTargetInfo(area, ecModel); + + if (__DEV__) { + assert$1( + !targetInfo || targetInfo === true || area.coordRange, + 'coordRange must be specified when coord index specified.' + ); + assert$1( + !targetInfo || targetInfo !== true || area.range, + 'range must be specified in global brush.' + ); + } + + area.range = area.range || []; + + // convert coordRange to global range and set panelId. + if (targetInfo && targetInfo !== true) { + area.panelId = targetInfo.panelId; + // (1) area.range shoule always be calculate from coordRange but does + // not keep its original value, for the sake of the dataZoom scenario, + // where area.coordRange remains unchanged but area.range may be changed. + // (2) Only support converting one coordRange to pixel range in brush + // component. So do not consider `coordRanges`. + // (3) About __rangeOffset, see comment above. + var result = coordConvert[area.brushType](0, targetInfo.coordSys, area.coordRange); + var rangeOffset = area.__rangeOffset; + area.range = rangeOffset + ? diffProcessor[area.brushType]( + result.values, + rangeOffset.offset, + getScales(result.xyMinMax, rangeOffset.xyMinMax) + ) + : result.values; + } + }, this); +}; + +proto$2.makePanelOpts = function (api, getDefaultBrushType) { + return map(this._targetInfoList, function (targetInfo) { + var rect = targetInfo.getPanelRect(); + return { + panelId: targetInfo.panelId, + defaultBrushType: getDefaultBrushType && getDefaultBrushType(targetInfo), + clipPath: makeRectPanelClipPath(rect), + isTargetByCursor: makeRectIsTargetByCursor( + rect, api, targetInfo.coordSysModel + ), + getLinearBrushOtherExtent: makeLinearBrushOtherExtent(rect) + }; + }); +}; + +proto$2.controlSeries = function (area, seriesModel, ecModel) { + // 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 targetInfo = this.findTargetInfo(area, ecModel); + return targetInfo === true || ( + targetInfo && indexOf$1(targetInfo.coordSyses, seriesModel.coordinateSystem) >= 0 + ); +}; + +/** + * If return Object, a coord found. + * If reutrn true, global found. + * Otherwise nothing found. + * + * @param {Object} area + * @param {Array} targetInfoList + * @return {Object|boolean} + */ +proto$2.findTargetInfo = function (area, ecModel) { + var targetInfoList = this._targetInfoList; + var foundCpts = parseFinder$1(ecModel, area); + + for (var i = 0; i < targetInfoList.length; i++) { + var targetInfo = targetInfoList[i]; + var areaPanelId = area.panelId; + if (areaPanelId) { + if (targetInfo.panelId === areaPanelId) { + return targetInfo; + } + } + else { + for (var i = 0; i < targetInfoMatchers.length; i++) { + if (targetInfoMatchers[i](foundCpts, targetInfo)) { + return targetInfo; + } + } + } + } + + return true; +}; + +function formatMinMax(minMax) { + minMax[0] > minMax[1] && minMax.reverse(); + return minMax; +} + +function parseFinder$1(ecModel, option) { + return parseFinder( + ecModel, option, {includeMainTypes: INCLUDE_FINDER_MAIN_TYPES} + ); +} + +var targetInfoBuilders = { + + grid: function (foundCpts, targetInfoList) { + var xAxisModels = foundCpts.xAxisModels; + var yAxisModels = foundCpts.yAxisModels; + var gridModels = foundCpts.gridModels; + // Remove duplicated. + var gridModelMap = createHashMap(); + var xAxesHas = {}; + var yAxesHas = {}; + + if (!xAxisModels && !yAxisModels && !gridModels) { + return; + } + + each$20(xAxisModels, function (axisModel) { + var gridModel = axisModel.axis.grid.model; + gridModelMap.set(gridModel.id, gridModel); + xAxesHas[gridModel.id] = true; + }); + each$20(yAxisModels, function (axisModel) { + var gridModel = axisModel.axis.grid.model; + gridModelMap.set(gridModel.id, gridModel); + yAxesHas[gridModel.id] = true; + }); + each$20(gridModels, function (gridModel) { + gridModelMap.set(gridModel.id, gridModel); + xAxesHas[gridModel.id] = true; + yAxesHas[gridModel.id] = true; + }); + + gridModelMap.each(function (gridModel) { + var grid = gridModel.coordinateSystem; + var cartesians = []; + + each$20(grid.getCartesians(), function (cartesian, index) { + if (indexOf$1(xAxisModels, cartesian.getAxis('x').model) >= 0 + || indexOf$1(yAxisModels, cartesian.getAxis('y').model) >= 0 + ) { + cartesians.push(cartesian); + } + }); + targetInfoList.push({ + panelId: 'grid--' + gridModel.id, + gridModel: gridModel, + coordSysModel: gridModel, + // Use the first one as the representitive coordSys. + coordSys: cartesians[0], + coordSyses: cartesians, + getPanelRect: panelRectBuilder.grid, + xAxisDeclared: xAxesHas[gridModel.id], + yAxisDeclared: yAxesHas[gridModel.id] + }); + }); + }, + + geo: function (foundCpts, targetInfoList) { + each$20(foundCpts.geoModels, function (geoModel) { + var coordSys = geoModel.coordinateSystem; + targetInfoList.push({ + panelId: 'geo--' + geoModel.id, + geoModel: geoModel, + coordSysModel: geoModel, + coordSys: coordSys, + coordSyses: [coordSys], + getPanelRect: panelRectBuilder.geo + }); + }); + } +}; + +var targetInfoMatchers = [ + + // grid + function (foundCpts, targetInfo) { + var xAxisModel = foundCpts.xAxisModel; + var yAxisModel = foundCpts.yAxisModel; + var gridModel = foundCpts.gridModel; + + !gridModel && xAxisModel && (gridModel = xAxisModel.axis.grid.model); + !gridModel && yAxisModel && (gridModel = yAxisModel.axis.grid.model); + + return gridModel && gridModel === targetInfo.gridModel; + }, + + // geo + function (foundCpts, targetInfo) { + var geoModel = foundCpts.geoModel; + return geoModel && geoModel === targetInfo.geoModel; + } +]; + +var panelRectBuilder = { + + grid: function () { + // grid is not Transformable. + return this.coordSys.grid.getRect().clone(); + }, + + geo: function () { + var coordSys = this.coordSys; + var rect = coordSys.getBoundingRect().clone(); + // geo roam and zoom transform + rect.applyTransform(getTransform(coordSys)); + return rect; + } +}; + +var coordConvert = { + + lineX: curry$5(axisConvert, 0), + + lineY: curry$5(axisConvert, 1), + + rect: function (to, coordSys, rangeOrCoordRange) { + var xminymin = coordSys[COORD_CONVERTS[to]]([rangeOrCoordRange[0][0], rangeOrCoordRange[1][0]]); + var xmaxymax = coordSys[COORD_CONVERTS[to]]([rangeOrCoordRange[0][1], rangeOrCoordRange[1][1]]); + var values = [ + formatMinMax([xminymin[0], xmaxymax[0]]), + formatMinMax([xminymin[1], xmaxymax[1]]) + ]; + return {values: values, xyMinMax: values}; + }, + + polygon: function (to, coordSys, rangeOrCoordRange) { + var xyMinMax = [[Infinity, -Infinity], [Infinity, -Infinity]]; + var values = map(rangeOrCoordRange, function (item) { + var p = coordSys[COORD_CONVERTS[to]](item); + xyMinMax[0][0] = Math.min(xyMinMax[0][0], p[0]); + xyMinMax[1][0] = Math.min(xyMinMax[1][0], p[1]); + xyMinMax[0][1] = Math.max(xyMinMax[0][1], p[0]); + xyMinMax[1][1] = Math.max(xyMinMax[1][1], p[1]); + return p; + }); + return {values: values, xyMinMax: xyMinMax}; + } +}; + +function axisConvert(axisNameIndex, to, coordSys, rangeOrCoordRange) { + if (__DEV__) { + assert$1( + coordSys.type === 'cartesian2d', + 'lineX/lineY brush is available only in cartesian2d.' + ); + } + + var axis = coordSys.getAxis(['x', 'y'][axisNameIndex]); + var values = formatMinMax(map([0, 1], function (i) { + return to + ? axis.coordToData(axis.toLocalCoord(rangeOrCoordRange[i])) + : axis.toGlobalCoord(axis.dataToCoord(rangeOrCoordRange[i])); + })); + var xyMinMax = []; + xyMinMax[axisNameIndex] = values; + xyMinMax[1 - axisNameIndex] = [NaN, NaN]; + + return {values: values, xyMinMax: xyMinMax}; +} + +var diffProcessor = { + lineX: curry$5(axisDiffProcessor, 0), + + lineY: curry$5(axisDiffProcessor, 1), + + rect: function (values, refer, scales) { + return [ + [values[0][0] - scales[0] * refer[0][0], values[0][1] - scales[0] * refer[0][1]], + [values[1][0] - scales[1] * refer[1][0], values[1][1] - scales[1] * refer[1][1]] + ]; + }, + + polygon: function (values, refer, scales) { + return map(values, function (item, idx) { + return [item[0] - scales[0] * refer[idx][0], item[1] - scales[1] * refer[idx][1]]; + }); + } +}; + +function axisDiffProcessor(axisNameIndex, values, refer, scales) { + return [ + values[0] - scales[axisNameIndex] * refer[0], + values[1] - scales[axisNameIndex] * refer[1] + ]; +} + +// We have to process scale caused by dataZoom manually, +// although it might be not accurate. +function getScales(xyMinMaxCurr, xyMinMaxOrigin) { + var sizeCurr = getSize(xyMinMaxCurr); + var sizeOrigin = getSize(xyMinMaxOrigin); + var scales = [sizeCurr[0] / sizeOrigin[0], sizeCurr[1] / sizeOrigin[1]]; + isNaN(scales[0]) && (scales[0] = 1); + isNaN(scales[1]) && (scales[1] = 1); + return scales; +} + +function getSize(xyMinMax) { + return xyMinMax + ? [xyMinMax[0][1] - xyMinMax[0][0], xyMinMax[1][1] - xyMinMax[1][0]] + : [NaN, NaN]; +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var STATE_LIST = ['inBrush', 'outOfBrush']; +var DISPATCH_METHOD = '__ecBrushSelect'; +var DISPATCH_FLAG = '__ecInBrushSelectEvent'; +var PRIORITY_BRUSH = PRIORITY.VISUAL.BRUSH; + +/** + * Layout for visual, the priority higher than other layout, and before brush visual. + */ +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} + ); + + var brushTargetManager = brushModel.brushTargetManager = new BrushTargetManager(brushModel.option, ecModel); + + brushTargetManager.setInputRanges(brushModel.areas, ecModel); + }); +}); + +/** + * Register the visual encoding if this modules required. + */ +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: 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 = map(brushModel.areas, function (area) { + return bindSelector( + defaults( + {boundingRect: boundingRectBuilders[area.brushType](area)}, + area + ) + ); + }); + + var visualMappings = createVisualMappings( + brushModel.option, STATE_LIST, function (mappingOption) { + mappingOption.mappingMethod = 'fixed'; + } + ); + + isArray(brushLink) && each$1(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; + } + + each$1(areas, function (area) { + selectorsByBrushType[area.brushType] + && brushModel.brushTargetManager.controlSeries(area, seriesModel, ecModel) + && 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)) + && 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 = 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) { + for (var i = 0, len = rangeInfoList.length; i < len; i++) { + var area = rangeInfoList[i]; + if (selectorsByBrushType[area.brushType]( + dataIndex, data, area.selectors, area + )) { + return true; + } + } +} + +function getSelectorsByBrushType(seriesModel) { + var brushSelector = seriesModel.brushSelector; + if (isString(brushSelector)) { + var sels = []; + each$1(selector, function (selectorsByElementType, brushType) { + sels[brushType] = function (dataIndex, data, selectors, area) { + var itemLayout = data.getItemLayout(dataIndex); + return selectorsByElementType[brushSelector](itemLayout, selectors, area); + }; + }); + return sels; + } + else if (isFunction$1(brushSelector)) { + var bSelector = {}; + each$1(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' + && ( + isArray(seriesIndices) + ? indexOf(seriesIndices, seriesIndex) < 0 + : seriesIndex !== seriesIndices + ); +} + +function bindSelector(area) { + var selectors = area.selectors = {}; + each$1(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: noop, + + lineY: 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] + ); +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var DEFAULT_OUT_OF_BRUSH_COLOR = ['#ddd']; + +var BrushModel = 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)' + }, + + 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, + + z: 10000 + }, + + /** + * @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 && replaceVisualOption( + thisOption, newOption, ['inBrush', 'outOfBrush'] + ); + + var inBrush = 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 (!inBrush.hasOwnProperty('liftZ')) { + // Bigger than the highlight z lift, otherwise it will + // be effected by the highlight z when brush. + inBrush.liftZ = 5; + } + }, + + /** + * If ranges is null/undefined, range state remain. + * + * @param {Array.} [ranges] + */ + setAreas: function (areas) { + if (__DEV__) { + assert$1(isArray(areas)); + each$1(areas, function (area) { + assert$1(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 = map(areas, function (area) { + return generateBrushOption(this.option, area); + }, this); + }, + + /** + * see module:echarts/component/helper/BrushController + * @param {Object} brushOption + */ + setBrushOption: function (brushOption) { + this.brushOption = generateBrushOption(this.option, brushOption); + this.brushType = this.brushOption.brushType; + } + +}); + +function generateBrushOption(option, brushOption) { + return merge( + { + brushType: option.brushType, + brushMode: option.brushMode, + transformable: option.transformable, + brushStyle: new Model(option.brushStyle).getItemStyle(), + removeOnClick: option.removeOnClick, + z: option.z + }, + brushOption, + true + ); +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +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', bind(this._onBrush, this)) + .mount(); + }, + + /** + * @override + */ + render: function (brushModel) { + this.model = brushModel; + return updateController.apply(this, arguments); + }, + + /** + * @override + */ + updateTransform: updateController, + + /** + * @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; + + this.model.brushTargetManager.setOutputRanges(areas, 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: clone(areas), + $from: modelId + }); + } + +}); + +function updateController(brushModel, ecModel, api, payload) { + // Do not update controller when drawing. + (!payload || payload.$from !== brushModel.id) && this._brushController + .setPanels(brushModel.brushTargetManager.makePanelOpts(api)) + .enableBrush(brushModel.brushOption) + .updateCovers(brushModel.areas.slice()); +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * payload: { + * brushIndex: number, or, + * brushId: string, or, + * brushName: string, + * globalRanges: Array + * } + */ +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, ...] + * }, + * ... + * ] + * }, + * ... + * ] + * } + */ +registerAction( + {type: 'brushSelect', event: 'brushSelected', update: 'none'}, + function () {} +); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + + +var features = {}; + +function register$1(name, ctor) { + features[name] = ctor; +} + +function get$1(name) { + return features[name]; +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var brushLang = lang.toolbox.brush; + +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 + }, + // `rect`, `polygon`, `lineX`, `lineY`, `keep`, `clear` + title: clone(brushLang.title) +}; + +var proto$3 = Brush.prototype; + +// proto.updateLayout = function (featureModel, ecModel, api) { +proto$3.render = +proto$3.updateView = 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; + + each$1(featureModel.get('type', true), function (type) { + featureModel.setIconStatus( + type, + ( + type === 'keep' + ? brushMode === 'multiple' + : type === 'clear' + ? isBrushed + : type === brushType + ) ? 'emphasis' : 'normal' + ); + }); +}; + +proto$3.getIcons = function () { + var model = this.model; + var availableIcons = model.get('icon', true); + var icons = {}; + each$1(model.get('type', true), function (type) { + if (availableIcons[type]) { + icons[type] = availableIcons[type]; + } + }); + return icons; +}; + +proto$3.onclick = function (ecModel, api, type) { + var brushType = this._brushType; + var brushMode = this._brushMode; + + if (type === 'clear') { + // Trigger parallel action firstly + api.dispatchAction({ + type: 'axisAreaSelect', + intervals: [] + }); + + api.dispatchAction({ + type: 'brush', + command: 'clear', + // 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 + } + }); + } +}; + +register$1('brush', Brush); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * Brush component entry + */ + +registerPreprocessor(preprocessor$1); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +// (24*60*60*1000) +var PROXIMATE_ONE_DAY = 86400000; + +/** + * Calendar + * + * @constructor + * + * @param {Object} calendarModel calendarModel + * @param {Object} ecModel ecModel + * @param {Object} api api + */ +function Calendar(calendarModel, ecModel, api) { + this._model = calendarModel; +} + +Calendar.prototype = { + + constructor: Calendar, + + type: 'calendar', + + dimensions: ['time', 'value'], + + // Required in createListFromData + getDimensionsInfo: function () { + return [{name: 'time', type: 'time'}, 'value']; + }, + + getRangeInfo: function () { + return this._rangeInfo; + }, + + getModel: function () { + return this._model; + }, + + getRect: function () { + return this._rect; + }, + + getCellWidth: function () { + return this._sw; + }, + + getCellHeight: function () { + return this._sh; + }, + + getOrient: function () { + return this._orient; + }, + + /** + * getFirstDayOfWeek + * + * @example + * 0 : start at Sunday + * 1 : start at Monday + * + * @return {number} + */ + getFirstDayOfWeek: function () { + return this._firstDayOfWeek; + }, + + /** + * get date info + * + * @param {string|number} date date + * @return {Object} + * { + * y: string, local full year, eg., '1940', + * m: string, local month, from '01' ot '12', + * d: string, local date, from '01' to '31' (if exists), + * day: It is not date.getDay(). It is the location of the cell in a week, from 0 to 6, + * time: timestamp, + * formatedDate: string, yyyy-MM-dd, + * date: original date object. + * } + */ + getDateInfo: function (date) { + + date = parseDate(date); + + var y = date.getFullYear(); + + var m = date.getMonth() + 1; + m = m < 10 ? '0' + m : m; + + var d = date.getDate(); + d = d < 10 ? '0' + d : d; + + var day = date.getDay(); + + day = Math.abs((day + 7 - this.getFirstDayOfWeek()) % 7); + + return { + y: y, + m: m, + d: d, + day: day, + time: date.getTime(), + formatedDate: y + '-' + m + '-' + d, + date: date + }; + }, + + getNextNDay: function (date, n) { + n = n || 0; + if (n === 0) { + return this.getDateInfo(date); + } + + date = new Date(this.getDateInfo(date).time); + date.setDate(date.getDate() + n); + + return this.getDateInfo(date); + }, + + update: function (ecModel, api) { + + this._firstDayOfWeek = +this._model.getModel('dayLabel').get('firstDay'); + this._orient = this._model.get('orient'); + this._lineWidth = this._model.getModel('itemStyle').getItemStyle().lineWidth || 0; + + + this._rangeInfo = this._getRangeInfo(this._initRangeOption()); + var weeks = this._rangeInfo.weeks || 1; + var whNames = ['width', 'height']; + var cellSize = this._model.get('cellSize').slice(); + var layoutParams = this._model.getBoxLayoutParams(); + var cellNumbers = this._orient === 'horizontal' ? [weeks, 7] : [7, weeks]; + + each$1([0, 1], function (idx) { + if (cellSizeSpecified(cellSize, idx)) { + layoutParams[whNames[idx]] = cellSize[idx] * cellNumbers[idx]; + } + }); + + var whGlobal = { + width: api.getWidth(), + height: api.getHeight() + }; + var calendarRect = this._rect = getLayoutRect(layoutParams, whGlobal); + + each$1([0, 1], function (idx) { + if (!cellSizeSpecified(cellSize, idx)) { + cellSize[idx] = calendarRect[whNames[idx]] / cellNumbers[idx]; + } + }); + + function cellSizeSpecified(cellSize, idx) { + return cellSize[idx] != null && cellSize[idx] !== 'auto'; + } + + this._sw = cellSize[0]; + this._sh = cellSize[1]; + }, + + + /** + * Convert a time data(time, value) item to (x, y) point. + * + * @override + * @param {Array|number} data data + * @param {boolean} [clamp=true] out of range + * @return {Array} point + */ + dataToPoint: function (data, clamp) { + isArray(data) && (data = data[0]); + clamp == null && (clamp = true); + + var dayInfo = this.getDateInfo(data); + var range = this._rangeInfo; + var date = dayInfo.formatedDate; + + // if not in range return [NaN, NaN] + if (clamp && !( + dayInfo.time >= range.start.time + && dayInfo.time < range.end.time + PROXIMATE_ONE_DAY + )) { + return [NaN, NaN]; + } + + var week = dayInfo.day; + var nthWeek = this._getRangeInfo([range.start.time, date]).nthWeek; + + if (this._orient === 'vertical') { + return [ + this._rect.x + week * this._sw + this._sw / 2, + this._rect.y + nthWeek * this._sh + this._sh / 2 + ]; + + } + + return [ + this._rect.x + nthWeek * this._sw + this._sw / 2, + this._rect.y + week * this._sh + this._sh / 2 + ]; + + }, + + /** + * Convert a (x, y) point to time data + * + * @override + * @param {string} point point + * @return {string} data + */ + pointToData: function (point) { + + var date = this.pointToDate(point); + + return date && date.time; + }, + + /** + * Convert a time date item to (x, y) four point. + * + * @param {Array} data date[0] is date + * @param {boolean} [clamp=true] out of range + * @return {Object} point + */ + dataToRect: function (data, clamp) { + var point = this.dataToPoint(data, clamp); + + return { + contentShape: { + x: point[0] - (this._sw - this._lineWidth) / 2, + y: point[1] - (this._sh - this._lineWidth) / 2, + width: this._sw - this._lineWidth, + height: this._sh - this._lineWidth + }, + + center: point, + + tl: [ + point[0] - this._sw / 2, + point[1] - this._sh / 2 + ], + + tr: [ + point[0] + this._sw / 2, + point[1] - this._sh / 2 + ], + + br: [ + point[0] + this._sw / 2, + point[1] + this._sh / 2 + ], + + bl: [ + point[0] - this._sw / 2, + point[1] + this._sh / 2 + ] + + }; + }, + + /** + * Convert a (x, y) point to time date + * + * @param {Array} point point + * @return {Object} date + */ + pointToDate: function (point) { + var nthX = Math.floor((point[0] - this._rect.x) / this._sw) + 1; + var nthY = Math.floor((point[1] - this._rect.y) / this._sh) + 1; + var range = this._rangeInfo.range; + + if (this._orient === 'vertical') { + return this._getDateByWeeksAndDay(nthY, nthX - 1, range); + } + + return this._getDateByWeeksAndDay(nthX, nthY - 1, range); + }, + + /** + * @inheritDoc + */ + convertToPixel: curry(doConvert$2, 'dataToPoint'), + + /** + * @inheritDoc + */ + convertFromPixel: curry(doConvert$2, 'pointToData'), + + /** + * initRange + * + * @private + * @return {Array} [start, end] + */ + _initRangeOption: function () { + var range = this._model.get('range'); + + var rg = range; + + if (isArray(rg) && rg.length === 1) { + rg = rg[0]; + } + + if (/^\d{4}$/.test(rg)) { + range = [rg + '-01-01', rg + '-12-31']; + } + + if (/^\d{4}[\/|-]\d{1,2}$/.test(rg)) { + + var start = this.getDateInfo(rg); + var firstDay = start.date; + firstDay.setMonth(firstDay.getMonth() + 1); + + var end = this.getNextNDay(firstDay, -1); + range = [start.formatedDate, end.formatedDate]; + } + + if (/^\d{4}[\/|-]\d{1,2}[\/|-]\d{1,2}$/.test(rg)) { + range = [rg, rg]; + } + + var tmp = this._getRangeInfo(range); + + if (tmp.start.time > tmp.end.time) { + range.reverse(); + } + + return range; + }, + + /** + * range info + * + * @private + * @param {Array} range range ['2017-01-01', '2017-07-08'] + * If range[0] > range[1], they will not be reversed. + * @return {Object} obj + */ + _getRangeInfo: function (range) { + range = [ + this.getDateInfo(range[0]), + this.getDateInfo(range[1]) + ]; + + var reversed; + if (range[0].time > range[1].time) { + reversed = true; + range.reverse(); + } + + var allDay = Math.floor(range[1].time / PROXIMATE_ONE_DAY) + - Math.floor(range[0].time / PROXIMATE_ONE_DAY) + 1; + + // Consider case: + // Firstly set system timezone as "Time Zone: America/Toronto", + // ``` + // var first = new Date(1478412000000 - 3600 * 1000 * 2.5); + // var second = new Date(1478412000000); + // var allDays = Math.floor(second / ONE_DAY) - Math.floor(first / ONE_DAY) + 1; + // ``` + // will get wrong result because of DST. So we should fix it. + var date = new Date(range[0].time); + var startDateNum = date.getDate(); + var endDateNum = range[1].date.getDate(); + date.setDate(startDateNum + allDay - 1); + // The bias can not over a month, so just compare date. + if (date.getDate() !== endDateNum) { + var sign = date.getTime() - range[1].time > 0 ? 1 : -1; + while (date.getDate() !== endDateNum && (date.getTime() - range[1].time) * sign > 0) { + allDay -= sign; + date.setDate(startDateNum + allDay - 1); + } + } + + var weeks = Math.floor((allDay + range[0].day + 6) / 7); + var nthWeek = reversed ? -weeks + 1: weeks - 1; + + reversed && range.reverse(); + + return { + range: [range[0].formatedDate, range[1].formatedDate], + start: range[0], + end: range[1], + allDay: allDay, + weeks: weeks, + // From 0. + nthWeek: nthWeek, + fweek: range[0].day, + lweek: range[1].day + }; + }, + + /** + * get date by nthWeeks and week day in range + * + * @private + * @param {number} nthWeek the week + * @param {number} day the week day + * @param {Array} range [d1, d2] + * @return {Object} + */ + _getDateByWeeksAndDay: function (nthWeek, day, range) { + var rangeInfo = this._getRangeInfo(range); + + if (nthWeek > rangeInfo.weeks + || (nthWeek === 0 && day < rangeInfo.fweek) + || (nthWeek === rangeInfo.weeks && day > rangeInfo.lweek) + ) { + return false; + } + + var nthDay = (nthWeek - 1) * 7 - rangeInfo.fweek + day; + var date = new Date(rangeInfo.start.time); + date.setDate(rangeInfo.start.d + nthDay); + + return this.getDateInfo(date); + } +}; + +Calendar.dimensions = Calendar.prototype.dimensions; + +Calendar.getDimensionsInfo = Calendar.prototype.getDimensionsInfo; + +Calendar.create = function (ecModel, api) { + var calendarList = []; + + ecModel.eachComponent('calendar', function (calendarModel) { + var calendar = new Calendar(calendarModel, ecModel, api); + calendarList.push(calendar); + calendarModel.coordinateSystem = calendar; + }); + + ecModel.eachSeries(function (calendarSeries) { + if (calendarSeries.get('coordinateSystem') === 'calendar') { + // Inject coordinate system + calendarSeries.coordinateSystem = calendarList[calendarSeries.get('calendarIndex') || 0]; + } + }); + return calendarList; +}; + +function doConvert$2(methodName, ecModel, finder, value) { + var calendarModel = finder.calendarModel; + var seriesModel = finder.seriesModel; + + var coordSys = calendarModel + ? calendarModel.coordinateSystem + : seriesModel + ? seriesModel.coordinateSystem + : null; + + return coordSys === this ? coordSys[methodName](value) : null; +} + +CoordinateSystemManager.register('calendar', Calendar); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var CalendarModel = ComponentModel.extend({ + + type: 'calendar', + + /** + * @type {module:echarts/coord/calendar/Calendar} + */ + coordinateSystem: null, + + defaultOption: { + zlevel: 0, + z: 2, + left: 80, + top: 60, + + cellSize: 20, + + // horizontal vertical + orient: 'horizontal', + + // month separate line style + splitLine: { + show: true, + lineStyle: { + color: '#000', + width: 1, + type: 'solid' + } + }, + + // rect style temporarily unused emphasis + itemStyle: { + color: '#fff', + borderWidth: 1, + borderColor: '#ccc' + }, + + // week text style + dayLabel: { + show: true, + + // a week first day + firstDay: 0, + + // start end + position: 'start', + margin: '50%', // 50% of cellSize + nameMap: 'en', + color: '#000' + }, + + // month text style + monthLabel: { + show: true, + + // start end + position: 'start', + margin: 5, + + // center or left + align: 'center', + + // cn en [] + nameMap: 'en', + formatter: null, + color: '#000' + }, + + // year text style + yearLabel: { + show: true, + + // top bottom left right + position: null, + margin: 30, + formatter: null, + color: '#ccc', + fontFamily: 'sans-serif', + fontWeight: 'bolder', + fontSize: 20 + } + }, + + /** + * @override + */ + init: function (option, parentModel, ecModel, extraOpt) { + var inputPositionParams = getLayoutParams(option); + + CalendarModel.superApply(this, 'init', arguments); + + mergeAndNormalizeLayoutParams$1(option, inputPositionParams); + }, + + /** + * @override + */ + mergeOption: function (option, extraOpt) { + CalendarModel.superApply(this, 'mergeOption', arguments); + + mergeAndNormalizeLayoutParams$1(this.option, option); + } +}); + +function mergeAndNormalizeLayoutParams$1(target, raw) { + // Normalize cellSize + var cellSize = target.cellSize; + + if (!isArray(cellSize)) { + cellSize = target.cellSize = [cellSize, cellSize]; + } + else if (cellSize.length === 1) { + cellSize[1] = cellSize[0]; + } + + var ignoreSize = map([0, 1], function (hvIdx) { + // If user have set `width` or both `left` and `right`, cellSize + // will be automatically set to 'auto', otherwise the default + // setting of cellSize will make `width` setting not work. + if (sizeCalculable(raw, hvIdx)) { + cellSize[hvIdx] = 'auto'; + } + return cellSize[hvIdx] != null && cellSize[hvIdx] !== 'auto'; + }); + + mergeLayoutParam(target, raw, { + type: 'box', ignoreSize: ignoreSize + }); +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var MONTH_TEXT = { + EN: [ + 'Jan', 'Feb', 'Mar', + 'Apr', 'May', 'Jun', + 'Jul', 'Aug', 'Sep', + 'Oct', 'Nov', 'Dec' + ], + CN: [ + '一月', '二月', '三月', + '四月', '五月', '六月', + '七月', '八月', '九月', + '十月', '十一月', '十二月' + ] +}; + +var WEEK_TEXT = { + EN: ['S', 'M', 'T', 'W', 'T', 'F', 'S'], + CN: ['日', '一', '二', '三', '四', '五', '六'] +}; + +extendComponentView({ + + type: 'calendar', + + /** + * top/left line points + * @private + */ + _tlpoints: null, + + /** + * bottom/right line points + * @private + */ + _blpoints: null, + + /** + * first day of month + * @private + */ + _firstDayOfMonth: null, + + /** + * first day point of month + * @private + */ + _firstDayPoints: null, + + render: function (calendarModel, ecModel, api) { + + var group = this.group; + + group.removeAll(); + + var coordSys = calendarModel.coordinateSystem; + + // range info + var rangeData = coordSys.getRangeInfo(); + var orient = coordSys.getOrient(); + + this._renderDayRect(calendarModel, rangeData, group); + + // _renderLines must be called prior to following function + this._renderLines(calendarModel, rangeData, orient, group); + + this._renderYearText(calendarModel, rangeData, orient, group); + + this._renderMonthText(calendarModel, orient, group); + + this._renderWeekText(calendarModel, rangeData, orient, group); + }, + + // render day rect + _renderDayRect: function (calendarModel, rangeData, group) { + var coordSys = calendarModel.coordinateSystem; + var itemRectStyleModel = calendarModel.getModel('itemStyle').getItemStyle(); + var sw = coordSys.getCellWidth(); + var sh = coordSys.getCellHeight(); + + for (var i = rangeData.start.time; + i <= rangeData.end.time; + i = coordSys.getNextNDay(i, 1).time + ) { + + var point = coordSys.dataToRect([i], false).tl; + + // every rect + var rect = new Rect({ + shape: { + x: point[0], + y: point[1], + width: sw, + height: sh + }, + cursor: 'default', + style: itemRectStyleModel + }); + + group.add(rect); + } + + }, + + // render separate line + _renderLines: function (calendarModel, rangeData, orient, group) { + + var self = this; + + var coordSys = calendarModel.coordinateSystem; + + var lineStyleModel = calendarModel.getModel('splitLine.lineStyle').getLineStyle(); + var show = calendarModel.get('splitLine.show'); + + var lineWidth = lineStyleModel.lineWidth; + + this._tlpoints = []; + this._blpoints = []; + this._firstDayOfMonth = []; + this._firstDayPoints = []; + + + var firstDay = rangeData.start; + + for (var i = 0; firstDay.time <= rangeData.end.time; i++) { + addPoints(firstDay.formatedDate); + + if (i === 0) { + firstDay = coordSys.getDateInfo(rangeData.start.y + '-' + rangeData.start.m); + } + + var date = firstDay.date; + date.setMonth(date.getMonth() + 1); + firstDay = coordSys.getDateInfo(date); + } + + addPoints(coordSys.getNextNDay(rangeData.end.time, 1).formatedDate); + + function addPoints(date) { + + self._firstDayOfMonth.push(coordSys.getDateInfo(date)); + self._firstDayPoints.push(coordSys.dataToRect([date], false).tl); + + var points = self._getLinePointsOfOneWeek(calendarModel, date, orient); + + self._tlpoints.push(points[0]); + self._blpoints.push(points[points.length - 1]); + + show && self._drawSplitline(points, lineStyleModel, group); + } + + + // render top/left line + show && this._drawSplitline(self._getEdgesPoints(self._tlpoints, lineWidth, orient), lineStyleModel, group); + + // render bottom/right line + show && this._drawSplitline(self._getEdgesPoints(self._blpoints, lineWidth, orient), lineStyleModel, group); + + }, + + // get points at both ends + _getEdgesPoints: function (points, lineWidth, orient) { + var rs = [points[0].slice(), points[points.length - 1].slice()]; + var idx = orient === 'horizontal' ? 0 : 1; + + // both ends of the line are extend half lineWidth + rs[0][idx] = rs[0][idx] - lineWidth / 2; + rs[1][idx] = rs[1][idx] + lineWidth / 2; + + return rs; + }, + + // render split line + _drawSplitline: function (points, lineStyleModel, group) { + + var poyline = new Polyline({ + z2: 20, + shape: { + points: points + }, + style: lineStyleModel + }); + + group.add(poyline); + }, + + // render month line of one week points + _getLinePointsOfOneWeek: function (calendarModel, date, orient) { + + var coordSys = calendarModel.coordinateSystem; + date = coordSys.getDateInfo(date); + + var points = []; + + for (var i = 0; i < 7; i++) { + + var tmpD = coordSys.getNextNDay(date.time, i); + var point = coordSys.dataToRect([tmpD.time], false); + + points[2 * tmpD.day] = point.tl; + points[2 * tmpD.day + 1] = point[orient === 'horizontal' ? 'bl' : 'tr']; + } + + return points; + + }, + + _formatterLabel: function (formatter, params) { + + if (typeof formatter === 'string' && formatter) { + return formatTplSimple(formatter, params); + } + + if (typeof formatter === 'function') { + return formatter(params); + } + + return params.nameMap; + + }, + + _yearTextPositionControl: function (textEl, point, orient, position, margin) { + + point = point.slice(); + var aligns = ['center', 'bottom']; + + if (position === 'bottom') { + point[1] += margin; + aligns = ['center', 'top']; + } + else if (position === 'left') { + point[0] -= margin; + } + else if (position === 'right') { + point[0] += margin; + aligns = ['center', 'top']; + } + else { // top + point[1] -= margin; + } + + var rotate = 0; + if (position === 'left' || position === 'right') { + rotate = Math.PI / 2; + } + + return { + rotation: rotate, + position: point, + style: { + textAlign: aligns[0], + textVerticalAlign: aligns[1] + } + }; + }, + + // render year + _renderYearText: function (calendarModel, rangeData, orient, group) { + var yearLabel = calendarModel.getModel('yearLabel'); + + if (!yearLabel.get('show')) { + return; + } + + var margin = yearLabel.get('margin'); + var pos = yearLabel.get('position'); + + if (!pos) { + pos = orient !== 'horizontal' ? 'top' : 'left'; + } + + var points = [this._tlpoints[this._tlpoints.length - 1], this._blpoints[0]]; + var xc = (points[0][0] + points[1][0]) / 2; + var yc = (points[0][1] + points[1][1]) / 2; + + var idx = orient === 'horizontal' ? 0 : 1; + + var posPoints = { + top: [xc, points[idx][1]], + bottom: [xc, points[1 - idx][1]], + left: [points[1 - idx][0], yc], + right: [points[idx][0], yc] + }; + + var name = rangeData.start.y; + + if (+rangeData.end.y > +rangeData.start.y) { + name = name + '-' + rangeData.end.y; + } + + var formatter = yearLabel.get('formatter'); + + var params = { + start: rangeData.start.y, + end: rangeData.end.y, + nameMap: name + }; + + var content = this._formatterLabel(formatter, params); + + var yearText = new Text({z2: 30}); + setTextStyle(yearText.style, yearLabel, {text: content}), + yearText.attr(this._yearTextPositionControl(yearText, posPoints[pos], orient, pos, margin)); + + group.add(yearText); + }, + + _monthTextPositionControl: function (point, isCenter, orient, position, margin) { + var align = 'left'; + var vAlign = 'top'; + var x = point[0]; + var y = point[1]; + + if (orient === 'horizontal') { + y = y + margin; + + if (isCenter) { + align = 'center'; + } + + if (position === 'start') { + vAlign = 'bottom'; + } + } + else { + x = x + margin; + + if (isCenter) { + vAlign = 'middle'; + } + + if (position === 'start') { + align = 'right'; + } + } + + return { + x: x, + y: y, + textAlign: align, + textVerticalAlign: vAlign + }; + }, + + // render month and year text + _renderMonthText: function (calendarModel, orient, group) { + var monthLabel = calendarModel.getModel('monthLabel'); + + if (!monthLabel.get('show')) { + return; + } + + var nameMap = monthLabel.get('nameMap'); + var margin = monthLabel.get('margin'); + var pos = monthLabel.get('position'); + var align = monthLabel.get('align'); + + var termPoints = [this._tlpoints, this._blpoints]; + + if (isString(nameMap)) { + nameMap = MONTH_TEXT[nameMap.toUpperCase()] || []; + } + + var idx = pos === 'start' ? 0 : 1; + var axis = orient === 'horizontal' ? 0 : 1; + margin = pos === 'start' ? -margin : margin; + var isCenter = (align === 'center'); + + for (var i = 0; i < termPoints[idx].length - 1; i++) { + + var tmp = termPoints[idx][i].slice(); + var firstDay = this._firstDayOfMonth[i]; + + if (isCenter) { + var firstDayPoints = this._firstDayPoints[i]; + tmp[axis] = (firstDayPoints[axis] + termPoints[0][i + 1][axis]) / 2; + } + + var formatter = monthLabel.get('formatter'); + var name = nameMap[+firstDay.m - 1]; + var params = { + yyyy: firstDay.y, + yy: (firstDay.y + '').slice(2), + MM: firstDay.m, + M: +firstDay.m, + nameMap: name + }; + + var content = this._formatterLabel(formatter, params); + + var monthText = new Text({z2: 30}); + extend( + setTextStyle(monthText.style, monthLabel, {text: content}), + this._monthTextPositionControl(tmp, isCenter, orient, pos, margin) + ); + + group.add(monthText); + } + }, + + _weekTextPositionControl: function (point, orient, position, margin, cellSize) { + var align = 'center'; + var vAlign = 'middle'; + var x = point[0]; + var y = point[1]; + var isStart = position === 'start'; + + if (orient === 'horizontal') { + x = x + margin + (isStart ? 1 : -1) * cellSize[0] / 2; + align = isStart ? 'right' : 'left'; + } + else { + y = y + margin + (isStart ? 1 : -1) * cellSize[1] / 2; + vAlign = isStart ? 'bottom' : 'top'; + } + + return { + x: x, + y: y, + textAlign: align, + textVerticalAlign: vAlign + }; + }, + + // render weeks + _renderWeekText: function (calendarModel, rangeData, orient, group) { + var dayLabel = calendarModel.getModel('dayLabel'); + + if (!dayLabel.get('show')) { + return; + } + + var coordSys = calendarModel.coordinateSystem; + var pos = dayLabel.get('position'); + var nameMap = dayLabel.get('nameMap'); + var margin = dayLabel.get('margin'); + var firstDayOfWeek = coordSys.getFirstDayOfWeek(); + + if (isString(nameMap)) { + nameMap = WEEK_TEXT[nameMap.toUpperCase()] || []; + } + + var start = coordSys.getNextNDay( + rangeData.end.time, (7 - rangeData.lweek) + ).time; + + var cellSize = [coordSys.getCellWidth(), coordSys.getCellHeight()]; + margin = parsePercent$1(margin, cellSize[orient === 'horizontal' ? 0 : 1]); + + if (pos === 'start') { + start = coordSys.getNextNDay( + rangeData.start.time, -(7 + rangeData.fweek) + ).time; + margin = -margin; + } + + for (var i = 0; i < 7; i++) { + + var tmpD = coordSys.getNextNDay(start, i); + var point = coordSys.dataToRect([tmpD.time], false).center; + var day = i; + day = Math.abs((i + firstDayOfWeek) % 7); + var weekText = new Text({z2: 30}); + + extend( + setTextStyle(weekText.style, dayLabel, {text: nameMap[day]}), + this._weekTextPositionControl(point, orient, pos, margin, cellSize) + ); + group.add(weekText); + } + } +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * @file calendar.js + * @author dxh + */ + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +// Model +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 +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 Text({ + style: setTextStyle({}, textStyleModel, { + text: titleModel.get('text'), + textFill: textStyleModel.getTextColor() + }, {disableBox: true}), + z2: 10 + }); + + var textRect = textEl.getBoundingRect(); + + var subText = titleModel.get('subtext'); + var subTextEl = new Text({ + style: setTextStyle({}, subtextStyleModel, { + text: subText, + textFill: subtextStyleModel.getTextColor(), + y: textRect.height + titleModel.get('itemGap'), + textVerticalAlign: 'top' + }, {disableBox: true}), + 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 = 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 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], + r: titleModel.get('borderRadius') + }, + style: style, + silent: true + }); + subPixelOptimizeRect(rect); + + group.add(rect); + } +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +ComponentModel.registerSubTypeDefaulter('dataZoom', function () { + // Default 'slider' when no type specified. + return 'slider'; +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var AXIS_DIMS = ['x', 'y', 'z', 'radius', 'angle', 'single']; +// Supported coords. +var COORDS = ['cartesian2d', 'polar', 'singleAxis']; + +/** + * @param {string} coordType + * @return {boolean} + */ +function isCoordSupported(coordType) { + return indexOf(COORDS, coordType) >= 0; +} + +/** + * Create "each" method to iterate names. + * + * @pubilc + * @param {Array.} names + * @param {Array.=} attrs + * @return {Function} + */ +function createNameEach(names, attrs) { + names = names.slice(); + var capitalNames = map(names, capitalFirst); + attrs = (attrs || []).slice(); + var capitalAttrs = map(attrs, capitalFirst); + + return function (callback, context) { + each$1(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 + */ +var eachAxisDim$1 = 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: {}} + */ +function createLinkedNodesFinder(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 indexOf(result.nodes, node) >= 0; + } + + function isLinked(node, result) { + var hasLink = false; + forEachEdgeType(function (edgeType) { + each$1(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) { + each$1(edgeIdGetter(node, edgeType) || [], function (edgeId) { + result.records[edgeType.name][edgeId] = true; + }); + }); + } +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var each$22 = each$1; +var asc$1 = 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; + + /** + * {minSpan, maxSpan, minValueSpan, maxValueSpan} + * @private + * @type {Object} + */ + this._minMaxSpan; + + /** + * @readOnly + * @type {module: echarts/model/Global} + */ + this.ecModel = ecModel; + + /** + * @private + * @type {module: echarts/component/dataZoom/DataZoomModel} + */ + this._dataZoomModel = dataZoomModel; + + // /** + // * @readOnly + // * @private + // */ + // this.hasSeriesStacked; +}; + +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.} Value can only be NaN or finite value. + */ + 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) { + if (isCoordSupported(seriesModel.get('coordinateSystem'))) { + 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; + }, + + getMinMaxSpan: function () { + return clone(this._minMaxSpan); + }, + + /** + * Only calculate by given range and this._dataExtent, do not change anything. + * + * @param {Object} opt + * @param {number} [opt.start] + * @param {number} [opt.end] + * @param {number} [opt.startValue] + * @param {number} [opt.endValue] + */ + calculateDataWindow: function (opt) { + var dataExtent = this._dataExtent; + var axisModel = this.getAxisModel(); + var scale = axisModel.axis.scale; + var rangePropMode = this._dataZoomModel.getRangePropMode(); + var percentExtent = [0, 100]; + var percentWindow = [ + opt.start, + opt.end + ]; + var valueWindow = []; + + each$22(['startValue', 'endValue'], function (prop) { + valueWindow.push(opt[prop] != null ? scale.parse(opt[prop]) : null); + }); + + // Normalize bound. + each$22([0, 1], function (idx) { + var boundValue = valueWindow[idx]; + var boundPercent = percentWindow[idx]; + + // Notice: dataZoom is based either on `percentProp` ('start', 'end') or + // on `valueProp` ('startValue', 'endValue'). The former one is suitable + // for cases that a dataZoom component controls multiple axes with different + // unit or extent, and the latter one is suitable for accurate zoom by pixel + // (e.g., in dataZoomSelect). `valueProp` can be calculated from `percentProp`, + // but it is awkward that `percentProp` can not be obtained from `valueProp` + // accurately (because all of values that are overflow the `dataExtent` will + // be calculated to percent '100%'). So we have to use + // `dataZoom.getRangePropMode()` to mark which prop is used. + // `rangePropMode` is updated only when setOption or dispatchAction, otherwise + // it remains its original value. + + if (rangePropMode[idx] === 'percent') { + if (boundPercent == null) { + boundPercent = percentExtent[idx]; + } + // Use scale.parse to math round for category or time axis. + boundValue = scale.parse(linearMap( + boundPercent, percentExtent, dataExtent, true + )); + } + else { + // Calculating `percent` from `value` may be not accurate, because + // This calculation can not be inversed, because all of values that + // are overflow the `dataExtent` will be calculated to percent '100%' + boundPercent = linearMap( + boundValue, dataExtent, percentExtent, true + ); + } + + // valueWindow[idx] = round(boundValue); + // percentWindow[idx] = round(boundPercent); + valueWindow[idx] = boundValue; + percentWindow[idx] = boundPercent; + }); + + return { + valueWindow: asc$1(valueWindow), + percentWindow: asc$1(percentWindow) + }; + }, + + /** + * 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; + } + + var targetSeries = this.getTargetSeriesModels(); + // Culculate data window and data extent, and record them. + this._dataExtent = calculateDataExtent(this, this._dimName, targetSeries); + + // this.hasSeriesStacked = false; + // each(targetSeries, function (series) { + // var data = series.getData(); + // var dataDim = data.mapDimension(this._dimName); + // var stackedDimension = data.getCalculationInfo('stackedDimension'); + // if (stackedDimension && stackedDimension === dataDim) { + // this.hasSeriesStacked = true; + // } + // }, this); + + var dataWindow = this.calculateDataWindow(dataZoomModel.option); + + this._valueWindow = dataWindow.valueWindow; + this._percentWindow = dataWindow.percentWindow; + + setMinMaxSpan(this); + + // 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, api) { + if (dataZoomModel !== this._dataZoomModel) { + return; + } + + var axisDim = this._dimName; + var seriesModels = this.getTargetSeriesModels(); + var filterMode = dataZoomModel.get('filterMode'); + var valueWindow = this._valueWindow; + + if (filterMode === 'none') { + return; + } + + // 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'. + // But currently, stack has been fixed to based on value but not index, + // so this is not an issue any more. + // var otherAxisModel = this.getOtherAxisModel(); + // if (dataZoomModel.get('$fromToolbox') + // && otherAxisModel + // && otherAxisModel.hasSeriesStacked + // ) { + // filterMode = 'empty'; + // } + + // TODO + // filterMode 'weakFilter' and 'empty' is not optimized for huge data yet. + + // Process series data + each$22(seriesModels, function (seriesModel) { + var seriesData = seriesModel.getData(); + var dataDims = seriesData.mapDimension(axisDim, true); + + if (filterMode === 'weakFilter') { + seriesData.filterSelf(function (dataIndex) { + var leftOut; + var rightOut; + var hasValue; + for (var i = 0; i < dataDims.length; i++) { + var value = seriesData.get(dataDims[i], dataIndex); + var thisHasValue = !isNaN(value); + var thisLeftOut = value < valueWindow[0]; + var thisRightOut = value > valueWindow[1]; + if (thisHasValue && !thisLeftOut && !thisRightOut) { + return true; + } + thisHasValue && (hasValue = true); + thisLeftOut && (leftOut = true); + thisRightOut && (rightOut = true); + } + // If both left out and right out, do not filter. + return hasValue && leftOut && rightOut; + }); + } + else { + each$22(dataDims, function (dim) { + if (filterMode === 'empty') { + seriesModel.setData( + seriesData.map(dim, function (value) { + return !isInWindow(value) ? NaN : value; + }) + ); + } + else { + var range = {}; + range[dim] = valueWindow; + + // console.time('select'); + seriesData.selectRange(range); + // console.timeEnd('select'); + } + }); + } + + each$22(dataDims, function (dim) { + seriesData.setApproximateExtent(valueWindow, dim); + }); + }); + + function isInWindow(value) { + return value >= valueWindow[0] && value <= valueWindow[1]; + } + } +}; + +function calculateDataExtent(axisProxy, axisDim, seriesModels) { + var dataExtent = [Infinity, -Infinity]; + + each$22(seriesModels, function (seriesModel) { + var seriesData = seriesModel.getData(); + if (seriesData) { + each$22(seriesData.mapDimension(axisDim, true), function (dim) { + var seriesExtent = seriesData.getApproximateExtent(dim); + seriesExtent[0] < dataExtent[0] && (dataExtent[0] = seriesExtent[0]); + seriesExtent[1] > dataExtent[1] && (dataExtent[1] = seriesExtent[1]); + }); + } + }); + + if (dataExtent[1] < dataExtent[0]) { + dataExtent = [NaN, NaN]; + } + + // It is important to get "consistent" extent when more then one axes is + // controlled by a `dataZoom`, otherwise those axes will not be synchronized + // when zooming. But it is difficult to know what is "consistent", considering + // axes have different type or even different meanings (For example, two + // time axes are used to compare data of the same date in different years). + // So basically dataZoom just obtains extent by series.data (in category axis + // extent can be obtained from axis.data). + // Nevertheless, user can set min/max/scale on axes to make extent of axes + // consistent. + fixExtentByAxis(axisProxy, dataExtent); + + return dataExtent; +} + +function fixExtentByAxis(axisProxy, dataExtent) { + var axisModel = axisProxy.getAxisModel(); + var min = axisModel.getMin(true); + + // For category axis, if min/max/scale are not set, extent is determined + // by axis.data by default. + var isCategoryAxis = axisModel.get('type') === 'category'; + var axisDataLen = isCategoryAxis && axisModel.getCategories().length; + + if (min != null && min !== 'dataMin' && typeof min !== 'function') { + dataExtent[0] = min; + } + else if (isCategoryAxis) { + dataExtent[0] = axisDataLen > 0 ? 0 : NaN; + } + + var max = axisModel.getMax(true); + if (max != null && max !== 'dataMax' && typeof max !== 'function') { + dataExtent[1] = max; + } + else if (isCategoryAxis) { + dataExtent[1] = axisDataLen > 0 ? axisDataLen - 1 : NaN; + } + + if (!axisModel.get('scale', true)) { + dataExtent[0] > 0 && (dataExtent[0] = 0); + dataExtent[1] < 0 && (dataExtent[1] = 0); + } + + // For value axis, if min/max/scale are not set, we just use the extent obtained + // by series data, which may be a little different from the extent calculated by + // `axisHelper.getScaleExtent`. But the different just affects the experience a + // little when zooming. So it will not be fixed until some users require it strongly. + + return dataExtent; +} + +function setAxisModel(axisProxy, isRestore) { + var axisModel = axisProxy.getAxisModel(); + + var percentWindow = axisProxy._percentWindow; + var valueWindow = axisProxy._valueWindow; + + if (!percentWindow) { + return; + } + + // [0, 500]: arbitrary value, guess axis extent. + var precision = getPixelPrecision(valueWindow, [0, 500]); + precision = Math.min(precision, 20); + // isRestore or isFull + var useOrigin = isRestore || (percentWindow[0] === 0 && percentWindow[1] === 100); + + axisModel.setRange( + useOrigin ? null : +valueWindow[0].toFixed(precision), + useOrigin ? null : +valueWindow[1].toFixed(precision) + ); +} + +function setMinMaxSpan(axisProxy) { + var minMaxSpan = axisProxy._minMaxSpan = {}; + var dataZoomModel = axisProxy._dataZoomModel; + + each$22(['min', 'max'], function (minMax) { + minMaxSpan[minMax + 'Span'] = dataZoomModel.get(minMax + 'Span'); + + // minValueSpan and maxValueSpan has higher priority than minSpan and maxSpan + var valueSpan = dataZoomModel.get(minMax + 'ValueSpan'); + + if (valueSpan != null) { + minMaxSpan[minMax + 'ValueSpan'] = valueSpan; + valueSpan = axisProxy.getAxisModel().axis.scale.parse(valueSpan); + + if (valueSpan != null) { + var dataExtent = axisProxy._dataExtent; + minMaxSpan[minMax + 'Span'] = linearMap( + dataExtent[0] + valueSpan, dataExtent, [0, 100], true + ); + } + } + }); +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var each$21 = each$1; +var eachAxisDim = eachAxisDim$1; + +var DataZoomModel = extendComponentModel({ + + type: 'dataZoom', + + dependencies: [ + 'xAxis', 'yAxis', 'zAxis', 'radiusAxis', 'angleAxis', 'singleAxis', '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' or 'weakFilter'. + // 'filter': data items which are out of window will be removed. This option is + // applicable when filtering outliers. For each data item, it will be + // filtered if one of the relevant dimensions is out of the window. + // 'weakFilter': data items which are out of window will be removed. This option + // is applicable when filtering outliers. For each data item, it will be + // filtered only if all of the relevant dimensions are out of the same + // side of the window. + // '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. + // 'none': Do not filter. + // 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. + minSpan: null, // 0 ~ 100 + maxSpan: null, // 0 ~ 100 + minValueSpan: null, // The range of dataZoom can not be smaller than that. + maxValueSpan: null, // The range of dataZoom can not be larger than that. + rangeMode: null // Array, can be 'value' or 'percent'. + }, + + /** + * @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; + + /** + * 'percent' or 'value' + * @private + */ + this._rangePropMode = ['percent', 'percent']; + + var rawOption = retrieveRaw(option); + + this.mergeDefaultAndTheme(option, ecModel); + + this.doInit(rawOption); + }, + + /** + * @override + */ + mergeOption: function (newOption) { + var rawOption = retrieveRaw(newOption); + + //FIX #2591 + 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$1.canvasSupported) { + thisOption.realtime = false; + } + + this._setDefaultThrottle(rawOption); + + updateRangeUse(this, rawOption); + + each$21([['start', 'startValue'], ['end', 'endValue']], function (names, index) { + // start/end has higher priority over startValue/endValue if they + // both set, but we should make chart.setOption({endValue: 1000}) + // effective, rather than chart.setOption({endValue: 1000, end: null}). + if (this._rangePropMode[index] === 'value') { + thisOption[names[0]] = null; + } + // Otherwise do nothing and use the merge result. + }, this); + + 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] = 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; + var dependentModels = this.dependentModels; + + if (autoAxisIndex) { + // Find axis that parallel to dataZoom as default. + var dimName = orient === 'vertical' ? 'y' : 'x'; + + if (dependentModels[dimName + 'Axis'].length) { + thisOption[dimName + 'AxisIndex'] = [0]; + autoAxisIndex = false; + } + else { + each$21(dependentModels.singleAxis, function (singleAxisModel) { + if (autoAxisIndex && singleAxisModel.get('orient', true) === orient) { + thisOption.singleAxisIndex = [singleAxisModel.componentIndex]; + 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 (__DEV__) { + if (!axisModel) { + throw new Error( + dimNames.axis + ' "' + retrieve( + axisIndex, + axisId, + 0 + ) + '" not found' + ); + } + } + axisIndex = axisModel.componentIndex; + + if (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$21( + this.get(dimNames.axisIndex), + function (axisIndex) { + callback.call(context, dimNames, axisIndex, this, ecModel); + }, + this + ); + }, this); + }, + + /** + * @param {string} dimName + * @param {number} axisIndex + * @return {module:echarts/component/dataZoom/AxisProxy} If not found, return null/undefined. + */ + getAxisProxy: function (dimName, axisIndex) { + return this._axisProxies[dimName + '_' + axisIndex]; + }, + + /** + * @param {string} dimName + * @param {number} axisIndex + * @return {module:echarts/model/Model} If not found, return null/undefined. + */ + getAxisModel: function (dimName, axisIndex) { + var axisProxy = this.getAxisProxy(dimName, axisIndex); + return axisProxy && axisProxy.getAxisModel(); + }, + + /** + * 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] + * @param {boolean} [ignoreUpdateRangeUsg=false] + */ + setRawRange: function (opt, ignoreUpdateRangeUsg) { + var option = this.option; + each$21([['start', 'startValue'], ['end', 'endValue']], function (names) { + // If only one of 'start' and 'startValue' is not null/undefined, the other + // should be cleared, which enable clear the option. + // If both of them are not set, keep option with the original value, which + // enable use only set start but not set end when calling `dispatchAction`. + // The same as 'end' and 'endValue'. + if (opt[names[0]] != null || opt[names[1]] != null) { + option[names[0]] = opt[names[0]]; + option[names[1]] = opt[names[1]]; + } + }, this); + + !ignoreUpdateRangeUsg && updateRangeUse(this, opt); + }, + + /** + * @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] value can only be '-' or finite number. + */ + 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 + * @param {module:echarts/model/Model} [axisModel] If axisModel given, find axisProxy + * corresponding to the axisModel + * @return {module:echarts/component/dataZoom/AxisProxy} + */ + findRepresentativeAxisProxy: function (axisModel) { + if (axisModel) { + return axisModel.__dzAxisProxy; + } + + // 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]; + } + } + }, + + /** + * @return {Array.} + */ + getRangePropMode: function () { + return this._rangePropMode.slice(); + } + +}); + +function retrieveRaw(option) { + var ret = {}; + each$21( + ['start', 'end', 'startValue', 'endValue', 'throttle'], + function (name) { + option.hasOwnProperty(name) && (ret[name] = option[name]); + } + ); + return ret; +} + +function updateRangeUse(dataZoomModel, rawOption) { + var rangePropMode = dataZoomModel._rangePropMode; + var rangeModeInOption = dataZoomModel.get('rangeMode'); + + each$21([['start', 'startValue'], ['end', 'endValue']], function (names, index) { + var percentSpecified = rawOption[names[0]] != null; + var valueSpecified = rawOption[names[1]] != null; + if (percentSpecified && !valueSpecified) { + rangePropMode[index] = 'percent'; + } + else if (!percentSpecified && valueSpecified) { + rangePropMode[index] = 'value'; + } + else if (rangeModeInOption) { + rangePropMode[index] = rangeModeInOption[index]; + } + else if (percentSpecified) { // percentSpecified && valueSpecified + rangePropMode[index] = 'percent'; + } + // else remain its original setting. + }); +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var DataZoomView = Component.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} { + * grid: [ + * {model: coord0, axisModels: [axis1, axis3], coordIndex: 1}, + * {model: coord1, axisModels: [axis0, axis2], coordIndex: 0}, + * ... + * ], // cartesians must not be null/undefined. + * polar: [ + * {model: coord0, axisModels: [axis4], coordIndex: 0}, + * ... + * ], // polars must not be null/undefined. + * singleAxis: [ + * {model: coord0, axisModels: [], coordIndex: 0} + * ] + */ + getTargetCoordInfo: function () { + var dataZoomModel = this.dataZoomModel; + var ecModel = this.ecModel; + var coordSysLists = {}; + + dataZoomModel.eachTargetAxis(function (dimNames, axisIndex) { + var axisModel = ecModel.getComponent(dimNames.axis, axisIndex); + if (axisModel) { + var coordModel = axisModel.getCoordSysModel(); + coordModel && save( + coordModel, + axisModel, + coordSysLists[coordModel.mainType] || (coordSysLists[coordModel.mainType] = []), + 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 coordSysLists; + } + +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +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' + } + } + +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var Rect$2 = Rect; +var linearMap$1 = linearMap; +var asc$2 = asc; +var bind$4 = bind; +var each$23 = each$1; + +// 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); + + 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); + clear(this, '_dispatchZoomAction'); + }, + + /** + * @override + */ + dispose: function () { + SliderZoomView.superApply(this, 'dispose', arguments); + clear(this, '_dispatchZoomAction'); + }, + + _buildView: function () { + var thisGroup = this.group; + + thisGroup.removeAll(); + + this._resetLocation(); + this._resetInterval(); + + var barGroup = this._displayables.barGroup = new 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 = getLayoutParams(dataZoomModel.option); + + // Replace the placeholder value. + each$1(['right', 'top', 'width', 'height'], function (name) { + if (layoutParams[name] === 'ph') { + layoutParams[name] = positionInfo[name]; + } + }); + + var layoutRect = 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; + var barGroup = this._displayables.barGroup; + + barGroup.add(new Rect$2({ + silent: true, + shape: { + x: 0, y: 0, width: size[0], height: size[1] + }, + style: { + fill: dataZoomModel.get('backgroundColor') + }, + z2: -40 + })); + + // Click panel, over shadow, below handles. + barGroup.add(new Rect$2({ + shape: { + x: 0, y: 0, width: size[0], height: size[1] + }, + style: { + fill: 'transparent' + }, + z2: 0, + onclick: bind(this._onClickPanelClick, this) + })); + }, + + _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; + + if (otherDim == null) { + return; + } + + 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]); + var lastIsEmpty; + data.each([otherDim], function (value, index) { + if (stride > 0 && (index % stride)) { + thisCoord += step; + return; + } + + // FIXME + // Should consider axis.min/axis.max when drawing dataShadow. + + // FIXME + // 应该使用统一的空判断?还是在list里进行空判断? + var isEmpty = value == null || isNaN(value) || value === ''; + // See #4235. + var otherCoord = isEmpty + ? 0 : linearMap$1(value, otherDataExtent, otherShadowExtent, true); + + // Attempt to draw data shadow precisely when there are empty value. + if (isEmpty && !lastIsEmpty && index) { + areaPoints.push([areaPoints[areaPoints.length - 1][0], 0]); + linePoints.push([linePoints[linePoints.length - 1][0], 0]); + } + else if (!isEmpty && lastIsEmpty) { + areaPoints.push([thisCoord, 0]); + linePoints.push([thisCoord, 0]); + } + + areaPoints.push([thisCoord, otherCoord]); + linePoints.push([thisCoord, otherCoord]); + + thisCoord += step; + lastIsEmpty = isEmpty; + }); + + var dataZoomModel = this.dataZoomModel; + // var dataBackgroundModel = dataZoomModel.getModel('dataBackground'); + this._displayables.barGroup.add(new Polygon({ + shape: {points: areaPoints}, + style: defaults( + {fill: dataZoomModel.get('dataBackgroundColor')}, + dataZoomModel.getModel('dataBackground.areaStyle').getAreaStyle() + ), + silent: true, + z2: -20 + })); + this._displayables.barGroup.add(new 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(); + + each$1(seriesModels, function (seriesModel) { + if (result) { + return; + } + + if (showDataShadow !== true && indexOf( + SHOW_DATA_SHADOW_SERIES_TYPE, seriesModel.get('type') + ) < 0 + ) { + return; + } + + var thisAxis = ecModel.getComponent(dimNames.axis, axisIndex).axis; + var otherDim = getOtherDim(dimNames.name); + var otherAxisInverse; + var coordSys = seriesModel.coordinateSystem; + + if (otherDim != null && coordSys.getOtherAxis) { + otherAxisInverse = coordSys.getOtherAxis(thisAxis).inverse; + } + + otherDim = seriesModel.getData().mapDimension(otherDim); + + result = { + thisAxis: thisAxis, + series: seriesModel, + thisDim: dimNames.name, + otherDim: otherDim, + otherAxisInverse: otherAxisInverse + }; + + }, 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$2({ + draggable: true, + cursor: getCursor(this._orient), + drift: bind$4(this._onDragMove, this, 'all'), + onmousemove: function (e) { + // Fot mobile devicem, prevent screen slider on the button. + stop(e.event); + }, + ondragstart: bind$4(this._showDataInfo, this, true), + ondragend: bind$4(this._onDragEnd, this), + onmouseover: bind$4(this._showDataInfo, this, true), + onmouseout: bind$4(this._showDataInfo, this, false), + style: { + fill: dataZoomModel.get('fillerColor'), + textPosition : 'inside' + } + })); + + // Frame border. + barGroup.add(new Rect$2(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)' + } + }))); + + each$23([0, 1], function (handleIndex) { + var path = createIcon( + dataZoomModel.get('handleIcon'), + { + cursor: getCursor(this._orient), + draggable: true, + drift: bind$4(this._onDragMove, this, handleIndex), + onmousemove: function (e) { + // Fot mobile devicem, prevent screen slider on the button. + stop(e.event); + }, + ondragend: bind$4(this._onDragEnd, this), + onmouseover: bind$4(this._showDataInfo, this, true), + onmouseout: bind$4(this._showDataInfo, this, false) + }, + {x: -1, y: 0, width: 2, height: 2} + ); + + var bRect = path.getBoundingRect(); + this._handleHeight = parsePercent$1(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 Text({ + silent: true, + invisible: true, + style: { + x: 0, y: 0, text: '', + textVerticalAlign: 'middle', + textAlign: 'center', + textFill: 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$1(range[0], [0, 100], viewExtent, true), + linearMap$1(range[1], [0, 100], viewExtent, true) + ]; + }, + + /** + * @private + * @param {(number|string)} handleIndex 0 or 1 or 'all' + * @param {number} delta + * @return {boolean} changed + */ + _updateInterval: function (handleIndex, delta) { + var dataZoomModel = this.dataZoomModel; + var handleEnds = this._handleEnds; + var viewExtend = this._getViewExtent(); + var minMaxSpan = dataZoomModel.findRepresentativeAxisProxy().getMinMaxSpan(); + var percentExtent = [0, 100]; + + sliderMove( + delta, + handleEnds, + viewExtend, + dataZoomModel.get('zoomLock') ? 'all' : handleIndex, + minMaxSpan.minSpan != null + ? linearMap$1(minMaxSpan.minSpan, percentExtent, viewExtend, true) : null, + minMaxSpan.maxSpan != null + ? linearMap$1(minMaxSpan.maxSpan, percentExtent, viewExtend, true) : null + ); + + var lastRange = this._range; + var range = this._range = asc$2([ + linearMap$1(handleEnds[0], viewExtend, percentExtent, true), + linearMap$1(handleEnds[1], viewExtend, percentExtent, true) + ]); + + return !lastRange || lastRange[0] !== range[0] || lastRange[1] !== range[1]; + }, + + /** + * @private + */ + _updateView: function (nonRealtime) { + var displaybles = this._displayables; + var handleEnds = this._handleEnds; + var handleInterval = asc$2(handleEnds.slice()); + var size = this._size; + + each$23([0, 1], function (handleIndex) { + // Handles + var handle = displaybles.handles[handleIndex]; + var handleHeight = this._handleHeight; + handle.attr({ + scale: [handleHeight / 2, handleHeight / 2], + 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(nonRealtime); + }, + + /** + * @private + */ + _updateDataInfo: function (nonRealtime) { + 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 axisProxy = dataZoomModel.findRepresentativeAxisProxy(); + + if (axisProxy) { + var axis = axisProxy.getAxisModel().axis; + var range = this._range; + + var dataInterval = nonRealtime + // See #4434, data and axis are not processed and reset yet in non-realtime mode. + ? axisProxy.calculateDataWindow({ + start: range[0], end: range[1] + }).valueWindow + : axisProxy.getDataValueWindow(); + + labelTexts = [ + this._formatLabel(dataInterval[0], axis), + this._formatLabel(dataInterval[1], axis) + ]; + } + } + + var orderedHandleEnds = asc$2(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 = getTransform( + displaybles.handles[handleIndex].parent, this.group + ); + var direction = transformDirection( + handleIndex === 0 ? 'right' : 'left', barTransform + ); + var offset = this._handleWidth / 2 + LABEL_GAP; + var textPoint = applyTransform$1( + [ + 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'); + + var labelPrecision = dataZoomModel.get('labelPrecision'); + if (labelPrecision == null || labelPrecision === 'auto') { + labelPrecision = axis.getPixelPrecision(); + } + + var valueStr = (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)); + + return isFunction$1(labelFormatter) + ? labelFormatter(value, valueStr) + : isString(labelFormatter) + ? labelFormatter.replace('{value}', valueStr) + : valueStr; + }, + + /** + * @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 barTransform = this._displayables.barGroup.getLocalTransform(); + var vertex = applyTransform$1([dx, dy], barTransform, true); + + var changed = this._updateInterval(handleIndex, vertex[0]); + + var realtime = this.dataZoomModel.get('realtime'); + + this._updateView(!realtime); + + // Avoid dispatch dataZoom repeatly but range not changed, + // which cause bad visual effect when progressive enabled. + changed && realtime && this._dispatchZoomAction(); + }, + + _onDragEnd: function () { + this._dragging = false; + this._showDataInfo(false); + + // While in realtime mode and stream mode, dispatch action when + // drag end will cause the whole view rerender, which is unnecessary. + var realtime = this.dataZoomModel.get('realtime'); + !realtime && this._dispatchZoomAction(); + }, + + _onClickPanelClick: function (e) { + var size = this._size; + var localPoint = this._displayables.barGroup.transformCoordToLocal(e.offsetX, e.offsetY); + + if (localPoint[0] < 0 || localPoint[0] > size[0] + || localPoint[1] < 0 || localPoint[1] > size[1] + ) { + return; + } + + var handleEnds = this._handleEnds; + var center = (handleEnds[0] + handleEnds[1]) / 2; + + var changed = this._updateInterval('all', localPoint[0] - center); + this._updateView(); + changed && 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 + */ + _findCoordRect: function () { + // Find the grid coresponding to the first axis referred by dataZoom. + var rect; + each$23(this.getTargetCoordInfo(), function (coordInfoList) { + if (!rect && coordInfoList.length) { + var coordSys = coordInfoList[0].model.coordinateSystem; + rect = coordSys.getRect && coordSys.getRect(); + } + }); + if (!rect) { + 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里一致,但是写在这里是否不好 + var map$$1 = {x: 'y', y: 'x', radius: 'angle', angle: 'radius'}; + return map$$1[thisDim]; +} + +function getCursor(orient) { + return orient === 'vertical' ? 'ns-resize' : 'ew-resize'; +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +DataZoomModel.extend({ + + type: 'dataZoom.inside', + + /** + * @protected + */ + defaultOption: { + disabled: false, // Whether disable this inside zoom. + zoomLock: false, // Whether disable zoom but only pan. + zoomOnMouseWheel: true, // Can be: true / false / 'shift' / 'ctrl' / 'alt'. + moveOnMouseMove: true, // Can be: true / false / 'shift' / 'ctrl' / 'alt'. + preventDefaultMouseMove: true + } +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +// 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 curry$6 = curry; + +var ATTR$1 = '\0_ec_dataZoom_roams'; + + +/** + * @public + * @param {module:echarts/ExtensionAPI} api + * @param {Object} dataZoomInfo + * @param {string} dataZoomInfo.coordId + * @param {Function} dataZoomInfo.containsPoint + * @param {Array.} dataZoomInfo.allCoordIds + * @param {string} dataZoomInfo.dataZoomId + * @param {number} dataZoomInfo.throttleRate + * @param {Function} dataZoomInfo.panGetRange + * @param {Function} dataZoomInfo.zoomGetRange + * @param {boolean} [dataZoomInfo.zoomLock] + * @param {boolean} [dataZoomInfo.disabled] + */ +function register$2(api, dataZoomInfo) { + var store = giveStore(api); + var theDataZoomId = dataZoomInfo.dataZoomId; + var theCoordId = dataZoomInfo.coordId; + + // Do clean when a dataZoom changes its target coordnate system. + // Avoid memory leak, dispose all not-used-registered. + each$1(store, function (record, coordId) { + var dataZoomInfos = record.dataZoomInfos; + if (dataZoomInfos[theDataZoomId] + && 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, record); + record.dispatchAction = curry(dispatchAction$1, api); + } + + // Update reference of dataZoom. + !(record.dataZoomInfos[theDataZoomId]) && record.count++; + record.dataZoomInfos[theDataZoomId] = dataZoomInfo; + + var controllerParams = mergeControllerParams(record.dataZoomInfos); + record.controller.enable(controllerParams.controlType, controllerParams.opt); + + // Consider resize, area should be always updated. + record.controller.setPointerChecker(dataZoomInfo.containsPoint); + + // Update throttle. + createOrUpdate( + record, + 'dispatchAction', + dataZoomInfo.throttleRate, + 'fixRate' + ); +} + +/** + * @public + * @param {module:echarts/ExtensionAPI} api + * @param {string} dataZoomId + */ +function unregister$1(api, dataZoomId) { + var store = giveStore(api); + + each$1(store, function (record) { + record.controller.dispose(); + var dataZoomInfos = record.dataZoomInfos; + if (dataZoomInfos[dataZoomId]) { + delete dataZoomInfos[dataZoomId]; + record.count--; + } + }); + + cleanStore(store); +} + +/** + * @public + */ +function generateCoordId(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$1] || (zr[ATTR$1] = {}); +} + +function createController(api, newRecord) { + var controller = new RoamController(api.getZr()); + controller.on('pan', curry$6(onPan, newRecord)); + controller.on('zoom', curry$6(onZoom, newRecord)); + + return controller; +} + +function cleanStore(store) { + each$1(store, function (record, coordId) { + if (!record.count) { + record.controller.dispose(); + delete store[coordId]; + } + }); +} + +function onPan(record, dx, dy, oldX, oldY, newX, newY) { + wrapAndDispatch(record, function (info) { + return info.panGetRange(record.controller, dx, dy, oldX, oldY, newX, newY); + }); +} + +function onZoom(record, scale, mouseX, mouseY) { + wrapAndDispatch(record, function (info) { + return info.zoomGetRange(record.controller, scale, mouseX, mouseY); + }); +} + +function wrapAndDispatch(record, getRange) { + var batch = []; + + each$1(record.dataZoomInfos, function (info) { + var range = getRange(info); + !info.disabled && range && batch.push({ + dataZoomId: info.dataZoomId, + start: range[0], + end: range[1] + }); + }); + + batch.length && record.dispatchAction(batch); +} + +/** + * This action will be throttled. + */ +function dispatchAction$1(api, batch) { + api.dispatchAction({ + type: 'dataZoom', + batch: batch + }); +} + +/** + * Merge roamController settings when multiple dataZooms share one roamController. + */ +function mergeControllerParams(dataZoomInfos) { + var controlType; + var opt = {}; + // DO NOT use reserved word (true, false, undefined) as key literally. Even if encapsulated + // as string, it is probably revert to reserved word by compress tool. See #7411. + var prefix = 'type_'; + var typePriority = { + 'type_true': 2, + 'type_move': 1, + 'type_false': 0, + 'type_undefined': -1 + }; + each$1(dataZoomInfos, function (dataZoomInfo) { + var oneType = dataZoomInfo.disabled ? false : dataZoomInfo.zoomLock ? 'move' : true; + if (typePriority[prefix + oneType] > typePriority[prefix + controlType]) { + controlType = oneType; + } + // Do not support that different 'shift'/'ctrl'/'alt' setting used in one coord sys. + extend(opt, dataZoomInfo.roamControllerOpt); + }); + + return { + controlType: controlType, + opt: opt + }; +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var bind$5 = 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); + + // Hance the `throttle` util ensures to preserve command order, + // here simply updating range all the time will not cause missing + // any of the the roam change. + this._range = dataZoomModel.getPercentRange(); + + // Reset controllers. + each$1(this.getTargetCoordInfo(), function (coordInfoList, coordSysName) { + + var allCoordIds = map(coordInfoList, function (coordInfo) { + return generateCoordId(coordInfo.model); + }); + + each$1(coordInfoList, function (coordInfo) { + var coordModel = coordInfo.model; + var dataZoomOption = dataZoomModel.option; + + register$2( + api, + { + coordId: generateCoordId(coordModel), + allCoordIds: allCoordIds, + containsPoint: function (e, x, y) { + return coordModel.coordinateSystem.containPoint([x, y]); + }, + dataZoomId: dataZoomModel.id, + throttleRate: dataZoomModel.get('throttle', true), + panGetRange: bind$5(this._onPan, this, coordInfo, coordSysName), + zoomGetRange: bind$5(this._onZoom, this, coordInfo, coordSysName), + zoomLock: dataZoomOption.zoomLock, + disabled: dataZoomOption.disabled, + roamControllerOpt: { + zoomOnMouseWheel: dataZoomOption.zoomOnMouseWheel, + moveOnMouseMove: dataZoomOption.moveOnMouseMove, + preventDefaultMouseMove: dataZoomOption.preventDefaultMouseMove + } + } + ); + }, this); + + }, this); + }, + + /** + * @override + */ + dispose: function () { + unregister$1(this.api, this.dataZoomModel.id); + InsideZoomView.superApply(this, 'dispose', arguments); + this._range = null; + }, + + /** + * @private + */ + _onPan: function (coordInfo, coordSysName, controller, dx, dy, oldX, oldY, newX, newY) { + var lastRange = this._range; + var range = lastRange.slice(); + + // Calculate transform by the first axis. + var axisModel = coordInfo.axisModels[0]; + if (!axisModel) { + return; + } + + var directionInfo = getDirectionInfo[coordSysName]( + [oldX, oldY], [newX, newY], axisModel, controller, coordInfo + ); + + var percentDelta = directionInfo.signal + * (range[1] - range[0]) + * directionInfo.pixel / directionInfo.pixelLength; + + sliderMove(percentDelta, range, [0, 100], 'all'); + + this._range = range; + + if (lastRange[0] !== range[0] || lastRange[1] !== range[1]) { + return range; + } + }, + + /** + * @private + */ + _onZoom: function (coordInfo, coordSysName, controller, scale, mouseX, mouseY) { + var lastRange = this._range; + var range = lastRange.slice(); + + // Calculate transform by the first axis. + var axisModel = coordInfo.axisModels[0]; + if (!axisModel) { + return; + } + + var directionInfo = getDirectionInfo[coordSysName]( + null, [mouseX, mouseY], axisModel, controller, coordInfo + ); + var percentPoint = ( + directionInfo.signal > 0 + ? (directionInfo.pixelStart + directionInfo.pixelLength - directionInfo.pixel) + : (directionInfo.pixel - directionInfo.pixelStart) + ) / directionInfo.pixelLength * (range[1] - range[0]) + range[0]; + + scale = Math.max(1 / scale, 0); + range[0] = (range[0] - percentPoint) * scale + percentPoint; + range[1] = (range[1] - percentPoint) * scale + percentPoint; + + // Restrict range. + var minMaxSpan = this.dataZoomModel.findRepresentativeAxisProxy().getMinMaxSpan(); + + sliderMove(0, range, [0, 100], 0, minMaxSpan.minSpan, minMaxSpan.maxSpan); + + this._range = range; + + if (lastRange[0] !== range[0] || lastRange[1] !== range[1]) { + return range; + } + } + +}); + +var getDirectionInfo = { + + grid: function (oldPoint, newPoint, axisModel, controller, coordInfo) { + var axis = axisModel.axis; + var ret = {}; + var rect = coordInfo.model.coordinateSystem.getRect(); + oldPoint = oldPoint || [0, 0]; + + if (axis.dim === 'x') { + ret.pixel = newPoint[0] - oldPoint[0]; + ret.pixelLength = rect.width; + ret.pixelStart = rect.x; + ret.signal = axis.inverse ? 1 : -1; + } + else { // axis.dim === 'y' + ret.pixel = newPoint[1] - oldPoint[1]; + ret.pixelLength = rect.height; + ret.pixelStart = rect.y; + ret.signal = axis.inverse ? -1 : 1; + } + + return ret; + }, + + polar: function (oldPoint, newPoint, axisModel, controller, coordInfo) { + var axis = axisModel.axis; + var ret = {}; + var polar = coordInfo.model.coordinateSystem; + var radiusExtent = polar.getRadiusAxis().getExtent(); + var angleExtent = polar.getAngleAxis().getExtent(); + + oldPoint = oldPoint ? polar.pointToCoord(oldPoint) : [0, 0]; + newPoint = polar.pointToCoord(newPoint); + + if (axisModel.mainType === 'radiusAxis') { + ret.pixel = newPoint[0] - oldPoint[0]; + // ret.pixelLength = Math.abs(radiusExtent[1] - radiusExtent[0]); + // ret.pixelStart = Math.min(radiusExtent[0], radiusExtent[1]); + ret.pixelLength = radiusExtent[1] - radiusExtent[0]; + ret.pixelStart = radiusExtent[0]; + ret.signal = axis.inverse ? 1 : -1; + } + else { // 'angleAxis' + ret.pixel = newPoint[1] - oldPoint[1]; + // ret.pixelLength = Math.abs(angleExtent[1] - angleExtent[0]); + // ret.pixelStart = Math.min(angleExtent[0], angleExtent[1]); + ret.pixelLength = angleExtent[1] - angleExtent[0]; + ret.pixelStart = angleExtent[0]; + ret.signal = axis.inverse ? -1 : 1; + } + + return ret; + }, + + singleAxis: function (oldPoint, newPoint, axisModel, controller, coordInfo) { + var axis = axisModel.axis; + var rect = coordInfo.model.coordinateSystem.getRect(); + var ret = {}; + + oldPoint = oldPoint || [0, 0]; + + if (axis.orient === 'horizontal') { + ret.pixel = newPoint[0] - oldPoint[0]; + ret.pixelLength = rect.width; + ret.pixelStart = rect.x; + ret.signal = axis.inverse ? 1 : -1; + } + else { // 'vertical' + ret.pixel = newPoint[1] - oldPoint[1]; + ret.pixelLength = rect.height; + ret.pixelStart = rect.y; + ret.signal = axis.inverse ? -1 : 1; + } + + return ret; + } +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +registerProcessor({ + + // `dataZoomProcessor` will only be performed in needed series. Consider if + // there is a line series and a pie series, it is better not to update the + // line series if only pie series is needed to be updated. + getTargetSeries: function (ecModel) { + var seriesModelMap = createHashMap(); + + ecModel.eachComponent('dataZoom', function (dataZoomModel) { + dataZoomModel.eachTargetAxis(function (dimNames, axisIndex, dataZoomModel) { + var axisProxy = dataZoomModel.getAxisProxy(dimNames.name, axisIndex); + each$1(axisProxy.getTargetSeriesModels(), function (seriesModel) { + seriesModelMap.set(seriesModel.uid, seriesModel); + }); + }); + }); + + return seriesModelMap; + }, + + modifyOutputEnd: true, + + // Consider appendData, where filter should be performed. Because data process is + // in block mode currently, it is not need to worry about that the overallProgress + // execute every frame. + overallReset: 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(function (dimNames, axisIndex, dataZoomModel) { + dataZoomModel.getAxisProxy(dimNames.name, axisIndex).reset(dataZoomModel, api); + }); + + // 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(function (dimNames, axisIndex, dataZoomModel) { + dataZoomModel.getAxisProxy(dimNames.name, axisIndex).filterData(dataZoomModel, api); + }); + }); + + 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] + }, true); + }); + } +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +registerAction('dataZoom', function (payload, ecModel) { + + var linkedNodesFinder = createLinkedNodesFinder( + bind(ecModel.eachComponent, ecModel, 'dataZoom'), + eachAxisDim$1, + 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 + ); + } + ); + + each$1(effectedModels, function (dataZoomModel, index) { + dataZoomModel.setRawRange({ + start: payload.start, + end: payload.end, + startValue: payload.startValue, + endValue: payload.endValue + }); + }); + +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * DataZoom component entry + */ + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var each$24 = each$1; + +var preprocessor$2 = function (option) { + var visualMap = option && option.visualMap; + + if (!isArray(visualMap)) { + visualMap = visualMap ? [visualMap] : []; + } + + each$24(visualMap, function (opt) { + if (!opt) { + return; + } + + // rename splitList to pieces + if (has$1(opt, 'splitList') && !has$1(opt, 'pieces')) { + opt.pieces = opt.splitList; + delete opt.splitList; + } + + var pieces = opt.pieces; + if (pieces && isArray(pieces)) { + each$24(pieces, function (piece) { + if (isObject$1(piece)) { + if (has$1(piece, 'start') && !has$1(piece, 'min')) { + piece.min = piece.start; + } + if (has$1(piece, 'end') && !has$1(piece, 'max')) { + piece.max = piece.end; + } + } + }); + } + }); +}; + +function has$1(obj, name) { + return obj && obj.hasOwnProperty && obj.hasOwnProperty(name); +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +ComponentModel.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'; +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var VISUAL_PRIORITY = PRIORITY.VISUAL.COMPONENT; + +registerVisual(VISUAL_PRIORITY, { + createOnAllSeries: true, + reset: function (seriesModel, ecModel) { + var resetDefines = []; + ecModel.eachComponent('visualMap', function (visualMapModel) { + var pipelineContext = seriesModel.pipelineContext; + if (!visualMapModel.isTargetSeries(seriesModel) + || (pipelineContext && pipelineContext.large) + ) { + return; + } + + resetDefines.push(incrementalApplyVisual( + visualMapModel.stateList, + visualMapModel.targetVisuals, + bind(visualMapModel.getValueState, visualMapModel), + visualMapModel.getDataDimension(seriesModel.getData()) + )); + }); + + return resetDefines; + } +}); + +// Only support color. +registerVisual(VISUAL_PRIORITY, { + createOnAllSeries: true, + reset: function (seriesModel, ecModel) { + var data = seriesModel.getData(); + var visualMetaList = []; + + ecModel.eachComponent('visualMap', function (visualMapModel) { + if (visualMapModel.isTargetSeries(seriesModel)) { + var visualMeta = visualMapModel.getVisualMeta( + bind(getColorVisual, null, seriesModel, visualMapModel) + ) || {stops: [], outerColors: []}; + + var concreteDim = visualMapModel.getDataDimension(data); + var dimInfo = data.getDimensionInfo(concreteDim); + if (dimInfo != null) { + // visualMeta.dimension should be dimension index, but not concrete dimension. + visualMeta.dimension = dimInfo.index; + visualMetaList.push(visualMeta); + } + } + }); + + // console.log(JSON.stringify(visualMetaList.map(a => a.stops))); + seriesModel.getData().setVisual('visualMeta', visualMetaList); + } +}); + +// FIXME +// performance and export for heatmap? +// value can be Infinity or -Infinity +function getColorVisual(seriesModel, visualMapModel, value, valueState) { + var mappings = visualMapModel.targetVisuals[valueState]; + var visualTypes = VisualMapping.prepareVisualTypes(mappings); + var resultVisual = { + color: seriesModel.getData().getVisual('color') // default color. + }; + + for (var i = 0, len = visualTypes.length; i < len; i++) { + var type = visualTypes[i]; + var mapping = mappings[ + type === 'opacity' ? '__alphaForOpacity' : type + ]; + mapping && mapping.applyVisual(value, getVisual, setVisual); + } + + return resultVisual.color; + + function getVisual(key) { + return resultVisual[key]; + } + + function setVisual(key, value) { + resultVisual[key] = value; + } +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * @file Visual mapping. + */ + +var visualDefault = { + + /** + * @public + */ + get: function (visualType, key, isCategory) { + var value = clone( + (defaultOption$3[visualType] || {})[key] + ); + + return isCategory + ? (isArray(value) ? value[value.length - 1] : value) + : value; + } + +}; + +var defaultOption$3 = { + + 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] + } +}; + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var mapVisual$2 = VisualMapping.mapVisual; +var eachVisual = VisualMapping.eachVisual; +var isArray$3 = isArray; +var each$25 = each$1; +var asc$3 = asc; +var linearMap$2 = linearMap; +var noop$2 = noop; + +var VisualMapModel = 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: 'all', // 'all' or null/undefined: all series. + // A number or an array of number: the specified 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$1.canvasSupported) { + thisOption.realtime = false; + } + + !isInit && replaceVisualOption( + thisOption, newOption, this.replacableOptionKeys + ); + + this.textStyleModel = this.getModel('textStyle'); + + this.resetItemSize(); + + this.completeVisualOption(); + }, + + /** + * @protected + */ + resetVisual: function (supplementVisualOption) { + var stateList = this.stateList; + supplementVisualOption = bind(supplementVisualOption, this); + + this.controllerVisuals = createVisualMappings( + this.option.controller, stateList, supplementVisualOption + ); + this.targetVisuals = createVisualMappings( + this.option.target, stateList, supplementVisualOption + ); + }, + + /** + * @protected + * @return {Array.} An array of series indices. + */ + getTargetSeriesIndices: function () { + var optionSeriesIndex = this.option.seriesIndex; + var seriesIndices = []; + + if (optionSeriesIndex == null || optionSeriesIndex === 'all') { + this.ecModel.eachSeries(function (seriesModel, index) { + seriesIndices.push(index); + }); + } + else { + seriesIndices = normalizeToArray(optionSeriesIndex); + } + + return seriesIndices; + }, + + /** + * @public + */ + eachTargetSeries: function (callback, context) { + each$1(this.getTargetSeriesIndices(), 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 (isArray(value)) { + value = value.slice(); + isMinMax = true; + } + + textValue = isCategory + ? value + : (isMinMax + ? [toFixed(value[0]), toFixed(value[1])] + : toFixed(value) + ); + + if (isString(formatter)) { + return formatter + .replace('{value}', isMinMax ? textValue[0] : textValue) + .replace('{value2}', isMinMax ? textValue[1] : textValue); + } + else if (isFunction$1(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(Math.min(precision, 20)); + } + }, + + /** + * @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$3([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; + var listDimensions = list.dimensions; + if (optDim == null && !listDimensions.length) { + return; + } + + if (optDim != null) { + return list.getDimension(optDim); + } + + var dimNames = list.dimensions; + for (var i = dimNames.length - 1; i >= 0; i--) { + var dimName = dimNames[i]; + var dimInfo = list.getDimensionInfo(dimName); + if (!dimInfo.isCalculationCoord) { + return dimName; + } + } + }, + + /** + * @public + * @override + */ + getExtent: function () { + return this._dataExtent.slice(); + }, + + /** + * @protected + */ + completeVisualOption: function () { + var ecModel = this.ecModel; + var thisOption = this.option; + var base = {inRange: thisOption.inRange, outOfRange: thisOption.outOfRange}; + + var target = thisOption.target || (thisOption.target = {}); + var controller = thisOption.controller || (thisOption.controller = {}); + + merge(target, base); // Do not override + 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$3(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: ecModel.get('gradientColor')}; + + // If using shortcut like: {inRange: 'symbol'}, complete default value. + each$25(this.stateList, function (state) { + var visualType = base[state]; + + if (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$25(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$25(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 + && clone(symbolExists) + || (isCategory ? 'roundRect' : ['roundRect']); + } + if (visuals.symbolSize == null) { + visuals.symbolSize = symbolSizeExists + && clone(symbolSizeExists) + || (isCategory ? itemSize[0] : [itemSize[0], itemSize[0]]); + } + + // Filter square and none. + visuals.symbol = mapVisual$2(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$2(symbolSize, function (value) { + return linearMap$2(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$2, + + /** + * @public + * @abstract + * @param {*|module:echarts/data/List} valueOrData + * @param {number} dataIndex + * @return {string} state See this.stateList + */ + getValueState: noop$2, + + /** + * FIXME + * Do not publish to thirt-part-dev temporarily + * util the interface is stable. (Should it return + * a function but not visual meta?) + * + * @pubilc + * @abstract + * @param {Function} getColorVisual + * params: value, valueState + * return: color + * @return {Object} visualMeta + * should includes {stops, outerColors} + * outerColor means [colorBeyondMinValue, colorBeyondMaxValue] + */ + getVisualMeta: noop$2 + +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +// 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: null // Whether trigger hoverLink when hover handle. + // If not specified, follow the value of `realtime`. + }, + + /** + * @override + */ + optionUpdated: function (newOption, isInit) { + ContinuousModel.superApply(this, 'optionUpdated', arguments); + + 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 (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); + + each$1(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 = 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); + }, this); + + result.push({seriesId: seriesModel.id, dataIndex: dataIndices}); + }, this); + + return result; + }, + + /** + * @implement + */ + getVisualMeta: function (getColorVisual) { + var oVals = getColorStopValues(this, 'outOfRange', this.getExtent()); + var iVals = getColorStopValues(this, 'inRange', this.option.range.slice()); + var stops = []; + + function setStop(value, valueState) { + stops.push({ + value: value, + color: getColorVisual(value, valueState) + }); + } + + // Format to: outOfRange -- inRange -- outOfRange. + var iIdx = 0; + var oIdx = 0; + var iLen = iVals.length; + var oLen = oVals.length; + + for (; oIdx < oLen && (!iVals.length || oVals[oIdx] <= iVals[0]); oIdx++) { + // If oVal[oIdx] === iVals[iIdx], oVal[oIdx] should be ignored. + if (oVals[oIdx] < iVals[iIdx]) { + setStop(oVals[oIdx], 'outOfRange'); + } + } + for (var first = 1; iIdx < iLen; iIdx++, first = 0) { + // If range is full, value beyond min, max will be clamped. + // make a singularity + first && stops.length && setStop(iVals[iIdx], 'outOfRange'); + setStop(iVals[iIdx], 'inRange'); + } + for (var first = 1; oIdx < oLen; oIdx++) { + if (!iVals.length || iVals[iVals.length - 1] < oVals[oIdx]) { + // make a singularity + if (first) { + stops.length && setStop(stops[stops.length - 1].value, 'outOfRange'); + first = 0; + } + setStop(oVals[oIdx], 'outOfRange'); + } + } + + var stopsLen = stops.length; + + return { + stops: stops, + outerColors: [ + stopsLen ? stops[0].color : 'transparent', + stopsLen ? stops[stopsLen - 1].color : 'transparent' + ] + }; + } + +}); + +function getColorStopValues(visualMapModel, valueState, dataExtent) { + if (dataExtent[0] === dataExtent[1]) { + return dataExtent.slice(); + } + + // When using colorHue mapping, it is not linear color any more. + // Moreover, canvas gradient seems not to be accurate linear. + // FIXME + // Should be arbitrary value 100? or based on pixel size? + var count = 200; + var step = (dataExtent[1] - dataExtent[0]) / count; + + 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; +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var VisualMapView = 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 = normalizeCssArray$1(visualMapModel.get('padding') || 0); + var rect = group.getBoundingRect(); + + group.add(new 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 can be Infinity or -Infinity + * @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); + + each$1(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; + + positionElement( + group, + model.getBoxLayoutParams(), + {width: api.getWidth(), height: api.getHeight()} + ); + }, + + /** + * @protected + * @abstract + */ + doRender: noop + +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * @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' + */ +function getItemAlign(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 = 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 + ]; +} + +/** + * Prepare dataIndex for outside usage, where dataIndex means rawIndex, and + * dataIndexInside means filtered index. + */ +function convertDataIndex(batch) { + each$1(batch || [], function (batchItem) { + if (batch.dataIndex != null) { + batch.dataIndexInside = batch.dataIndex; + batch.dataIndex = null; + } + }); + return batch; +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var linearMap$3 = linearMap; +var each$26 = each$1; +var mathMin$7 = Math.min; +var mathMax$7 = 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 Text({ + style: { + x: position[0], + y: position[1], + textVerticalAlign: orient === 'horizontal' ? 'middle' : align, + textAlign: orient === 'horizontal' ? align : 'center', + text: text, + textFont: textStyleModel.getFont(), + textFill: 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 = 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 ? getCursor$1(this._orient) : null, + bind(this._dragHandle, this, 'all', false), + bind(this._dragHandle, this, 'all', true) + )); + + var textRect = visualMapModel.textStyleModel.getTextRect('国'); + var textSize = mathMax$7(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 = bind(this._dragHandle, this, handleIndex, false); + var onDragEnd = bind(this._dragHandle, this, handleIndex, true); + var handleThumb = createPolygon( + createHandlePoints(handleIndex, textSize), + getCursor$1(this._orient), + 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 Text({ + draggable: true, + drift: onDrift, + onmousemove: function (e) { + // Fot mobile devicem, prevent screen slider on the button. + stop(e.event); + }, + ondragend: onDragEnd, + style: { + x: 0, y: 0, text: '', + textFont: textStyleModel.getFont(), + textFill: 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 Text({ + silent: true, + invisible: true, + style: { + x: 0, y: 0, text: '', + textFont: textStyleModel.getFont(), + textFill: 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$3(dataInterval[0], dataExtent, sizeExtent, true), + linearMap$3(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; + var sizeExtent = [0, visualMapModel.itemSize[1]]; + + sliderMove( + delta, + handleEnds, + sizeExtent, + handleIndex, + // cross is forbiden + 0 + ); + + var dataExtent = visualMapModel.getExtent(); + // Update data interval. + this._dataInterval = [ + linearMap$3(handleEnds[0], sizeExtent, dataExtent, true), + linearMap$3(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 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$26([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 = applyTransform$1( + shapes.handleLabelPoints[handleIndex], + 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$3(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 = applyTransform$1( + shapes.indicatorLabelPoint, + 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$7(mathMax$7(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$7(mathMax$7(sizeExtent[0], cursorPos), sizeExtent[1]); + + var halfHoverLinkSize = getHalfHoverLinkSize(visualMapModel, dataExtent, sizeExtent); + var hoverRange = [cursorPos - halfHoverLinkSize, cursorPos + halfHoverLinkSize]; + var cursorValue = linearMap$3(cursorPos, sizeExtent, dataExtent, true); + var valueRange = [ + linearMap$3(hoverRange[0], sizeExtent, dataExtent, true), + linearMap$3(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 = compressBatches(oldBatch, newBatch); + + this._dispatchHighDown('downplay', convertDataIndex(resultBatches[0])); + this._dispatchHighDown('highlight', convertDataIndex(resultBatches[1])); + }, + + /** + * @private + */ + _hoverLinkFromSeriesMouseOver: function (e) { + var el = e.target; + var visualMapModel = this.visualMapModel; + + if (!el || el.dataIndex == null) { + return; + } + + var dataModel = this.ecModel.getSeriesByIndex(el.seriesIndex); + + if (!visualMapModel.isTargetSeries(dataModel)) { + return; + } + + var data = dataModel.getData(el.dataType); + var value = data.get(visualMapModel.getDataDimension(data), 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', convertDataIndex(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 = getTransform(element, global ? null : this.group); + + return graphic[ + 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 Polygon({ + shape: {points: points}, + draggable: !!onDrift, + cursor: cursor, + drift: onDrift, + onmousemove: function (e) { + // Fot mobile devicem, prevent screen slider on the button. + stop(e.event); + }, + 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$7(halfHoverLinkSize, mathMax$7(pos, 0))], + [HOVER_LINK_OUT, 0], + [0, mathMin$7(halfHoverLinkSize, mathMax$7(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$3(hoverLinkDataSize, dataExtent, sizeExtent, true) / 2; + } + return halfHoverLinkSize; +} + +function useHoverLinkOnHandle(visualMapModel) { + var hoverLinkOnHandle = visualMapModel.get('hoverLinkOnHandle'); + return !!(hoverLinkOnHandle == null ? visualMapModel.get('realtime') : hoverLinkOnHandle); +} + +function getCursor$1(orient) { + return orient === 'vertical' ? 'ns-resize' : 'ew-resize'; +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var actionInfo$2 = { + type: 'selectDataRange', + event: 'dataRangeSelected', + // FIXME use updateView appears wrong + update: 'update' +}; + +registerAction(actionInfo$2, function (payload, ecModel) { + + ecModel.eachComponent({mainType: 'visualMap', query: payload}, function (model) { + model.setSelected(payload.selected); + }); + +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * DataZoom component entry + */ + +registerPreprocessor(preprocessor$2); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +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. + + minOpen: false, // Whether include values that smaller than `min`. + maxOpen: false, // Whether include values that bigger than `max`. + + 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. + + showLabel: null // By default, when text is used, label will hide (the logic + // is remained for compatibility reason) + }, + + /** + * @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.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 = clone(categories); + } + else { + mappingOption.dataExtent = this.getExtent(); + mappingOption.mappingMethod = 'piecewise'; + mappingOption.pieceList = map(this._pieceList, function (piece) { + var piece = clone(piece); + if (state !== 'inRange') { + // FIXME + // outOfRange do not support special visual in pieces. + piece.visual = null; + } + return piece; + }); + } + }); + }, + + /** + * @protected + * @override + */ + completeVisualOption: function () { + // Consider this case: + // visualMap: { + // pieces: [{symbol: 'circle', lt: 0}, {symbol: 'rect', gte: 0}] + // } + // where no inRange/outOfRange set but only pieces. So we should make + // default inRange/outOfRange for this case, otherwise visuals that only + // appear in `pieces` will not be taken into account in visual encoding. + + var option = this.option; + var visualTypesInPieces = {}; + var visualTypes = VisualMapping.listVisualTypes(); + var isCategory = this.isCategory(); + + each$1(option.pieces, function (piece) { + each$1(visualTypes, function (visualType) { + if (piece.hasOwnProperty(visualType)) { + visualTypesInPieces[visualType] = 1; + } + }); + }); + + each$1(visualTypesInPieces, function (v, visualType) { + var exists = 0; + each$1(this.stateList, function (state) { + exists |= has(option, state, visualType) + || has(option.target, state, visualType); + }, this); + + !exists && each$1(this.stateList, function (state) { + (option[state] || (option[state] = {}))[visualType] = visualDefault.get( + visualType, state === 'inRange' ? 'active' : 'inactive', isCategory + ); + }); + }, this); + + function has(obj, state, visualType) { + return obj && obj[state] && ( + isObject$1(obj[state]) + ? obj[state].hasOwnProperty(visualType) + : obj[state] === visualType // e.g., inRange: 'symbol' + ); + } + + VisualMapModel.prototype.completeVisualOption.apply(this, arguments); + }, + + _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. + each$1(pieceList, function (piece, index) { + var key = this.getSelectedMapKey(piece); + if (!selected.hasOwnProperty(key)) { + selected[key] = true; + } + }, this); + + if (thisOption.selectedMode === 'single') { + // Ensure there is only one selected. + var hasSel = false; + + each$1(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 = 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); + }, this); + + result.push({seriesId: seriesModel.id, dataIndex: dataIndices}); + }, this); + + return result; + }, + + /** + * @private + * @param {Object} piece piece.value or piece.interval is required. + * @return {number} Can be Infinity or -Infinity + */ + 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] === -Infinity && pieceInterval[1] === Infinity) + ? 0 + : (pieceInterval[0] + pieceInterval[1]) / 2; + } + } + return representValue; + }, + + getVisualMeta: function (getColorVisual) { + // Do not support category. (category axis is ordinal, numerical) + if (this.isCategory()) { + return; + } + + var stops = []; + var outerColors = []; + var visualMapModel = this; + + function setStop(interval, valueState) { + var representValue = visualMapModel.getRepresentValue({interval: interval}); + if (!valueState) { + valueState = visualMapModel.getValueState(representValue); + } + var color = getColorVisual(representValue, valueState); + if (interval[0] === -Infinity) { + outerColors[0] = color; + } + else if (interval[1] === Infinity) { + outerColors[1] = color; + } + else { + stops.push( + {value: interval[0], color: color}, + {value: interval[1], color: color} + ); + } + } + + // Suplement + var pieceList = this._pieceList.slice(); + if (!pieceList.length) { + pieceList.push({interval: [-Infinity, Infinity]}); + } + else { + var edge = pieceList[0].interval[0]; + edge !== -Infinity && pieceList.unshift({interval: [-Infinity, edge]}); + edge = pieceList[pieceList.length - 1].interval[1]; + edge !== Infinity && pieceList.push({interval: [edge, Infinity]}); + } + + var curr = -Infinity; + each$1(pieceList, function (piece) { + var interval = piece.interval; + if (interval) { + // Fulfill gap. + interval[0] > curr && setStop([curr, interval[0]], 'outOfRange'); + setStop(interval.slice()); + curr = interval[1]; + } + }, this); + + return {stops: stops, outerColors: outerColors}; + } + +}); + +/** + * 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 = Math.min(thisOption.precision, 20); + 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); + + var index = 0; + + if (thisOption.minOpen) { + pieceList.push({ + index: index++, + interval: [-Infinity, dataExtent[0]], + close: [0, 0] + }); + } + + for ( + var curr = dataExtent[0], len = index + splitNumber; + index < len; + curr += splitStep + ) { + var max = index === splitNumber - 1 ? dataExtent[1] : (curr + splitStep); + + pieceList.push({ + index: index++, + interval: [curr, max], + close: [1, 1] + }); + } + + if (thisOption.maxOpen) { + pieceList.push({ + index: index++, + interval: [dataExtent[1], Infinity], + close: [0, 0] + }); + } + + reformIntervals(pieceList); + + each$1(pieceList, function (piece) { + piece.text = this.formatValueText(piece.interval); + }, this); + }, + + categories: function () { + var thisOption = this.option; + each$1(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; + + each$1(thisOption.pieces, function (pieceListItem, index) { + + if (!isObject$1(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 (__DEV__) { + 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 + reformIntervals(pieceList); + + each$1(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(); + } +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +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 endsText = viewData.endsText; + var showLabel = retrieve(visualMapModel.get('showLabel', true), !endsText); + + endsText && this._renderEndsText( + thisGroup, endsText[0], itemSize, showLabel, itemAlign + ); + + each$1(viewData.viewPieceList, renderItem, this); + + endsText && this._renderEndsText( + thisGroup, endsText[1], itemSize, showLabel, itemAlign + ); + + box( + visualMapModel.get('orient'), thisGroup, visualMapModel.get('itemGap') + ); + + this.renderBackground(thisGroup); + + this.positionGroup(thisGroup); + + function renderItem(item) { + var piece = item.piece; + + var itemGroup = new Group(); + itemGroup.onclick = 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 Text({ + style: { + x: itemAlign === 'right' ? -textGap : itemSize[0] + textGap, + y: itemSize[1] / 2, + text: piece.text, + textVerticalAlign: 'middle', + textAlign: itemAlign, + textFont: textFont, + textFill: textFill, + opacity: visualState === 'outOfRange' ? 0.5 : 1 + } + })); + } + + thisGroup.add(itemGroup); + } + }, + + /** + * @private + */ + _enableHoverLink: function (itemGroup, pieceIndex) { + itemGroup + .on('mouseover', bind(onHoverLink, this, 'highlight')) + .on('mouseout', bind(onHoverLink, this, 'downplay')); + + function onHoverLink(method) { + var visualMapModel = this.visualMapModel; + + visualMapModel.option.hoverLink && this.api.dispatchAction({ + type: method, + batch: convertDataIndex( + visualMapModel.findTargetDataIndices(pieceIndex) + ) + }); + } + }, + + /** + * @private + */ + _getItemAlign: function () { + var visualMapModel = this.visualMapModel; + var modelOption = visualMapModel.option; + + if (modelOption.orient === 'vertical') { + return 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, showLabel, itemAlign) { + if (!text) { + return; + } + + var itemGroup = new Group(); + var textStyleModel = this.visualMapModel.textStyleModel; + + itemGroup.add(new Text({ + style: { + x: showLabel ? (itemAlign === 'right' ? itemSize[0] : 0) : itemSize[0] / 2, + y: itemSize[1] / 2, + textVerticalAlign: 'middle', + textAlign: showLabel ? itemAlign : 'center', + text: text, + textFont: textStyleModel.getFont(), + textFill: 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 = 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(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 = clone(option.selected); + var newKey = visualMapModel.getSelectedMapKey(piece); + + if (option.selectedMode === 'single') { + selected[newKey] = true; + each$1(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 + }); + } +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * DataZoom component entry + */ + +registerPreprocessor(preprocessor$2); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * visualMap component entry + */ + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var addCommas$1 = addCommas; +var encodeHTML$1 = encodeHTML; + +function fillLabel(opt) { + defaultEmphasis(opt, 'label', ['show']); +} +var MarkerModel = extendComponentModel({ + + type: 'marker', + + dependencies: ['series', 'grid', 'polar', 'geo'], + + /** + * @overrite + */ + init: function (option, parentModel, ecModel, extraOpt) { + + if (__DEV__) { + 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} + */ + isAnimationEnabled: function () { + if (env$1.node) { + return false; + } + + var hostSeries = this.__hostSeries; + return this.getShallow('animation') && hostSeries && hostSeries.isAnimationEnabled(); + }, + + 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, true); + + var markerModel = seriesModel[modelPropName]; + if (!markerOpt || !markerOpt.data) { + seriesModel[modelPropName] = null; + return; + } + if (!markerModel) { + if (isInit) { + // Default label emphasis `position` and `show` + fillLabel(markerOpt); + } + each$1(markerOpt.data, function (item) { + // FIXME Overwrite fillLabel method ? + if (item instanceof Array) { + fillLabel(item[0]); + fillLabel(item[1]); + } + else { + fillLabel(item); + } + }); + + markerModel = new MarkerModel( + markerOpt, this, ecModel + ); + + extend(markerModel, { + mainType: this.mainType, + // Use the same series index and name + seriesIndex: seriesModel.seriesIndex, + name: seriesModel.name, + createdBySelf: true + }); + + 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 = isArray(value) + ? map(value, addCommas$1).join(', ') : addCommas$1(value); + var name = data.getName(dataIndex); + var html = encodeHTML$1(this.name); + if (value != null || name) { + html += '
'; + } + if (name) { + html += encodeHTML$1(name); + if (value != null) { + html += ' : '; + } + } + if (value != null) { + html += encodeHTML$1(formattedValue); + } + return html; + }, + + getData: function () { + return this._data; + }, + + setData: function (data) { + this._data = data; + } +}); + +mixin(MarkerModel, dataFormatMixin); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +MarkerModel.extend({ + + type: 'markPoint', + + defaultOption: { + zlevel: 0, + z: 5, + symbol: 'pin', + symbolSize: 50, + //symbolRotate: 0, + //symbolOffset: [0, 0] + tooltip: { + trigger: 'item' + }, + label: { + show: true, + position: 'inside' + }, + itemStyle: { + borderWidth: 2 + }, + emphasis: { + label: { + show: true + } + } + } +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var indexOf$2 = 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)); +} + +// Make it simple, do not visit all stacked value to count precision. +// function getPrecision(data, valueAxisDim, dataIndex) { +// var precision = -1; +// var stackedDim = data.mapDimension(valueAxisDim); +// do { +// precision = Math.max( +// numberUtil.getPrecision(data.get(stackedDim, dataIndex)), +// precision +// ); +// var stackedOnSeries = data.getCalculationInfo('stackedOnSeries'); +// if (stackedOnSeries) { +// var byValue = data.get(data.getCalculationInfo('stackedByDimension'), dataIndex); +// data = stackedOnSeries.getData(); +// dataIndex = data.indexOf(data.getCalculationInfo('stackedByDimension'), byValue); +// stackedDim = data.getCalculationInfo('stackedDimension'); +// } +// else { +// data = null; +// } +// } while (data); + +// return precision; +// } + +function markerTypeCalculatorWithExtent( + mlType, data, otherDataDim, targetDataDim, otherCoordIndex, targetCoordIndex +) { + var coordArr = []; + + var stacked = isDimensionStacked(data, targetDataDim /*, otherDataDim*/); + var calcDataDim = stacked + ? data.getCalculationInfo('stackResultDimension') + : targetDataDim; + + var value = numCalculate(data, calcDataDim, mlType); + + var dataIndex = data.indicesOfNearest(calcDataDim, value)[0]; + coordArr[otherCoordIndex] = data.get(otherDataDim, dataIndex); + coordArr[targetCoordIndex] = data.get(targetDataDim, dataIndex); + + // Make it simple, do not visit all stacked value to count precision. + var precision = getPrecision(data.get(targetDataDim, dataIndex)); + precision = Math.min(precision, 20); + if (precision >= 0) { + coordArr[targetCoordIndex] = +coordArr[targetCoordIndex].toFixed(precision); + } + + return coordArr; +} + +var curry$7 = curry; +// TODO Specified percent +var markerTypeCalculator = { + /** + * @method + * @param {module:echarts/data/List} data + * @param {string} baseAxisDim + * @param {string} valueAxisDim + */ + min: curry$7(markerTypeCalculatorWithExtent, 'min'), + /** + * @method + * @param {module:echarts/data/List} data + * @param {string} baseAxisDim + * @param {string} valueAxisDim + */ + max: curry$7(markerTypeCalculatorWithExtent, 'max'), + + /** + * @method + * @param {module:echarts/data/List} data + * @param {string} baseAxisDim + * @param {string} valueAxisDim + */ + average: curry$7(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} + */ +function dataTransform(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) && !isArray(item.coord) && coordSys) { + var dims = coordSys.dimensions; + var axisInfo = getAxisInfo$1(item, data, coordSys, seriesModel); + + // Clone the option + // Transform the properties xAxis, yAxis, radiusAxis, angleAxis, geoCoord to value + item = clone(item); + + if (item.type + && markerTypeCalculator[item.type] + && axisInfo.baseAxis && axisInfo.valueAxis + ) { + var otherCoordIndex = indexOf$2(dims, axisInfo.baseAxis.dim); + var targetCoordIndex = indexOf$2(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]]) { + coord[i] = numCalculate(data, data.mapDimension(dims[i]), coord[i]); + } + } + item.coord = coord; + } + } + return item; +} + +function getAxisInfo$1(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(dataDimToCoordDim(seriesModel, ret.valueDataDim)); + ret.baseAxis = coordSys.getOtherAxis(ret.valueAxis); + ret.baseDataDim = data.mapDimension(ret.baseAxis.dim); + } + else { + ret.baseAxis = seriesModel.getBaseAxis(); + ret.valueAxis = coordSys.getOtherAxis(ret.baseAxis); + ret.baseDataDim = data.mapDimension(ret.baseAxis.dim); + ret.valueDataDim = data.mapDimension(ret.valueAxis.dim); + } + + return ret; +} + +function dataDimToCoordDim(seriesModel, dataDim) { + var data = seriesModel.getData(); + var dimensions = data.dimensions; + dataDim = data.getDimension(dataDim); + for (var i = 0; i < dimensions.length; i++) { + var dimItem = data.getDimensionInfo(dimensions[i]); + if (dimItem.name === dataDim) { + return dimItem.coordDim; + } + } +} + +/** + * Filter data which is out of coordinateSystem range + * [dataFilter description] + * @param {module:echarts/coord/*} [coordSys] + * @param {Object} item + * @return {boolean} + */ +function dataFilter$1(coordSys, item) { + // Alwalys return true if there is no coordSys + return (coordSys && coordSys.containData && item.coord && !hasXOrY(item)) + ? coordSys.containData(item.coord) : true; +} + +function dimValueGetter(item, dimName, dataIndex, dimIndex) { + // x, y, radius, angle + if (dimIndex < 2) { + return item.coord && item.coord[dimIndex]; + } + return item.value; +} + +function numCalculate(data, valueDataDim, type) { + if (type === 'average') { + var sum = 0; + var count = 0; + data.each(valueDataDim, function (val, idx) { + if (!isNaN(val)) { + sum += val; + count++; + } + }); + return sum / count; + } + else if (type === 'median') { + return data.getMedian(valueDataDim); + } + else { + // max & min + return data.getDataExtent(valueDataDim, true)[type === 'max' ? 1 : 0]; + } +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var MarkerView = extendComponentView({ + + type: 'marker', + + init: function () { + /** + * Markline grouped by series + * @private + * @type {module:zrender/core/util.HashMap} + */ + this.markerGroupMap = createHashMap(); + }, + + render: function (markerModel, ecModel, api) { + var markerGroupMap = this.markerGroupMap; + markerGroupMap.each(function (item) { + item.__keep = false; + }); + + var markerModelKey = this.type + 'Model'; + ecModel.eachSeries(function (seriesModel) { + var markerModel = seriesModel[markerModelKey]; + markerModel && this.renderSeries(seriesModel, markerModel, ecModel, api); + }, this); + + markerGroupMap.each(function (item) { + !item.__keep && this.group.remove(item.group); + }, this); + }, + + renderSeries: function () {} +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +function updateMarkerLayout(mpData, seriesModel, api) { + var coordSys = seriesModel.coordinateSystem; + mpData.each(function (idx) { + var itemModel = mpData.getItemModel(idx); + var point; + var xPx = parsePercent$1(itemModel.get('x'), api.getWidth()); + var yPx = parsePercent$1(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); + }); +} + +MarkerView.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.get(seriesModel.id).updateLayout(mpModel); + // } + // }, this); + // }, + + updateTransform: function (markPointModel, ecModel, api) { + ecModel.eachSeries(function (seriesModel) { + var mpModel = seriesModel.markPointModel; + if (mpModel) { + updateMarkerLayout(mpModel.getData(), seriesModel, api); + this.markerGroupMap.get(seriesModel.id).updateLayout(mpModel); + } + }, this); + }, + + renderSeries: function (seriesModel, mpModel, ecModel, api) { + var coordSys = seriesModel.coordinateSystem; + var seriesId = seriesModel.id; + var seriesData = seriesModel.getData(); + + var symbolDrawMap = this.markerGroupMap; + var symbolDraw = symbolDrawMap.get(seriesId) + || symbolDrawMap.set(seriesId, new SymbolDraw()); + + var mpData = createList$1(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.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$1(coordSys, seriesModel, mpModel) { + var coordDimsInfos; + if (coordSys) { + coordDimsInfos = map(coordSys && coordSys.dimensions, function (coordDim) { + var info = seriesModel.getData().getDimensionInfo( + seriesModel.getData().mapDimension(coordDim) + ) || {}; + // In map series data don't have lng and lat dimension. Fallback to same with coordSys + return defaults({name: coordDim}, info); + }); + } + else { + coordDimsInfos =[{ + name: 'value', + type: 'float' + }]; + } + + var mpData = new List(coordDimsInfos, mpModel); + var dataOpt = map(mpModel.get('data'), curry( + dataTransform, seriesModel + )); + if (coordSys) { + dataOpt = filter( + dataOpt, curry(dataFilter$1, coordSys) + ); + } + + mpData.initData(dataOpt, null, + coordSys ? dimValueGetter : function (item) { + return item.value; + } + ); + + return mpData; +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +// HINT Markpoint can't be used too much +registerPreprocessor(function (opt) { + // Make sure markPoint component is enabled + opt.markPoint = opt.markPoint || {}; +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +MarkerModel.extend({ + + type: 'markLine', + + defaultOption: { + zlevel: 0, + z: 5, + + symbol: ['circle', 'arrow'], + symbolSize: [8, 16], + + //symbolRotate: 0, + + precision: 2, + tooltip: { + trigger: 'item' + }, + label: { + show: true, + position: 'end' + }, + lineStyle: { + type: 'dashed' + }, + emphasis: { + label: { + show: true + }, + lineStyle: { + width: 3 + } + }, + animationEasing: 'linear' + } +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var markLineTransform = function (seriesModel, coordSys, mlModel, item) { + var data = seriesModel.getData(); + // Special type markLine like 'min', 'max', 'average', 'median' + var mlType = item.type; + + if (!isArray(item) + && ( + mlType === 'min' || mlType === 'max' || mlType === 'average' || mlType === 'median' + // 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 = retrieve(item.yAxis, item.xAxis); + } + else { + var axisInfo = getAxisInfo$1(item, data, coordSys, seriesModel); + valueDataDim = axisInfo.valueDataDim; + valueAxis = axisInfo.valueAxis; + value = numCalculate(data, valueDataDim, mlType); + } + var valueIndex = valueDataDim === 'x' ? 0 : 1; + var baseIndex = 1 - valueIndex; + + var mlFrom = 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 && typeof value === 'number') { + value = +value.toFixed(Math.min(precision, 20)); + } + + 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 = [ + dataTransform(seriesModel, item[0]), + dataTransform(seriesModel, item[1]), + 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 + merge(item[2], item[0]); + 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 dataFilter$1(coordSys, item[0]) + && dataFilter$1(coordSys, item[1]); +} + +function updateSingleMarkerEndLayout( + data, idx, isFrom, seriesModel, api +) { + var coordSys = seriesModel.coordinateSystem; + var itemModel = data.getItemModel(idx); + + var point; + var xPx = parsePercent$1(itemModel.get('x'), api.getWidth()); + var yPx = parsePercent$1(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); +} + +MarkerView.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.get(seriesModel.id).updateLayout(); + + // } + // }, this); + // }, + + updateTransform: 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.get(seriesModel.id).updateLayout(); + + } + }, this); + }, + + renderSeries: function (seriesModel, mlModel, ecModel, api) { + var coordSys = seriesModel.coordinateSystem; + var seriesId = seriesModel.id; + var seriesData = seriesModel.getData(); + + var lineDrawMap = this.markerGroupMap; + var lineDraw = lineDrawMap.get(seriesId) + || lineDrawMap.set(seriesId, new LineDraw()); + this.group.add(lineDraw.group); + + var mlData = createList$2(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 (!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.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.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$2(coordSys, seriesModel, mlModel) { + + var coordDimsInfos; + if (coordSys) { + coordDimsInfos = map(coordSys && coordSys.dimensions, function (coordDim) { + var info = seriesModel.getData().getDimensionInfo( + seriesModel.getData().mapDimension(coordDim) + ) || {}; + // In map series data don't have lng and lat dimension. Fallback to same with coordSys + return defaults({name: coordDim}, 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 = map(mlModel.get('data'), curry( + markLineTransform, seriesModel, coordSys, mlModel + )); + if (coordSys) { + optData = filter( + optData, curry(markLineFilter, coordSys) + ); + } + var dimValueGetter$$1 = coordSys ? dimValueGetter : function (item) { + return item.value; + }; + fromData.initData( + map(optData, function (item) { return item[0]; }), + null, dimValueGetter$$1 + ); + toData.initData( + map(optData, function (item) { return item[1]; }), + null, dimValueGetter$$1 + ); + lineData.initData( + map(optData, function (item) { return item[2]; }) + ); + lineData.hasItemOption = true; + + return { + from: fromData, + to: toData, + line: lineData + }; +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +registerPreprocessor(function (opt) { + // Make sure markLine component is enabled + opt.markLine = opt.markLine || {}; +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +MarkerModel.extend({ + + type: 'markArea', + + defaultOption: { + zlevel: 0, + // PENDING + z: 1, + tooltip: { + trigger: 'item' + }, + // markArea should fixed on the coordinate system + animation: false, + label: { + show: true, + position: 'top' + }, + itemStyle: { + // color and borderColor default to use color from series + // color: 'auto' + // borderColor: 'auto' + borderWidth: 0 + }, + + emphasis: { + label: { + show: true, + position: 'top' + } + } + } +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +// TODO Better on polar + +var markAreaTransform = function (seriesModel, coordSys, maModel, item) { + var lt = dataTransform(seriesModel, item[0]); + var rb = dataTransform(seriesModel, item[1]); + var retrieve$$1 = retrieve; + + // FIXME make sure lt is less than rb + var ltCoord = lt.coord; + var rbCoord = rb.coord; + ltCoord[0] = retrieve$$1(ltCoord[0], -Infinity); + ltCoord[1] = retrieve$$1(ltCoord[1], -Infinity); + + rbCoord[0] = retrieve$$1(rbCoord[0], Infinity); + rbCoord[1] = retrieve$$1(rbCoord[1], Infinity); + + // Merge option into one + var result = 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$1(val) { + return !isNaN(val) && !isFinite(val); +} + +// If a markArea has one dim +function ifMarkLineHasOnlyDim$1(dimIndex, fromCoord, toCoord, coordSys) { + var otherDimIndex = 1 - dimIndex; + return isInifinity$1(fromCoord[otherDimIndex]) && isInifinity$1(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(1, fromCoord, toCoord, coordSys) + || ifMarkLineHasOnlyDim$1(0, fromCoord, toCoord, coordSys)) + ) { + return true; + } + } + return dataFilter$1(coordSys, { + coord: fromCoord, + x: item.x0, + y: item.y0 + }) + || dataFilter$1(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 = parsePercent$1(itemModel.get(dims[0]), api.getWidth()); + var yPx = parsePercent$1(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); + var pt = [x, y]; + coordSys.clampData && coordSys.clampData(pt, pt); + point = coordSys.dataToPoint(pt, 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$1(x)) { + point[0] = xAxis.toGlobalCoord(xAxis.getExtent()[dims[0] === 'x0' ? 0 : 1]); + } + else if (isInifinity$1(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']]; + +MarkerView.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); + // }, + + updateTransform: function (markAreaModel, ecModel, api) { + ecModel.eachSeries(function (seriesModel) { + var maModel = seriesModel.markAreaModel; + if (maModel) { + var areaData = maModel.getData(); + areaData.each(function (idx) { + var points = 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 seriesId = seriesModel.id; + var seriesData = seriesModel.getData(); + + var areaGroupMap = this.markerGroupMap; + var polygonGroup = areaGroupMap.get(seriesId) + || areaGroupMap.set(seriesId, {group: new Group()}); + + this.group.add(polygonGroup.group); + polygonGroup.__keep = true; + + var areaData = createList$3(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, 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 Polygon({ + shape: { + points: areaData.getItemLayout(idx) + } + }); + areaData.setItemGraphicEl(idx, polygon); + polygonGroup.group.add(polygon); + }) + .update(function (newIdx, oldIdx) { + var polygon = polygonGroup.__data.getItemGraphicEl(oldIdx); + 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'); + var labelHoverModel = itemModel.getModel('emphasis.label'); + var color = areaData.getItemVisual(idx, 'color'); + polygon.useStyle( + defaults( + itemModel.getModel('itemStyle').getItemStyle(), + { + fill: modifyAlpha(color, 0.4), + stroke: color + } + ) + ); + + polygon.hoverStyle = itemModel.getModel('emphasis.itemStyle').getItemStyle(); + + setLabelStyle( + polygon.style, polygon.hoverStyle, labelModel, labelHoverModel, + { + labelFetcher: maModel, + labelDataIndex: idx, + defaultText: areaData.getName(idx) || '', + isRectText: true, + autoColor: color + } + ); + + 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$3(coordSys, seriesModel, maModel) { + + var coordDimsInfos; + var areaData; + var dims = ['x0', 'y0', 'x1', 'y1']; + if (coordSys) { + coordDimsInfos = map(coordSys && coordSys.dimensions, function (coordDim) { + var data = seriesModel.getData(); + var info = data.getDimensionInfo( + data.mapDimension(coordDim) + ) || {}; + // In map series data don't have lng and lat dimension. Fallback to same with coordSys + return defaults({name: coordDim}, info); + }); + areaData = new List(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 = map(maModel.get('data'), curry( + markAreaTransform, seriesModel, coordSys, maModel + )); + if (coordSys) { + optData = filter( + optData, curry(markAreaFilter, coordSys) + ); + } + + var dimValueGetter$$1 = 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$$1); + areaData.hasItemOption = true; + return areaData; +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +registerPreprocessor(function (opt) { + // Make sure markArea component is enabled + opt.markArea = opt.markArea || {}; +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var preprocessor$3 = function (option) { + var timelineOpt = option && option.timeline; + + if (!isArray(timelineOpt)) { + timelineOpt = timelineOpt ? [timelineOpt] : []; + } + + each$1(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$2(opt, 'controlPosition')) { + var controlStyle = opt.controlStyle || (opt.controlStyle = {}); + if (!has$2(controlStyle, 'position')) { + controlStyle.position = opt.controlPosition; + } + if (controlStyle.position === 'none' && !has$2(controlStyle, 'show')) { + controlStyle.show = false; + delete controlStyle.position; + } + delete opt.controlPosition; + } + + each$1(opt.data || [], function (dataItem) { + if (isObject$1(dataItem) && !isArray(dataItem)) { + if (!has$2(dataItem, 'value') && has$2(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}; + + each$1(label, function (value, name) { + if (!excludeLabelAttr[name] && !has$2(labelNormal, name)) { + labelNormal[name] = value; + } + }); + + if (itemStyleEmphasis.label && !has$2(label, 'emphasis')) { + label.emphasis = itemStyleEmphasis.label; + delete itemStyleEmphasis.label; + } +} + +function has$2(obj, attr) { + return obj.hasOwnProperty(attr); +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +ComponentModel.registerSubTypeDefaulter('timeline', function () { + // Only slider now. + return 'slider'; +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +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); + } + } + + // Set normalized currentIndex to payload. + ecModel.resetOption('timeline'); + + return defaults({ + currentIndex: timelineModel.option.currentIndex + }, payload); + } +); + +registerAction( + + {type: 'timelinePlayChange', event: 'timelinePlayChanged', update: 'update'}, + + function (payload, ecModel) { + var timelineModel = ecModel.getComponent('timeline'); + if (timelineModel && payload.playState != null) { + timelineModel.setPlayState(payload.playState); + } + } +); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +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: {}, + label: { + color: '#000' + }, + + 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 = []; + each$1(dataArr, function (item, index) { + var value = getDataItemValue(item); + var newItem; + + if (isObject$1(item)) { + newItem = clone(item); + newItem.value = index; + } + else { + newItem = index; + } + + idxArr.push(newItem); + + if (!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(); + } + } + +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +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 + show: true, + interval: 'auto', + rotate: 0, + // formatter: null, + // 其余属性默认使用全局文本样式,详见TEXTSTYLE + color: '#304654' + }, + itemStyle: { + color: '#304654', + borderWidth: 1 + }, + + 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 + + color: '#304654', + borderColor: '#304654', + borderWidth: 1 + }, + + emphasis: { + label: { + show: true, + // 其余属性默认使用全局文本样式,详见TEXTSTYLE + color: '#c23531' + }, + + itemStyle: { + color: '#c23531' + }, + + controlStyle: { + color: '#c23531', + borderColor: '#c23531', + borderWidth: 2 + } + }, + data: [] + } + +}); + +mixin(SliderTimelineModel, dataFormatMixin); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var TimelineView = Component.extend({ + type: 'timeline' +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * 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'; + + /** + * Axis model + * @param {module:echarts/component/TimelineModel} + */ + this.model = null; +}; + +TimelineAxis.prototype = { + + constructor: TimelineAxis, + + /** + * @override + */ + getLabelModel: function () { + return this.model.getModel('label'); + }, + + /** + * @override + */ + isHorizontal: function () { + return this.model.get('orient') === 'horizontal'; + } + +}; + +inherits(TimelineAxis, Axis); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var bind$6 = bind; +var each$27 = each$1; + +var PI$4 = Math.PI; + +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:zrender/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$27( + ['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.position'); + var orient = timelineModel.get('orient'); + var viewRect = getViewRect$4(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]; + } + + 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$4 / 2 + }; + + // Position + var mainLength = orient === 'vertical' ? viewRect.height : viewRect.width; + + var controlModel = timelineModel.getModel('controlStyle'); + var showControl = controlModel.get('show', true); + 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.rotate') || 0; + labelRotation = labelRotation * PI$4 / 180; // To radian. + + var playPosition; + var prevBtnPosition; + var nextBtnPosition; + var axisExtent; + var controlPosition = controlModel.get('position', 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: timelineModel.get('label.align') || labelAlignMap[orient], + labelBaseline: timelineModel.get('label.verticalAlign') + || timelineModel.get('label.baseline') + || 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') { + // transform to horizontal, inverse rotate by left-top point. + var m = create$1(); + var rotateOriginX = viewRect.x; + var rotateOriginY = viewRect.y + viewRect.height; + translate(m, m, [-rotateOriginX, -rotateOriginY]); + rotate(m, m, -PI$4 / 2); + 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 = createScaleByModel(timelineModel, axisType); + + // Customize scale. The `tickValue` is `dataIndex`. + scale.getTicks = function () { + return data.mapArray(['value'], function (value) { + return value; + }); + }; + + var dataExtent = data.getDataExtent('value'); + scale.setExtent(dataExtent[0], dataExtent[1]); + scale.niceTicks(); + + var axis = new TimelineAxis('value', scale, layoutInfo.axisExtent, axisType); + axis.model = timelineModel; + + return axis; + }, + + _createGroup: function (name) { + var newGroup = this['_' + name] = new 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 Line({ + shape: { + x1: axisExtent[0], y1: 0, + x2: axisExtent[1], y2: 0 + }, + style: extend( + {lineCap: 'round'}, + timelineModel.getModel('lineStyle').getLineStyle() + ), + silent: true, + z2: 1 + })); + }, + + /** + * @private + */ + _renderAxisTick: function (layoutInfo, group, axis, timelineModel) { + var data = timelineModel.getData(); + // Show all ticks, despite ignoring strategy. + var ticks = axis.scale.getTicks(); + + // The value is dataIndex, see the costomized scale. + each$27(ticks, function (value) { + var tickCoord = axis.dataToCoord(value); + var itemModel = data.getItemModel(value); + var itemStyleModel = itemModel.getModel('itemStyle'); + var hoverStyleModel = itemModel.getModel('emphasis.itemStyle'); + var symbolOpt = { + position: [tickCoord, 0], + onclick: bind$6(this._changeTimeline, this, value) + }; + var el = giveSymbol(itemModel, itemStyleModel, group, symbolOpt); + setHoverStyle(el, hoverStyleModel.getItemStyle()); + + if (itemModel.get('tooltip')) { + el.dataIndex = value; + el.dataModel = timelineModel; + } + else { + el.dataIndex = el.dataModel = null; + } + + }, this); + }, + + /** + * @private + */ + _renderAxisLabel: function (layoutInfo, group, axis, timelineModel) { + var labelModel = axis.getLabelModel(); + + if (!labelModel.get('show')) { + return; + } + + var data = timelineModel.getData(); + var labels = axis.getViewLabels(); + + each$27(labels, function (labelItem) { + // The tickValue is dataIndex, see the costomized scale. + var dataIndex = labelItem.tickValue; + + var itemModel = data.getItemModel(dataIndex); + var normalLabelModel = itemModel.getModel('label'); + var hoverLabelModel = itemModel.getModel('emphasis.label'); + var tickCoord = axis.dataToCoord(labelItem.tickValue); + var textEl = new Text({ + position: [tickCoord, 0], + rotation: layoutInfo.labelRotation - layoutInfo.rotation, + onclick: bind$6(this._changeTimeline, this, dataIndex), + silent: false + }); + setTextStyle(textEl.style, normalLabelModel, { + text: labelItem.formattedLabel, + textAlign: layoutInfo.labelAlign, + textVerticalAlign: layoutInfo.labelBaseline + }); + + group.add(textEl); + setHoverStyle( + textEl, setTextStyle({}, hoverLabelModel) + ); + + }, this); + }, + + /** + * @private + */ + _renderControl: function (layoutInfo, group, axis, timelineModel) { + var controlSize = layoutInfo.controlSize; + var rotation = layoutInfo.rotation; + + var itemStyle = timelineModel.getModel('controlStyle').getItemStyle(); + var hoverStyle = timelineModel.getModel('emphasis.controlStyle').getItemStyle(); + var rect = [0, -controlSize / 2, controlSize, controlSize]; + var playState = timelineModel.getPlayState(); + var inverse = timelineModel.get('inverse', true); + + makeBtn( + layoutInfo.nextBtnPosition, + 'controlStyle.nextIcon', + bind$6(this._changeTimeline, this, inverse ? '-' : '+') + ); + makeBtn( + layoutInfo.prevBtnPosition, + 'controlStyle.prevIcon', + bind$6(this._changeTimeline, this, inverse ? '+' : '-') + ); + makeBtn( + layoutInfo.playPosition, + 'controlStyle.' + (playState ? 'stopIcon' : 'playIcon'), + bind$6(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); + 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$6(me._handlePointerDrag, me); + pointer.ondragend = bind$6(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 = 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$6(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 applyTransform$1(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$4(model, api) { + return getLayoutRect( + model.getBoxLayoutParams(), + { + width: api.getWidth(), + height: api.getHeight() + }, + model.get('padding') + ); +} + +function makeIcon(timelineModel, objPath, rect, opts) { + var icon = makePath( + timelineModel.get(objPath).replace(/^path:\/\//, ''), + clone(opts || {}), + new BoundingRect(rect[0], rect[1], rect[2], rect[3]), + 'center' + ); + + return icon; +} + +/** + * Create symbol or update symbol + * opt: basic position and event handlers + */ +function giveSymbol(hostModel, itemStyleModel, group, opt, symbol, callback) { + var color = itemStyleModel.get('color'); + + if (!symbol) { + var symbolType = hostModel.get('symbol'); + symbol = createSymbol(symbolType, -1, -1, 2, 2, color); + symbol.setStyle('strokeNoScale', true); + group.add(symbol); + callback && callback.onCreate(symbol); + } + else { + symbol.setColor(color); + group.add(symbol); // Group may be new, also need to add. + callback && callback.onUpdate(symbol); + } + + // Style + var itemStyle = itemStyleModel.getItemStyle(['color', 'symbol', 'symbolSize']); + symbol.setStyle(itemStyle); + + // Transform and events. + opt = merge({ + rectHover: true, + z2: 100 + }, opt, true); + + var symbolSize = hostModel.get('symbolSize'); + symbolSize = symbolSize instanceof Array + ? symbolSize.slice() + : [+symbolSize, +symbolSize]; + symbolSize[0] /= 2; + symbolSize[1] /= 2; + opt.scale = symbolSize; + + var symbolOffset = hostModel.get('symbolOffset'); + if (symbolOffset) { + var pos = opt.position = opt.position || [0, 0]; + pos[0] += parsePercent$1(symbolOffset[0], symbolSize[0]); + pos[1] += parsePercent$1(symbolOffset[1], symbolSize[1]); + } + + var symbolRotate = hostModel.get('symbolRotate'); + opt.rotation = (symbolRotate || 0) * Math.PI / 180 || 0; + + symbol.attr(opt); + + // FIXME + // (1) When symbol.style.strokeNoScale is true and updateTransform is not performed, + // getBoundingRect will return wrong result. + // (This is supposed to be resolved in zrender, but it is a little difficult to + // leverage performance and auto updateTransform) + // (2) All of ancesters of symbol do not scale, so we can just updateTransform symbol. + symbol.updateTransform(); + + 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) + ); + } +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * DataZoom component entry + */ + +registerPreprocessor(preprocessor$3); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var ToolboxModel = extendComponentModel({ + + type: 'toolbox', + + layoutMode: { + type: 'box', + ignoreSize: true + }, + + optionUpdated: function () { + ToolboxModel.superApply(this, 'optionUpdated', arguments); + + each$1(this.option.feature, function (featureOpt, featureName) { + var Feature = get$1(featureName); + Feature && merge(featureOpt, Feature.defaultOption); + }); + }, + + defaultOption: { + + show: true, + + z: 6, + + zlevel: 0, + + orient: 'horizontal', + + left: 'right', + + top: 'top', + + // right + // bottom + + backgroundColor: 'transparent', + + borderColor: '#ccc', + + borderRadius: 0, + + borderWidth: 0, + + padding: 5, + + itemSize: 15, + + itemGap: 8, + + showTitle: true, + + iconStyle: { + borderColor: '#666', + color: 'none' + }, + emphasis: { + iconStyle: { + borderColor: '#3E98C5' + } + } + // textStyle: {}, + + // feature + } +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +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 = []; + each$1(featureOpts, function (opt, name) { + featureNames.push(name); + }); + + (new DataDiffer(this._featureNames || [], featureNames)) + .add(processFeature) + .update(processFeature) + .remove(curry(processFeature, null)) + .execute(); + + // Keep for diff. + this._featureNames = featureNames; + + function processFeature(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 = get$1(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'); + var iconStyleEmphasisModel = featureModel.getModel('emphasis.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 = {}; + each$1(icons, function (iconStr, iconName) { + var path = createIcon( + iconStr, + {}, + { + x: -itemSize / 2, + y: -itemSize / 2, + width: itemSize, + height: itemSize + } + ); + path.setStyle(iconStyleModel.getItemStyle()); + path.hoverStyle = iconStyleEmphasisModel.getItemStyle(); + + setHoverStyle(path); + + if (toolboxModel.get('showTitle')) { + path.__title = titles[iconName]; + path.on('mouseover', function () { + // Should not reuse above hoverStyle, which might be modified. + var hoverStyle = iconStyleEmphasisModel.getItemStyle(); + 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', bind( + feature.onclick, feature, ecModel, api, iconName + )); + + iconPaths[iconName] = path; + }); + } + + layout$3(group, toolboxModel, api); + // Render background after group is layout + // FIXME + group.add(makeBackground(group.getBoundingRect(), 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 = getBoundingRect( + titleText, makeFont(hoverStyle) + ); + 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) { + each$1(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) { + each$1(this._features, function (feature) { + feature.remove && feature.remove(ecModel, api); + }); + this.group.removeAll(); + }, + + dispose: function (ecModel, api) { + each$1(this._features, function (feature) { + feature.dispose && feature.dispose(ecModel, api); + }); + } +}); + +function isUserFeatureName(featureName) { + return featureName.indexOf('my') === 0; +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var saveAsImageLang = lang.toolbox.saveAsImage; + +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: saveAsImageLang.title, + type: 'png', + // Default use option.backgroundColor + // backgroundColor: '#fff', + name: '', + excludeComponents: ['toolbox'], + pixelRatio: 1, + lang: saveAsImageLang.lang.slice() +}; + +SaveAsImage.prototype.unusable = !env$1.canvasSupported; + +var proto$4 = SaveAsImage.prototype; + +proto$4.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' && !env$1.browser.ie && !env$1.browser.edge) { + var evt = new MouseEvent('click', { + view: window, + bubbles: true, + cancelable: false + }); + $a.dispatchEvent(evt); + } + // IE + else { + if (window.navigator.msSaveOrOpenBlob) { + var bstr = atob(url.split(',')[1]); + var n = bstr.length; + var u8arr = new Uint8Array(n); + while(n--) { + u8arr[n] = bstr.charCodeAt(n); + } + var blob = new Blob([u8arr]); + window.navigator.msSaveOrOpenBlob(blob, title + '.' + type); + } + else { + var lang$$1 = model.get('lang'); + var html = '' + + '' + + '' + + ''; + var tab = window.open(); + tab.document.write(html); + } + } +}; + +register$1( + 'saveAsImage', SaveAsImage +); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var magicTypeLang = lang.toolbox.magicType; + +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' + }, + // `line`, `bar`, `stack`, `tiled` + title: clone(magicTypeLang.title), + option: {}, + seriesIndex: {} +}; + +var proto$5 = MagicType.prototype; + +proto$5.getIcons = function () { + var model = this.model; + var availableIcons = model.get('icon'); + var icons = {}; + each$1(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 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 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 merge({ + id: seriesId, + stack: '__ec_magicType_stack__' + }, model.get('option.stack') || {}, true); + } + }, + 'tiled': function (seriesType, seriesId, seriesModel, model) { + if (seriesType === 'line' || seriesType === 'bar') { + return merge({ + id: seriesId, + stack: '' + }, model.get('option.tiled') || {}, true); + } + } +}; + +var radioTypes = [ + ['line', 'bar'], + ['stack', 'tiled'] +]; + +proto$5.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? + 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; + } + } + }; + + each$1(radioTypes, function (radio) { + if (indexOf(radio, type) >= 0) { + each$1(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 + }); +}; + +registerAction({ + type: 'changeMagicType', + event: 'magicTypeChanged', + update: 'prepareAndUpdate' +}, function (payload, ecModel) { + ecModel.mergeOption(payload.newOption); +}); + +register$1('magicType', MagicType); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var dataViewLang = lang.toolbox.dataView; + +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 = []; + each$1(series, function (group, key) { + var categoryAxis = group.categoryAxis; + var valueAxis = group.valueAxis; + var valueAxisDim = valueAxis.dim; + + var headers = [' '].concat(map(group.series, function (series) { + return series.name; + })); + var columns = [categoryAxis.model.getCategories()]; + each$1(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 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 {Object} + * @inner + */ +function getContentFromModel(ecModel) { + + var result = groupSeries(ecModel); + + return { + value: 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$1(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 {Object} + */ +function parseTSVContents(tsv) { + var tsvLines = tsv.split(/\n+/g); + var headers = trim$1(tsvLines.shift()).split(itemSplitRegex); + + var categories = []; + var series = map(headers, function (header) { + return { + name: header, + data: [] + }; + }); + for (var i = 0; i < tsvLines.length; i++) { + var items = trim$1(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$1(lines.shift()); + + var data = []; + for (var i = 0; i < lines.length; i++) { + var items = trim$1(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: [] + }; + each$1(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: clone(dataViewLang.title), + lang: clone(dataViewLang.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$$1 = model.get('lang') || []; + header.innerHTML = lang$$1[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:auto;'; + + 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 (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; + } + addEventListener(closeButton, 'click', close); + + 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[1]; + refreshButton.innerHTML = lang$$1[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 + 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 + 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 map(newData, function (newVal, idx) { + var original = originalData && originalData[idx]; + if (isObject$1(original) && !isArray(original)) { + if (isObject$1(newVal) && !isArray(newVal)) { + newVal = newVal.value; + } + // Original data has option + return defaults({ + value: newVal + }, original); + } + else { + return newVal; + } + }); +} + +register$1('dataView', DataView); + +registerAction({ + type: 'changeDataView', + event: 'dataViewChanged', + update: 'prepareAndUpdate' +}, function (payload, ecModel) { + var newSeriesOptList = []; + each$1(payload.newOption.series, function (seriesOpt) { + var seriesModel = ecModel.getSeriesByName(seriesOpt.name)[0]; + if (!seriesModel) { + // New created series + // Geuss the series type + newSeriesOptList.push(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(defaults({ + series: newSeriesOptList + }, payload.newOption)); +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var each$29 = each$1; + +var ATTR$2 = '\0_ec_hist_store'; + +/** + * @param {module:echarts/model/Global} ecModel + * @param {Object} newSnapshot {dataZoomId, batch: [payloadInfo, ...]} + */ +function push(ecModel, newSnapshot) { + var store = giveStore$1(ecModel); + + // If previous dataZoom can not be found, + // complete an range with current range. + each$29(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); +} + +/** + * @param {module:echarts/model/Global} ecModel + * @return {Object} snapshot + */ +function pop(ecModel) { + var store = giveStore$1(ecModel); + var head = store[store.length - 1]; + store.length > 1 && store.pop(); + + // Find top for all dataZoom. + var snapshot = {}; + each$29(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; +} + +/** + * @param {module:echarts/model/Global} ecModel + */ +function clear$1(ecModel) { + ecModel[ATTR$2] = null; +} + +/** + * @param {module:echarts/model/Global} ecModel + * @return {number} records. always >= 1. + */ +function count(ecModel) { + return giveStore$1(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$1(ecModel) { + var store = ecModel[ATTR$2]; + if (!store) { + store = ecModel[ATTR$2] = [{}]; + } + return store; +} + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +DataZoomModel.extend({ + type: 'dataZoom.select' +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +DataZoomView.extend({ + type: 'dataZoom.select' +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/** + * DataZoom component entry + */ + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +// Use dataZoomSelect +var dataZoomLang = lang.toolbox.dataZoom; +var each$28 = each$1; + +// 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', 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' + }, + // `zoom`, `back` + title: clone(dataZoomLang.title) +}; + +var proto$6 = DataZoom.prototype; + +proto$6.render = function (featureModel, ecModel, api, payload) { + this.model = featureModel; + this.ecModel = ecModel; + this.api = api; + + updateZoomBtnStatus(featureModel, ecModel, this, payload, api); + updateBackBtnStatus(featureModel, ecModel); +}; + +proto$6.onclick = function (ecModel, api, type) { + handlers$1[type].call(this); +}; + +proto$6.remove = function (ecModel, api) { + this._brushController.unmount(); +}; + +proto$6.dispose = function (ecModel, api) { + this._brushController.dispose(); +}; + +/** + * @private + */ +var handlers$1 = { + + zoom: function () { + var nextActive = !this._isZoomActive; + + this.api.dispatchAction({ + type: 'takeGlobalCursor', + key: 'dataZoomSelect', + dataZoomSelectActive: nextActive + }); + }, + + back: function () { + this._dispatchZoomAction(pop(this.ecModel)); + } +}; + +/** + * @private + */ +proto$6._onBrush = function (areas, opt) { + if (!opt.isEnd || !areas.length) { + return; + } + var snapshot = {}; + var ecModel = this.ecModel; + + this._brushController.updateCovers([]); // remove cover + + var brushTargetManager = new BrushTargetManager( + retrieveAxisSetting(this.model.option), ecModel, {include: ['grid']} + ); + brushTargetManager.matchOutputRanges(areas, ecModel, function (area, coordRange, coordSys) { + if (coordSys.type !== 'cartesian2d') { + return; + } + + var brushType = area.brushType; + if (brushType === 'rect') { + setBatch('x', coordSys, coordRange[0]); + setBatch('y', coordSys, coordRange[1]); + } + else { + setBatch(({lineX: 'x', lineY: 'y'})[brushType], coordSys, coordRange); + } + }); + + push(ecModel, snapshot); + + this._dispatchZoomAction(snapshot); + + function setBatch(dimName, coordSys, minMax) { + var axis = coordSys.getAxis(dimName); + var axisModel = axis.model; + var dataZoomModel = findDataZoom(dimName, axisModel, ecModel); + + // Restrict range. + var minMaxSpan = dataZoomModel.findRepresentativeAxisProxy(axisModel).getMinMaxSpan(); + if (minMaxSpan.minValueSpan != null || minMaxSpan.maxValueSpan != null) { + minMax = sliderMove( + 0, minMax.slice(), axis.scale.getExtent(), 0, + minMaxSpan.minValueSpan, minMaxSpan.maxValueSpan + ); + } + + dataZoomModel && (snapshot[dataZoomModel.id] = { + dataZoomId: dataZoomModel.id, + startValue: minMax[0], + endValue: minMax[1] + }); + } + + function findDataZoom(dimName, axisModel, ecModel) { + var found; + ecModel.eachComponent({mainType: 'dataZoom', subType: 'select'}, function (dzModel) { + var has = dzModel.getAxisModel(dimName, axisModel.componentIndex); + has && (found = dzModel); + }); + return found; + } +}; + +/** + * @private + */ +proto$6._dispatchZoomAction = function (snapshot) { + var batch = []; + + // Convert from hash map to array. + each$28(snapshot, function (batchItem, dataZoomId) { + batch.push(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. + each$1(['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', + count(ecModel) > 1 ? 'emphasis' : 'normal' + ); +} + +function updateZoomBtnStatus(featureModel, ecModel, view, payload, api) { + 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 brushTargetManager = new BrushTargetManager( + retrieveAxisSetting(featureModel.option), ecModel, {include: ['grid']} + ); + + view._brushController + .setPanels(brushTargetManager.makePanelOpts(api, function (targetInfo) { + return (targetInfo.xAxisDeclared && !targetInfo.yAxisDeclared) + ? 'lineX' + : (!targetInfo.xAxisDeclared && targetInfo.yAxisDeclared) + ? 'lineY' + : 'rect'; + })) + .enableBrush( + zoomActive + ? { + brushType: 'auto', + brushStyle: { + // FIXME user customized? + lineWidth: 0, + fill: 'rgba(0,0,0,0.2)' + } + } + : false + ); +} + + +register$1('dataZoom', DataZoom); + + +// Create special dataZoom option for select +// FIXME consider the case of merge option, where axes options are not exists. +registerPreprocessor(function (option) { + if (!option) { + return; + } + + var dataZoomOpts = option.dataZoom || (option.dataZoom = []); + if (!isArray(dataZoomOpts)) { + option.dataZoom = dataZoomOpts = [dataZoomOpts]; + } + + var toolboxOpt = option.toolbox; + if (toolboxOpt) { + // Assume there is only one toolbox + if (isArray(toolboxOpt)) { + toolboxOpt = toolboxOpt[0]; + } + + if (toolboxOpt && toolboxOpt.feature) { + var dataZoomOpt = toolboxOpt.feature.dataZoom; + // FIXME: If add dataZoom when setOption in merge mode, + // no axis info to be added. See `test/dataZoom-extreme.html` + 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' + && !isArray(givenAxisIndices) + ) { + givenAxisIndices = (givenAxisIndices === false || givenAxisIndices === 'none') ? [] : [givenAxisIndices]; + } + + forEachComponent(axisName, function (axisOpt, axisIndex) { + if (givenAxisIndices != null + && givenAxisIndices != 'all' + && 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 (!isArray(opts)) { + opts = opts ? [opts] : []; + } + each$28(opts, cb); + } +}); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var restoreLang = lang.toolbox.restore; + +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: restoreLang.title +}; + +var proto$7 = Restore.prototype; + +proto$7.onclick = function (ecModel, api, type) { + clear$1(ecModel); + + api.dispatchAction({ + type: 'restore', + from: this.uid + }); +}; + +register$1('restore', Restore); + +registerAction( + {type: 'restore', event: 'restore', update: 'prepareAndUpdate'}, + function (payload, ecModel) { + ecModel.resetOption('recreate'); + } +); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +var urn = 'urn:schemas-microsoft-com:vml'; +var win = typeof window === 'undefined' ? null : window; + +var vmlInited = false; + +var doc = win && win.document; + +function createNode(tagName) { + return doCreateNode(tagName); +} + +// Avoid assign to an exported variable, for transforming to cjs. +var doCreateNode; + +if (doc && !env$1.canvasSupported) { + try { + !doc.namespaces.zrvml && doc.namespaces.add('zrvml', urn); + doCreateNode = function (tagName) { + return doc.createElement(''); + }; + } + catch (e) { + doCreateNode = function (tagName) { + return doc.createElement('<' + tagName + ' xmlns="' + urn + '" class="zrvml">'); + }; + } +} + +// From raphael +function initVML() { + if (vmlInited || !doc) { + 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)'); + } +} + +// http://www.w3.org/TR/NOTE-VML +// TODO Use proxy like svg instead of overwrite brush methods + +var CMD$3 = PathProxy.CMD; +var round$3 = Math.round; +var sqrt = Math.sqrt; +var abs$1 = Math.abs; +var cos = Math.cos; +var sin = Math.sin; +var mathMax$8 = Math.max; + +if (!env$1.canvasSupported) { + + var comma = ','; + var imageTransformPrefix = 'progid:DXImageTransform.Microsoft'; + + var Z = 21600; + var Z2 = Z / 2; + + var ZLEVEL_BASE = 100000; + var Z_BASE$1 = 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$1 + z2; + }; + + var parsePercent$3 = 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 = 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 = 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$$1 = 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$$1[0] * Z; + height /= scale$$1[1] * Z; + var dimension = mathMax$8(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$$1 = stops.length; + // Color and alpha list of first and last stop + var colorAndAlphaList = []; + var colors = []; + for (var i = 0; i < length$$1; i++) { + var stop = stops[i]; + var colorAndAlpha = getColorAndAlpha(stop.color); + colors.push(stop.offset * expansion + shift + ' ' + colorAndAlpha[0]); + if (i === 0 || i === length$$1 - 1) { + colorAndAlphaList.push(colorAndAlpha); + } + } + + if (length$$1 >= 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 = createNode(type); + } + + isFill ? updateFillNode(el, style, zrEl) : updateStrokeNode(el, style); + append(vmlEl, el); + } + else { + vmlEl[isFill ? 'filled' : 'stroked'] = 'false'; + remove(vmlEl, el); + } + }; + + var points$3 = [[], [], []]; + var pathDataToString = function (path, m) { + var M = CMD$3.M; + var C = CMD$3.C; + var L = CMD$3.L; + var A = CMD$3.A; + var Q = CMD$3.Q; + + var str = []; + var nPoint; + var cmdStr; + var cmd; + var i; + var xi; + var yi; + var data = path.data; + var dataLength = path.len(); + for (i = 0; i < dataLength;) { + cmd = data[i++]; + cmdStr = ''; + nPoint = 0; + switch (cmd) { + case M: + cmdStr = ' m '; + nPoint = 1; + xi = data[i++]; + yi = data[i++]; + points$3[0][0] = xi; + points$3[0][1] = yi; + break; + case L: + cmdStr = ' l '; + nPoint = 1; + xi = data[i++]; + yi = data[i++]; + points$3[0][0] = xi; + points$3[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$3[0][0] = x1; + points$3[0][1] = y1; + points$3[1][0] = x2; + points$3[1][1] = y2; + points$3[2][0] = x3; + points$3[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-4) { + // 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-4) { + 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$3(((cx - rx) * sx + x) * Z - Z2), comma, + round$3(((cy - ry) * sy + y) * Z - Z2), comma, + round$3(((cx + rx) * sx + x) * Z - Z2), comma, + round$3(((cy + ry) * sy + y) * Z - Z2), comma, + round$3((x0 * sx + x) * Z - Z2), comma, + round$3((y0 * sy + y) * Z - Z2), comma, + round$3((x1 * sx + x) * Z - Z2), comma, + round$3((y1 * sy + y) * Z - Z2) + ); + + xi = x1; + yi = y1; + break; + case CMD$3.R: + var p0 = points$3[0]; + var p1 = points$3[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$3(p0[0] * Z - Z2); + p1[0] = round$3(p1[0] * Z - Z2); + p0[1] = round$3(p0[1] * Z - Z2); + p1[1] = round$3(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$3.Z: + // FIXME Update xi, yi + str.push(' x '); + } + + if (nPoint > 0) { + str.push(cmdStr); + for (var k = 0; k < nPoint; k++) { + var p = points$3[k]; + + m && applyTransform(p, p, m); + // 不 round 会非常慢 + str.push( + round$3(p[0] * Z - Z2), comma, round$3(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 = 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$1(det)); + } + strokeEl.weight = lineWidth + 'px'; + } + + var path = this.path || (this.path = new PathProxy()); + if (this.__dirtyPath) { + path.beginPath(); + this.buildPath(path, this.shape); + path.toStatic(); + this.__dirtyPath = false; + } + + vmlEl.path = pathDataToString(path, this.transform); + + vmlEl.style.zIndex = getZIndex(this.zlevel, this.z, this.z2); + + // Append to root + append(vmlRoot, vmlEl); + + // Text + if (style.text != null) { + 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 = 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$8(p0[0], p1[0], p2[0], p3[0]); + var maxY = mathMax$8(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$3(x * scaleX + m[4]), comma, + 'Dy=', round$3(y * scaleY + m[5])); + + vmlElStyle.padding = '0 ' + round$3(maxX) + 'px ' + round$3(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$3(x) + 'px'; + vmlElStyle.top = round$3(y) + 'px'; + } + + var imageEl = this._imageEl; + var cropEl = this._cropEl; + + if (!imageEl) { + imageEl = 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$3(scaleX * ow * dw / sw) + 'px'; + imageELStyle.height = round$3(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$3(scaleX * ow * dw / sw) + 'px'; + imageELStyle.height = round$3(scaleY * oh * dh / sh) + 'px'; + } + + if (! cropEl) { + cropEl = doc.createElement('div'); + cropEl.style.overflow = 'hidden'; + this._cropEl = cropEl; + } + var cropElStyle = cropEl.style; + cropElStyle.width = round$3((dw + sx * dw / sw) * scaleX); + cropElStyle.height = round$3((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$3(scaleX * dw) + 'px'; + imageELStyle.height = round$3(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$3(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 != null) { + 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 + $override$1('measureText', function (text, textFont) { + var doc$$1 = doc; + if (!textMeasureEl) { + textMeasureEl = doc$$1.createElement('div'); + textMeasureEl.style.cssText = 'position:absolute;top:-20000px;left:0;' + + 'padding:0;margin:0;border:none;white-space:pre;'; + 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$$1.createTextNode(text)); + return { + width: textMeasureEl.offsetWidth + }; + }); + + var tmpRect$2 = new BoundingRect(); + + var drawRectText = function (vmlRoot, rect, textRect, fromTextEl) { + + var style = this.style; + + // Optimize, avoid normalize every time. + this.__dirty && normalizeTextStyle(style, true); + + var text = style.text; + // Convert to string + text != null && (text += ''); + if (!text) { + return; + } + + // Convert rich text to plain text. Rich text is not supported in + // IE8-, but tags in rich text template will be removed. + if (style.rich) { + var contentBlock = parseRichText(text, style); + text = []; + for (var i = 0; i < contentBlock.lines.length; i++) { + var tokens = contentBlock.lines[i].tokens; + var textLine = []; + for (var j = 0; j < tokens.length; j++) { + textLine.push(tokens[j].text); + } + text.push(textLine.join('')); + } + text = text.join('\n'); + } + + var x; + var y; + var align = style.textAlign; + var verticalAlign = style.textVerticalAlign; + + var fontStyle = getFontStyle(style.font); + // FIXME encodeHtmlAttribute ? + var font = fontStyle.style + ' ' + fontStyle.variant + ' ' + fontStyle.weight + ' ' + + fontStyle.size + 'px "' + fontStyle.family + '"'; + + textRect = textRect || getBoundingRect(text, font, align, verticalAlign); + + // Transform rect to view space + var m = this.transform; + // Ignore transform for text in other element + if (m && !fromTextEl) { + tmpRect$2.copy(rect); + tmpRect$2.applyTransform(m); + rect = tmpRect$2; + } + + if (!fromTextEl) { + var textPosition = style.textPosition; + var distance$$1 = style.textDistance; + // Text position represented by coord + if (textPosition instanceof Array) { + x = rect.x + parsePercent$3(textPosition[0], rect.width); + y = rect.y + parsePercent$3(textPosition[1], rect.height); + + align = align || 'left'; + } + else { + var res = adjustTextPositionOnRect( + textPosition, rect, distance$$1 + ); + x = res.x; + y = res.y; + + // Default align and baseline when has textPosition + align = align || res.textAlign; + verticalAlign = verticalAlign || res.textVerticalAlign; + } + } + else { + x = rect.x; + y = rect.y; + } + + x = adjustTextX(x, textRect.width, align); + y = adjustTextY(y, textRect.height, verticalAlign); + + // Force baseline 'middle' + y += textRect.height / 2; + + // 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$$1 = createNode; + + var textVmlEl = this._textVmlEl; + var pathEl; + var textPathEl; + var skewEl; + if (!textVmlEl) { + textVmlEl = createNode$$1('line'); + pathEl = createNode$$1('path'); + textPathEl = createNode$$1('textpath'); + skewEl = createNode$$1('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$3(coords[0]) || 0) + ',' + (round$3(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$3(x) + 'px'; + textVmlElStyle.top = round$3(y) + 'px'; + } + + textPathEl.string = encodeHtmlAttribute(text); + // TODO + try { + textPathEl.style.font = font; + } + // Error font format + catch (e) {} + + updateFillAndStroke(textVmlEl, 'fill', { + fill: style.textFill, + opacity: style.opacity + }, this); + updateFillAndStroke(textVmlEl, 'stroke', { + 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$3 = 0; i$3 < list.length; i$3++) { + var proto$8 = list[i$3].prototype; + proto$8.drawRectText = drawRectText; + proto$8.removeRectText = removeRectText; + proto$8.appendRectText = appendRectText; + } + + Text.prototype.brushVML = function (vmlRoot) { + var style = this.style; + if (style.text != null) { + 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); + }; +} + +/** + * VML Painter. + * + * @module zrender/vml/Painter + */ + +function parseInt10$1(val) { + return parseInt(val, 10); +} + +/** + * @alias module:zrender/vml/Painter + */ +function VMLPainter(root, storage) { + + 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 oldDelFromStorage = storage.delFromStorage; + var oldAddToStorage = storage.addToStorage; + storage.delFromStorage = function (el) { + oldDelFromStorage.call(storage, el); + + if (el) { + el.onRemove && el.onRemove(vmlRoot); + } + }; + + storage.addToStorage = function (el) { + // Displayable already has a vml node + el.onAdd && el.onAdd(vmlRoot); + + oldAddToStorage.call(storage, el); + }; + + this._firstPaint = true; +} + +VMLPainter.prototype = { + + constructor: VMLPainter, + + getType: function () { + return 'vml'; + }, + + /** + * @return {HTMLDivElement} + */ + getViewportRoot: function () { + return this._vmlViewport; + }, + + getViewportRootOffset: function () { + var viewportRoot = this.getViewportRoot(); + if (viewportRoot) { + return { + offsetLeft: viewportRoot.offsetLeft || 0, + offsetTop: viewportRoot.offsetTop || 0 + }; + } + }, + + /** + * 刷新 + */ + 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 (width, height) { + var width = width == null ? this._getWidth() : width; + var height = height == null ? this._getHeight() : height; + + 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 () { + if (this._vmlViewport) { + this.root.removeChild(this._vmlViewport); + } + }, + + _getWidth: function () { + var root = this.root; + var stl = root.currentStyle; + + return ((root.clientWidth || parseInt10$1(stl.width)) + - parseInt10$1(stl.paddingLeft) + - parseInt10$1(stl.paddingRight)) | 0; + }, + + _getHeight: function () { + var root = this.root; + var stl = root.currentStyle; + + return ((root.clientHeight || parseInt10$1(stl.height)) + - parseInt10$1(stl.paddingTop) + - parseInt10$1(stl.paddingBottom)) | 0; + } +}; + +// Not supported methods +function createMethodNotSupport(method) { + return function () { + zrLog('In IE8.0 VML mode painter not support method "' + method + '"'); + }; +} + +// Unsupported methods +each$1([ + 'getLayer', 'insertLayer', 'eachLayer', 'eachBuiltinLayer', 'eachOtherLayer', 'getLayers', + 'modLayer', 'delLayer', 'clearLayer', 'toDataURL', 'pathToImage' +], function (name) { + VMLPainter.prototype[name] = createMethodNotSupport(name); +}); + +registerPainter('vml', VMLPainter); + +var svgURI = 'http://www.w3.org/2000/svg'; + +function createElement(name) { + return document.createElementNS(svgURI, name); +} + +// TODO +// 1. shadow +// 2. Image: sx, sy, sw, sh + +var CMD$4 = PathProxy.CMD; +var arrayJoin = Array.prototype.join; + +var NONE = 'none'; +var mathRound = Math.round; +var mathSin$3 = Math.sin; +var mathCos$3 = Math.cos; +var PI$5 = Math.PI; +var PI2$7 = Math.PI * 2; +var degree = 180 / PI$5; + +var EPSILON$4 = 1e-4; + +function round4(val) { + return mathRound(val * 1e4) / 1e4; +} + +function isAroundZero$1(val) { + return val < EPSILON$4 && val > -EPSILON$4; +} + +function pathHasFill(style, isText) { + var fill = isText ? style.textFill : style.fill; + return fill != null && fill !== NONE; +} + +function pathHasStroke(style, isText) { + var stroke = isText ? style.textStroke : style.stroke; + return stroke != null && stroke !== NONE; +} + +function setTransform(svgEl, m) { + if (m) { + attr(svgEl, 'transform', 'matrix(' + arrayJoin.call(m, ',') + ')'); + } +} + +function attr(el, key, val) { + if (!val || val.type !== 'linear' && val.type !== 'radial') { + // Don't set attribute for gradient, since it need new dom nodes + if (typeof val === 'string' && val.indexOf('NaN') > -1) { + console.log(val); + } + el.setAttribute(key, val); + } +} + +function attrXLink(el, key, val) { + el.setAttributeNS('http://www.w3.org/1999/xlink', key, val); +} + +function bindStyle(svgEl, style, isText) { + if (pathHasFill(style, isText)) { + var fill = isText ? style.textFill : style.fill; + fill = fill === 'transparent' ? NONE : fill; + + /** + * FIXME: + * This is a temporary fix for Chrome's clipping bug + * that happens when a clip-path is referring another one. + * This fix should be used before Chrome's bug is fixed. + * For an element that has clip-path, and fill is none, + * set it to be "rgba(0, 0, 0, 0.002)" will hide the element. + * Otherwise, it will show black fill color. + * 0.002 is used because this won't work for alpha values smaller + * than 0.002. + * + * See + * https://bugs.chromium.org/p/chromium/issues/detail?id=659790 + * for more information. + */ + if (svgEl.getAttribute('clip-path') !== 'none' && fill === NONE) { + fill = 'rgba(0, 0, 0, 0.002)'; + } + + attr(svgEl, 'fill', fill); + attr(svgEl, 'fill-opacity', style.opacity); + } + else { + attr(svgEl, 'fill', NONE); + } + + if (pathHasStroke(style, isText)) { + var stroke = isText ? style.textStroke : style.stroke; + stroke = stroke === 'transparent' ? NONE : stroke; + attr(svgEl, 'stroke', stroke); + var strokeWidth = isText + ? style.textStrokeWidth + : style.lineWidth; + var strokeScale = !isText && style.strokeNoScale + ? style.host.getLineScale() + : 1; + attr(svgEl, 'stroke-width', strokeWidth / strokeScale); + // stroke then fill for text; fill then stroke for others + attr(svgEl, 'paint-order', isText ? 'stroke' : 'fill'); + attr(svgEl, 'stroke-opacity', style.opacity); + var lineDash = style.lineDash; + if (lineDash) { + attr(svgEl, 'stroke-dasharray', style.lineDash.join(',')); + attr(svgEl, 'stroke-dashoffset', mathRound(style.lineDashOffset || 0)); + } + else { + attr(svgEl, 'stroke-dasharray', ''); + } + + // PENDING + style.lineCap && attr(svgEl, 'stroke-linecap', style.lineCap); + style.lineJoin && attr(svgEl, 'stroke-linejoin', style.lineJoin); + style.miterLimit && attr(svgEl, 'stroke-miterlimit', style.miterLimit); + } + else { + attr(svgEl, 'stroke', NONE); + } +} + +/*************************************************** + * PATH + **************************************************/ +function pathDataToString$1(path) { + var str = []; + var data = path.data; + var dataLength = path.len(); + for (var i = 0; i < dataLength;) { + var cmd = data[i++]; + var cmdStr = ''; + var nData = 0; + switch (cmd) { + case CMD$4.M: + cmdStr = 'M'; + nData = 2; + break; + case CMD$4.L: + cmdStr = 'L'; + nData = 2; + break; + case CMD$4.Q: + cmdStr = 'Q'; + nData = 4; + break; + case CMD$4.C: + cmdStr = 'C'; + nData = 6; + break; + case CMD$4.A: + var cx = data[i++]; + var cy = data[i++]; + var rx = data[i++]; + var ry = data[i++]; + var theta = data[i++]; + var dTheta = data[i++]; + var psi = data[i++]; + var clockwise = data[i++]; + + var dThetaPositive = Math.abs(dTheta); + var isCircle = isAroundZero$1(dThetaPositive - PI2$7) + && !isAroundZero$1(dThetaPositive); + + var large = false; + if (dThetaPositive >= PI2$7) { + large = true; + } + else if (isAroundZero$1(dThetaPositive)) { + large = false; + } + else { + large = (dTheta > -PI$5 && dTheta < 0 || dTheta > PI$5) + === !!clockwise; + } + + var x0 = round4(cx + rx * mathCos$3(theta)); + var y0 = round4(cy + ry * mathSin$3(theta)); + + // It will not draw if start point and end point are exactly the same + // We need to shift the end point with a small value + // FIXME A better way to draw circle ? + if (isCircle) { + if (clockwise) { + dTheta = PI2$7 - 1e-4; + } + else { + dTheta = -PI2$7 + 1e-4; + } + + large = true; + + if (i === 9) { + // Move to (x0, y0) only when CMD.A comes at the + // first position of a shape. + // For instance, when drawing a ring, CMD.A comes + // after CMD.M, so it's unnecessary to move to + // (x0, y0). + str.push('M', x0, y0); + } + } + + var x = round4(cx + rx * mathCos$3(theta + dTheta)); + var y = round4(cy + ry * mathSin$3(theta + dTheta)); + + // FIXME Ellipse + str.push('A', round4(rx), round4(ry), + mathRound(psi * degree), +large, +clockwise, x, y); + break; + case CMD$4.Z: + cmdStr = 'Z'; + break; + case CMD$4.R: + var x = round4(data[i++]); + var y = round4(data[i++]); + var w = round4(data[i++]); + var h = round4(data[i++]); + str.push( + 'M', x, y, + 'L', x + w, y, + 'L', x + w, y + h, + 'L', x, y + h, + 'L', x, y + ); + break; + } + cmdStr && str.push(cmdStr); + for (var j = 0; j < nData; j++) { + // PENDING With scale + str.push(round4(data[i++])); + } + } + return str.join(' '); +} + +var svgPath = {}; +svgPath.brush = function (el) { + var style = el.style; + + var svgEl = el.__svgEl; + if (!svgEl) { + svgEl = createElement('path'); + el.__svgEl = svgEl; + } + + if (!el.path) { + el.createPathProxy(); + } + var path = el.path; + + if (el.__dirtyPath) { + path.beginPath(); + el.buildPath(path, el.shape); + el.__dirtyPath = false; + + var pathStr = pathDataToString$1(path); + if (pathStr.indexOf('NaN') < 0) { + // Ignore illegal path, which may happen such in out-of-range + // data in Calendar series. + attr(svgEl, 'd', pathStr); + } + } + + bindStyle(svgEl, style); + setTransform(svgEl, el.transform); + + if (style.text != null) { + svgTextDrawRectText(el, el.getBoundingRect()); + } +}; + +/*************************************************** + * IMAGE + **************************************************/ +var svgImage = {}; +svgImage.brush = function (el) { + var style = el.style; + var image = style.image; + + if (image instanceof HTMLImageElement) { + var src = image.src; + image = src; + } + if (! image) { + return; + } + + var x = style.x || 0; + var y = style.y || 0; + + var dw = style.width; + var dh = style.height; + + var svgEl = el.__svgEl; + if (! svgEl) { + svgEl = createElement('image'); + el.__svgEl = svgEl; + } + + if (image !== el.__imageSrc) { + attrXLink(svgEl, 'href', image); + // Caching image src + el.__imageSrc = image; + } + + attr(svgEl, 'width', dw); + attr(svgEl, 'height', dh); + + attr(svgEl, 'x', x); + attr(svgEl, 'y', y); + + setTransform(svgEl, el.transform); + + if (style.text != null) { + svgTextDrawRectText(el, el.getBoundingRect()); + } +}; + +/*************************************************** + * TEXT + **************************************************/ +var svgText = {}; +var tmpRect$3 = new BoundingRect(); + +var svgTextDrawRectText = function (el, rect, textRect) { + var style = el.style; + + el.__dirty && normalizeTextStyle(style, true); + + var text = style.text; + // Convert to string + if (text == null) { + // Draw no text only when text is set to null, but not '' + return; + } + else { + text += ''; + } + + var textSvgEl = el.__textSvgEl; + if (! textSvgEl) { + textSvgEl = createElement('text'); + el.__textSvgEl = textSvgEl; + } + + var x; + var y; + var textPosition = style.textPosition; + var distance = style.textDistance; + var align = style.textAlign || 'left'; + + if (typeof style.fontSize === 'number') { + style.fontSize += 'px'; + } + var font = style.font + || [ + style.fontStyle || '', + style.fontWeight || '', + style.fontSize || '', + style.fontFamily || '' + ].join(' ') + || DEFAULT_FONT; + + var verticalAlign = getVerticalAlignForSvg(style.textVerticalAlign); + + textRect = getBoundingRect(text, font, align, + verticalAlign); + + var lineHeight = textRect.lineHeight; + // Text position represented by coord + if (textPosition instanceof Array) { + x = rect.x + textPosition[0]; + y = rect.y + textPosition[1]; + } + else { + var newPos = adjustTextPositionOnRect( + textPosition, rect, distance + ); + x = newPos.x; + y = newPos.y; + verticalAlign = getVerticalAlignForSvg(newPos.textVerticalAlign); + align = newPos.textAlign; + } + + attr(textSvgEl, 'alignment-baseline', verticalAlign); + + if (font) { + textSvgEl.style.font = font; + } + + var textPadding = style.textPadding; + + // Make baseline top + attr(textSvgEl, 'x', x); + attr(textSvgEl, 'y', y); + + bindStyle(textSvgEl, style, true); + if (el instanceof Text || el.style.transformText) { + // Transform text with element + setTransform(textSvgEl, el.transform); + } + else { + if (el.transform) { + tmpRect$3.copy(rect); + tmpRect$3.applyTransform(el.transform); + rect = tmpRect$3; + } + else { + var pos = el.transformCoordToGlobal(rect.x, rect.y); + rect.x = pos[0]; + rect.y = pos[1]; + } + + // Text rotation, but no element transform + var origin = style.textOrigin; + if (origin === 'center') { + x = textRect.width / 2 + x; + y = textRect.height / 2 + y; + } + else if (origin) { + x = origin[0] + x; + y = origin[1] + y; + } + var rotate$$1 = -style.textRotation || 0; + var transform = create$1(); + // Apply textRotate to element matrix + rotate(transform, el.transform, rotate$$1); + setTransform(textSvgEl, transform); + } + + var textLines = text.split('\n'); + var nTextLines = textLines.length; + var textAnchor = align; + // PENDING + if (textAnchor === 'left') { + textAnchor = 'start'; + textPadding && (x += textPadding[3]); + } + else if (textAnchor === 'right') { + textAnchor = 'end'; + textPadding && (x -= textPadding[1]); + } + else if (textAnchor === 'center') { + textAnchor = 'middle'; + textPadding && (x += (textPadding[3] - textPadding[1]) / 2); + } + + var dy = 0; + if (verticalAlign === 'baseline') { + dy = -textRect.height + lineHeight; + textPadding && (dy -= textPadding[2]); + } + else if (verticalAlign === 'middle') { + dy = (-textRect.height + lineHeight) / 2; + textPadding && (y += (textPadding[0] - textPadding[2]) / 2); + } + else { + textPadding && (dy += textPadding[0]); + } + + // Font may affect position of each tspan elements + if (el.__text !== text || el.__textFont !== font) { + var tspanList = el.__tspanList || []; + el.__tspanList = tspanList; + for (var i = 0; i < nTextLines; i++) { + // Using cached tspan elements + var tspan = tspanList[i]; + if (! tspan) { + tspan = tspanList[i] = createElement('tspan'); + textSvgEl.appendChild(tspan); + attr(tspan, 'alignment-baseline', verticalAlign); + attr(tspan, 'text-anchor', textAnchor); + } + else { + tspan.innerHTML = ''; + } + attr(tspan, 'x', x); + attr(tspan, 'y', y + i * lineHeight + dy); + tspan.appendChild(document.createTextNode(textLines[i])); + } + // Remove unsed tspan elements + for (; i < tspanList.length; i++) { + textSvgEl.removeChild(tspanList[i]); + } + tspanList.length = nTextLines; + + el.__text = text; + el.__textFont = font; + } + else if (el.__tspanList.length) { + // Update span x and y + var len = el.__tspanList.length; + for (var i = 0; i < len; ++i) { + var tspan = el.__tspanList[i]; + if (tspan) { + attr(tspan, 'x', x); + attr(tspan, 'y', y + i * lineHeight + dy); + } + } + } +}; + +function getVerticalAlignForSvg(verticalAlign) { + if (verticalAlign === 'middle') { + return 'middle'; + } + else if (verticalAlign === 'bottom') { + return 'baseline'; + } + else { + return 'hanging'; + } +} + +svgText.drawRectText = svgTextDrawRectText; + +svgText.brush = function (el) { + var style = el.style; + if (style.text != null) { + // 强制设置 textPosition + style.textPosition = [0, 0]; + svgTextDrawRectText(el, { + x: style.x || 0, y: style.y || 0, + width: 0, height: 0 + }, el.getBoundingRect()); + } +}; + +// Myers' Diff Algorithm +// Modified from https://github.com/kpdecker/jsdiff/blob/master/src/diff/base.js + +function Diff() {} + +Diff.prototype = { + diff: function (oldArr, newArr, equals) { + if (!equals) { + equals = function (a, b) { + return a === b; + }; + } + this.equals = equals; + + var self = this; + + oldArr = oldArr.slice(); + newArr = newArr.slice(); + // Allow subclasses to massage the input prior to running + var newLen = newArr.length; + var oldLen = oldArr.length; + var editLength = 1; + var maxEditLength = newLen + oldLen; + var bestPath = [{ newPos: -1, components: [] }]; + + // Seed editLength = 0, i.e. the content starts with the same values + var oldPos = this.extractCommon(bestPath[0], newArr, oldArr, 0); + if (bestPath[0].newPos + 1 >= newLen && oldPos + 1 >= oldLen) { + var indices = []; + for (var i = 0; i < newArr.length; i++) { + indices.push(i); + } + // Identity per the equality and tokenizer + return [{ + indices: indices, count: newArr.length + }]; + } + + // Main worker method. checks all permutations of a given edit length for acceptance. + function execEditLength() { + for (var diagonalPath = -1 * editLength; diagonalPath <= editLength; diagonalPath += 2) { + var basePath; + var addPath = bestPath[diagonalPath - 1]; + var removePath = bestPath[diagonalPath + 1]; + var oldPos = (removePath ? removePath.newPos : 0) - diagonalPath; + if (addPath) { + // No one else is going to attempt to use this value, clear it + bestPath[diagonalPath - 1] = undefined; + } + + var canAdd = addPath && addPath.newPos + 1 < newLen; + var canRemove = removePath && 0 <= oldPos && oldPos < oldLen; + if (!canAdd && !canRemove) { + // If this path is a terminal then prune + bestPath[diagonalPath] = undefined; + continue; + } + + // Select the diagonal that we want to branch from. We select the prior + // path whose position in the new string is the farthest from the origin + // and does not pass the bounds of the diff graph + if (!canAdd || (canRemove && addPath.newPos < removePath.newPos)) { + basePath = clonePath(removePath); + self.pushComponent(basePath.components, undefined, true); + } + else { + basePath = addPath; // No need to clone, we've pulled it from the list + basePath.newPos++; + self.pushComponent(basePath.components, true, undefined); + } + + oldPos = self.extractCommon(basePath, newArr, oldArr, diagonalPath); + + // If we have hit the end of both strings, then we are done + if (basePath.newPos + 1 >= newLen && oldPos + 1 >= oldLen) { + return buildValues(self, basePath.components, newArr, oldArr); + } + else { + // Otherwise track this path as a potential candidate and continue. + bestPath[diagonalPath] = basePath; + } + } + + editLength++; + } + + while (editLength <= maxEditLength) { + var ret = execEditLength(); + if (ret) { + return ret; + } + } + }, + + pushComponent: function (components, added, removed) { + var last = components[components.length - 1]; + if (last && last.added === added && last.removed === removed) { + // We need to clone here as the component clone operation is just + // as shallow array clone + components[components.length - 1] = {count: last.count + 1, added: added, removed: removed }; + } + else { + components.push({count: 1, added: added, removed: removed }); + } + }, + extractCommon: function (basePath, newArr, oldArr, diagonalPath) { + var newLen = newArr.length; + var oldLen = oldArr.length; + var newPos = basePath.newPos; + var oldPos = newPos - diagonalPath; + var commonCount = 0; + + while (newPos + 1 < newLen && oldPos + 1 < oldLen && this.equals(newArr[newPos + 1], oldArr[oldPos + 1])) { + newPos++; + oldPos++; + commonCount++; + } + + if (commonCount) { + basePath.components.push({count: commonCount}); + } + + basePath.newPos = newPos; + return oldPos; + }, + tokenize: function (value) { + return value.slice(); + }, + join: function (value) { + return value.slice(); + } +}; + +function buildValues(diff, components, newArr, oldArr) { + var componentPos = 0; + var componentLen = components.length; + var newPos = 0; + var oldPos = 0; + + for (; componentPos < componentLen; componentPos++) { + var component = components[componentPos]; + if (!component.removed) { + var indices = []; + for (var i = newPos; i < newPos + component.count; i++) { + indices.push(i); + } + component.indices = indices; + newPos += component.count; + // Common case + if (!component.added) { + oldPos += component.count; + } + } + else { + var indices = []; + for (var i = oldPos; i < oldPos + component.count; i++) { + indices.push(i); + } + component.indices = indices; + oldPos += component.count; + } + } + + return components; +} + +function clonePath(path) { + return { newPos: path.newPos, components: path.components.slice(0) }; +} + +var arrayDiff = new Diff(); + +var arrayDiff$1 = function (oldArr, newArr, callback) { + return arrayDiff.diff(oldArr, newArr, callback); +}; + +/** + * @file Manages elements that can be defined in in SVG, + * e.g., gradients, clip path, etc. + * @author Zhang Wenli + */ + +var MARK_UNUSED = '0'; +var MARK_USED = '1'; + +/** + * Manages elements that can be defined in in SVG, + * e.g., gradients, clip path, etc. + * + * @class + * @param {number} zrId zrender instance id + * @param {SVGElement} svgRoot root of SVG document + * @param {string|string[]} tagNames possible tag names + * @param {string} markLabel label name to make if the element + * is used + */ +function Definable( + zrId, + svgRoot, + tagNames, + markLabel, + domName +) { + this._zrId = zrId; + this._svgRoot = svgRoot; + this._tagNames = typeof tagNames === 'string' ? [tagNames] : tagNames; + this._markLabel = markLabel; + this._domName = domName || '_dom'; + + this.nextId = 0; +} + + +Definable.prototype.createElement = createElement; + + +/** + * Get the tag for svgRoot; optionally creates one if not exists. + * + * @param {boolean} isForceCreating if need to create when not exists + * @return {SVGDefsElement} SVG element, null if it doesn't + * exist and isForceCreating is false + */ +Definable.prototype.getDefs = function (isForceCreating) { + var svgRoot = this._svgRoot; + var defs = this._svgRoot.getElementsByTagName('defs'); + if (defs.length === 0) { + // Not exist + if (isForceCreating) { + defs = svgRoot.insertBefore( + this.createElement('defs'), // Create new tag + svgRoot.firstChild // Insert in the front of svg + ); + if (!defs.contains) { + // IE doesn't support contains method + defs.contains = function (el) { + var children = defs.children; + if (!children) { + return false; + } + for (var i = children.length - 1; i >= 0; --i) { + if (children[i] === el) { + return true; + } + } + return false; + }; + } + return defs; + } + else { + return null; + } + } + else { + return defs[0]; + } +}; + + +/** + * Update DOM element if necessary. + * + * @param {Object|string} element style element. e.g., for gradient, + * it may be '#ccc' or {type: 'linear', ...} + * @param {Function|undefined} onUpdate update callback + */ +Definable.prototype.update = function (element, onUpdate) { + if (!element) { + return; + } + + var defs = this.getDefs(false); + if (element[this._domName] && defs.contains(element[this._domName])) { + // Update DOM + if (typeof onUpdate === 'function') { + onUpdate(element); + } + } + else { + // No previous dom, create new + var dom = this.add(element); + if (dom) { + element[this._domName] = dom; + } + } +}; + + +/** + * Add gradient dom to defs + * + * @param {SVGElement} dom DOM to be added to + */ +Definable.prototype.addDom = function (dom) { + var defs = this.getDefs(true); + defs.appendChild(dom); +}; + + +/** + * Remove DOM of a given element. + * + * @param {SVGElement} element element to remove dom + */ +Definable.prototype.removeDom = function (element) { + var defs = this.getDefs(false); + if (defs && element[this._domName]) { + defs.removeChild(element[this._domName]); + element[this._domName] = null; + } +}; + + +/** + * Get DOMs of this element. + * + * @return {HTMLDomElement} doms of this defineable elements in + */ +Definable.prototype.getDoms = function () { + var defs = this.getDefs(false); + if (!defs) { + // No dom when defs is not defined + return []; + } + + var doms = []; + each$1(this._tagNames, function (tagName) { + var tags = defs.getElementsByTagName(tagName); + // Note that tags is HTMLCollection, which is array-like + // rather than real array. + // So `doms.concat(tags)` add tags as one object. + doms = doms.concat([].slice.call(tags)); + }); + + return doms; +}; + + +/** + * Mark DOMs to be unused before painting, and clear unused ones at the end + * of the painting. + */ +Definable.prototype.markAllUnused = function () { + var doms = this.getDoms(); + var that = this; + each$1(doms, function (dom) { + dom[that._markLabel] = MARK_UNUSED; + }); +}; + + +/** + * Mark a single DOM to be used. + * + * @param {SVGElement} dom DOM to mark + */ +Definable.prototype.markUsed = function (dom) { + if (dom) { + dom[this._markLabel] = MARK_USED; + } +}; + + +/** + * Remove unused DOMs defined in + */ +Definable.prototype.removeUnused = function () { + var defs = this.getDefs(false); + if (!defs) { + // Nothing to remove + return; + } + + var doms = this.getDoms(); + var that = this; + each$1(doms, function (dom) { + if (dom[that._markLabel] !== MARK_USED) { + // Remove gradient + defs.removeChild(dom); + } + }); +}; + + +/** + * Get SVG proxy. + * + * @param {Displayable} displayable displayable element + * @return {Path|Image|Text} svg proxy of given element + */ +Definable.prototype.getSvgProxy = function (displayable) { + if (displayable instanceof Path) { + return svgPath; + } + else if (displayable instanceof ZImage) { + return svgImage; + } + else if (displayable instanceof Text) { + return svgText; + } + else { + return svgPath; + } +}; + + +/** + * Get text SVG element. + * + * @param {Displayable} displayable displayable element + * @return {SVGElement} SVG element of text + */ +Definable.prototype.getTextSvgElement = function (displayable) { + return displayable.__textSvgEl; +}; + + +/** + * Get SVG element. + * + * @param {Displayable} displayable displayable element + * @return {SVGElement} SVG element + */ +Definable.prototype.getSvgElement = function (displayable) { + return displayable.__svgEl; +}; + +/** + * @file Manages SVG gradient elements. + * @author Zhang Wenli + */ + +/** + * Manages SVG gradient elements. + * + * @class + * @extends Definable + * @param {number} zrId zrender instance id + * @param {SVGElement} svgRoot root of SVG document + */ +function GradientManager(zrId, svgRoot) { + Definable.call( + this, + zrId, + svgRoot, + ['linearGradient', 'radialGradient'], + '__gradient_in_use__' + ); +} + + +inherits(GradientManager, Definable); + + +/** + * Create new gradient DOM for fill or stroke if not exist, + * but will not update gradient if exists. + * + * @param {SvgElement} svgElement SVG element to paint + * @param {Displayable} displayable zrender displayable element + */ +GradientManager.prototype.addWithoutUpdate = function ( + svgElement, + displayable +) { + if (displayable && displayable.style) { + var that = this; + each$1(['fill', 'stroke'], function (fillOrStroke) { + if (displayable.style[fillOrStroke] + && (displayable.style[fillOrStroke].type === 'linear' + || displayable.style[fillOrStroke].type === 'radial') + ) { + var gradient = displayable.style[fillOrStroke]; + var defs = that.getDefs(true); + + // Create dom in if not exists + var dom; + if (gradient._dom) { + // Gradient exists + dom = gradient._dom; + if (!defs.contains(gradient._dom)) { + // _dom is no longer in defs, recreate + that.addDom(dom); + } + } + else { + // New dom + dom = that.add(gradient); + } + + that.markUsed(displayable); + + var id = dom.getAttribute('id'); + svgElement.setAttribute(fillOrStroke, 'url(#' + id + ')'); + } + }); + } +}; + + +/** + * Add a new gradient tag in + * + * @param {Gradient} gradient zr gradient instance + * @return {SVGLinearGradientElement | SVGRadialGradientElement} + * created DOM + */ +GradientManager.prototype.add = function (gradient) { + var dom; + if (gradient.type === 'linear') { + dom = this.createElement('linearGradient'); + } + else if (gradient.type === 'radial') { + dom = this.createElement('radialGradient'); + } + else { + zrLog('Illegal gradient type.'); + return null; + } + + // Set dom id with gradient id, since each gradient instance + // will have no more than one dom element. + // id may exists before for those dirty elements, in which case + // id should remain the same, and other attributes should be + // updated. + gradient.id = gradient.id || this.nextId++; + dom.setAttribute('id', 'zr' + this._zrId + + '-gradient-' + gradient.id); + + this.updateDom(gradient, dom); + this.addDom(dom); + + return dom; +}; + + +/** + * Update gradient. + * + * @param {Gradient} gradient zr gradient instance + */ +GradientManager.prototype.update = function (gradient) { + var that = this; + Definable.prototype.update.call(this, gradient, function () { + var type = gradient.type; + var tagName = gradient._dom.tagName; + if (type === 'linear' && tagName === 'linearGradient' + || type === 'radial' && tagName === 'radialGradient' + ) { + // Gradient type is not changed, update gradient + that.updateDom(gradient, gradient._dom); + } + else { + // Remove and re-create if type is changed + that.removeDom(gradient); + that.add(gradient); + } + }); +}; + + +/** + * Update gradient dom + * + * @param {Gradient} gradient zr gradient instance + * @param {SVGLinearGradientElement | SVGRadialGradientElement} dom + * DOM to update + */ +GradientManager.prototype.updateDom = function (gradient, dom) { + if (gradient.type === 'linear') { + dom.setAttribute('x1', gradient.x); + dom.setAttribute('y1', gradient.y); + dom.setAttribute('x2', gradient.x2); + dom.setAttribute('y2', gradient.y2); + } + else if (gradient.type === 'radial') { + dom.setAttribute('cx', gradient.x); + dom.setAttribute('cy', gradient.y); + dom.setAttribute('r', gradient.r); + } + else { + zrLog('Illegal gradient type.'); + return; + } + + if (gradient.global) { + // x1, x2, y1, y2 in range of 0 to canvas width or height + dom.setAttribute('gradientUnits', 'userSpaceOnUse'); + } + else { + // x1, x2, y1, y2 in range of 0 to 1 + dom.setAttribute('gradientUnits', 'objectBoundingBox'); + } + + // Remove color stops if exists + dom.innerHTML = ''; + + // Add color stops + var colors = gradient.colorStops; + for (var i = 0, len = colors.length; i < len; ++i) { + var stop = this.createElement('stop'); + stop.setAttribute('offset', colors[i].offset * 100 + '%'); + stop.setAttribute('stop-color', colors[i].color); + dom.appendChild(stop); + } + + // Store dom element in gradient, to avoid creating multiple + // dom instances for the same gradient element + gradient._dom = dom; +}; + +/** + * Mark a single gradient to be used + * + * @param {Displayable} displayable displayable element + */ +GradientManager.prototype.markUsed = function (displayable) { + if (displayable.style) { + var gradient = displayable.style.fill; + if (gradient && gradient._dom) { + Definable.prototype.markUsed.call(this, gradient._dom); + } + + gradient = displayable.style.stroke; + if (gradient && gradient._dom) { + Definable.prototype.markUsed.call(this, gradient._dom); + } + } +}; + +/** + * @file Manages SVG clipPath elements. + * @author Zhang Wenli + */ + +/** + * Manages SVG clipPath elements. + * + * @class + * @extends Definable + * @param {number} zrId zrender instance id + * @param {SVGElement} svgRoot root of SVG document + */ +function ClippathManager(zrId, svgRoot) { + Definable.call(this, zrId, svgRoot, 'clipPath', '__clippath_in_use__'); +} + + +inherits(ClippathManager, Definable); + + +/** + * Update clipPath. + * + * @param {Displayable} displayable displayable element + */ +ClippathManager.prototype.update = function (displayable) { + var svgEl = this.getSvgElement(displayable); + if (svgEl) { + this.updateDom(svgEl, displayable.__clipPaths, false); + } + + var textEl = this.getTextSvgElement(displayable); + if (textEl) { + // Make another clipPath for text, since it's transform + // matrix is not the same with svgElement + this.updateDom(textEl, displayable.__clipPaths, true); + } + + this.markUsed(displayable); +}; + + +/** + * Create an SVGElement of displayable and create a of its + * clipPath + * + * @param {Displayable} parentEl parent element + * @param {ClipPath[]} clipPaths clipPaths of parent element + * @param {boolean} isText if parent element is Text + */ +ClippathManager.prototype.updateDom = function ( + parentEl, + clipPaths, + isText +) { + if (clipPaths && clipPaths.length > 0) { + // Has clipPath, create with the first clipPath + var defs = this.getDefs(true); + var clipPath = clipPaths[0]; + var clipPathEl; + var id; + + var dom = isText ? '_textDom' : '_dom'; + + if (clipPath[dom]) { + // Use a dom that is already in + id = clipPath[dom].getAttribute('id'); + clipPathEl = clipPath[dom]; + + // Use a dom that is already in + if (!defs.contains(clipPathEl)) { + // This happens when set old clipPath that has + // been previously removed + defs.appendChild(clipPathEl); + } + } + else { + // New + id = 'zr' + this._zrId + '-clip-' + this.nextId; + ++this.nextId; + clipPathEl = this.createElement('clipPath'); + clipPathEl.setAttribute('id', id); + defs.appendChild(clipPathEl); + + clipPath[dom] = clipPathEl; + } + + // Build path and add to + var svgProxy = this.getSvgProxy(clipPath); + if (clipPath.transform + && clipPath.parent.invTransform + && !isText + ) { + /** + * If a clipPath has a parent with transform, the transform + * of parent should not be considered when setting transform + * of clipPath. So we need to transform back from parent's + * transform, which is done by multiplying parent's inverse + * transform. + */ + // Store old transform + var transform = Array.prototype.slice.call( + clipPath.transform + ); + + // Transform back from parent, and brush path + mul$1( + clipPath.transform, + clipPath.parent.invTransform, + clipPath.transform + ); + svgProxy.brush(clipPath); + + // Set back transform of clipPath + clipPath.transform = transform; + } + else { + svgProxy.brush(clipPath); + } + + var pathEl = this.getSvgElement(clipPath); + + clipPathEl.innerHTML = ''; + /** + * Use `cloneNode()` here to appendChild to multiple parents, + * which may happend when Text and other shapes are using the same + * clipPath. Since Text will create an extra clipPath DOM due to + * different transform rules. + */ + clipPathEl.appendChild(pathEl.cloneNode()); + + parentEl.setAttribute('clip-path', 'url(#' + id + ')'); + + if (clipPaths.length > 1) { + // Make the other clipPaths recursively + this.updateDom(clipPathEl, clipPaths.slice(1), isText); + } + } + else { + // No clipPath + if (parentEl) { + parentEl.setAttribute('clip-path', 'none'); + } + } +}; + +/** + * Mark a single clipPath to be used + * + * @param {Displayable} displayable displayable element + */ +ClippathManager.prototype.markUsed = function (displayable) { + var that = this; + if (displayable.__clipPaths && displayable.__clipPaths.length > 0) { + each$1(displayable.__clipPaths, function (clipPath) { + if (clipPath._dom) { + Definable.prototype.markUsed.call(that, clipPath._dom); + } + if (clipPath._textDom) { + Definable.prototype.markUsed.call(that, clipPath._textDom); + } + }); + } +}; + +/** + * @file Manages SVG shadow elements. + * @author Zhang Wenli + */ + +/** + * Manages SVG shadow elements. + * + * @class + * @extends Definable + * @param {number} zrId zrender instance id + * @param {SVGElement} svgRoot root of SVG document + */ +function ShadowManager(zrId, svgRoot) { + Definable.call( + this, + zrId, + svgRoot, + ['filter'], + '__filter_in_use__', + '_shadowDom' + ); +} + + +inherits(ShadowManager, Definable); + + +/** + * Create new shadow DOM for fill or stroke if not exist, + * but will not update shadow if exists. + * + * @param {SvgElement} svgElement SVG element to paint + * @param {Displayable} displayable zrender displayable element + */ +ShadowManager.prototype.addWithoutUpdate = function ( + svgElement, + displayable +) { + if (displayable && hasShadow(displayable.style)) { + var style = displayable.style; + + // Create dom in if not exists + var dom; + if (style._shadowDom) { + // Gradient exists + dom = style._shadowDom; + + var defs = this.getDefs(true); + if (!defs.contains(style._shadowDom)) { + // _shadowDom is no longer in defs, recreate + this.addDom(dom); + } + } + else { + // New dom + dom = this.add(displayable); + } + + this.markUsed(displayable); + + var id = dom.getAttribute('id'); + svgElement.style.filter = 'url(#' + id + ')'; + } +}; + + +/** + * Add a new shadow tag in + * + * @param {Displayable} displayable zrender displayable element + * @return {SVGFilterElement} created DOM + */ +ShadowManager.prototype.add = function (displayable) { + var dom = this.createElement('filter'); + var style = displayable.style; + + // Set dom id with shadow id, since each shadow instance + // will have no more than one dom element. + // id may exists before for those dirty elements, in which case + // id should remain the same, and other attributes should be + // updated. + style._shadowDomId = style._shadowDomId || this.nextId++; + dom.setAttribute('id', 'zr' + this._zrId + + '-shadow-' + style._shadowDomId); + + this.updateDom(displayable, dom); + this.addDom(dom); + + return dom; +}; + + +/** + * Update shadow. + * + * @param {Displayable} displayable zrender displayable element + */ +ShadowManager.prototype.update = function (svgElement, displayable) { + var style = displayable.style; + if (hasShadow(style)) { + var that = this; + Definable.prototype.update.call(this, displayable, function (style) { + that.updateDom(displayable, style._shadowDom); + }); + } + else { + // Remove shadow + this.remove(svgElement, style); + } +}; + + +/** + * Remove DOM and clear parent filter + */ +ShadowManager.prototype.remove = function (svgElement, style) { + if (style._shadowDomId != null) { + this.removeDom(style); + svgElement.style.filter = ''; + } +}; + + +/** + * Update shadow dom + * + * @param {Displayable} displayable zrender displayable element + * @param {SVGFilterElement} dom DOM to update + */ +ShadowManager.prototype.updateDom = function (displayable, dom) { + var domChild = dom.getElementsByTagName('feDropShadow'); + if (domChild.length === 0) { + domChild = this.createElement('feDropShadow'); + } + else { + domChild = domChild[0]; + } + + var style = displayable.style; + var scaleX = displayable.scale ? (displayable.scale[0] || 1) : 1; + var scaleY = displayable.scale ? (displayable.scale[1] || 1) : 1; + + // TODO: textBoxShadowBlur is not supported yet + var offsetX, offsetY, blur, color; + if (style.shadowBlur || style.shadowOffsetX || style.shadowOffsetY) { + offsetX = style.shadowOffsetX || 0; + offsetY = style.shadowOffsetY || 0; + blur = style.shadowBlur; + color = style.shadowColor; + } + else if (style.textShadowBlur) { + offsetX = style.textShadowOffsetX || 0; + offsetY = style.textShadowOffsetY || 0; + blur = style.textShadowBlur; + color = style.textShadowColor; + } + else { + // Remove shadow + this.removeDom(dom, style); + return; + } + + domChild.setAttribute('dx', offsetX / scaleX); + domChild.setAttribute('dy', offsetY / scaleY); + domChild.setAttribute('flood-color', color); + + // Divide by two here so that it looks the same as in canvas + // See: https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-shadowblur + var stdDx = blur / 2 / scaleX; + var stdDy = blur / 2 / scaleY; + var stdDeviation = stdDx + ' ' + stdDy; + domChild.setAttribute('stdDeviation', stdDeviation); + + // Fix filter clipping problem + dom.setAttribute('x', '-100%'); + dom.setAttribute('y', '-100%'); + dom.setAttribute('width', Math.ceil(blur / 2 * 200) + '%'); + dom.setAttribute('height', Math.ceil(blur / 2 * 200) + '%'); + + dom.appendChild(domChild); + + // Store dom element in shadow, to avoid creating multiple + // dom instances for the same shadow element + style._shadowDom = dom; +}; + +/** + * Mark a single shadow to be used + * + * @param {Displayable} displayable displayable element + */ +ShadowManager.prototype.markUsed = function (displayable) { + var style = displayable.style; + if (style && style._shadowDom) { + Definable.prototype.markUsed.call(this, style._shadowDom); + } +}; + +function hasShadow(style) { + // TODO: textBoxShadowBlur is not supported yet + return style + && (style.shadowBlur || style.shadowOffsetX || style.shadowOffsetY + || style.textShadowBlur || style.textShadowOffsetX + || style.textShadowOffsetY); +} + +/** + * SVG Painter + * @module zrender/svg/Painter + */ + +function parseInt10$2(val) { + return parseInt(val, 10); +} + +function getSvgProxy(el) { + if (el instanceof Path) { + return svgPath; + } + else if (el instanceof ZImage) { + return svgImage; + } + else if (el instanceof Text) { + return svgText; + } + else { + return svgPath; + } +} + +function checkParentAvailable(parent, child) { + return child && parent && child.parentNode !== parent; +} + +function insertAfter(parent, child, prevSibling) { + if (checkParentAvailable(parent, child) && prevSibling) { + var nextSibling = prevSibling.nextSibling; + nextSibling ? parent.insertBefore(child, nextSibling) + : parent.appendChild(child); + } +} + +function prepend(parent, child) { + if (checkParentAvailable(parent, child)) { + var firstChild = parent.firstChild; + firstChild ? parent.insertBefore(child, firstChild) + : parent.appendChild(child); + } +} + +function remove$1(parent, child) { + if (child && parent && child.parentNode === parent) { + parent.removeChild(child); + } +} + +function getTextSvgElement(displayable) { + return displayable.__textSvgEl; +} + +function getSvgElement(displayable) { + return displayable.__svgEl; +} + +/** + * @alias module:zrender/svg/Painter + * @constructor + * @param {HTMLElement} root 绘图容器 + * @param {module:zrender/Storage} storage + * @param {Object} opts + */ +var SVGPainter = function (root, storage, opts, zrId) { + + this.root = root; + this.storage = storage; + this._opts = opts = extend({}, opts || {}); + + var svgRoot = createElement('svg'); + svgRoot.setAttribute('xmlns', 'http://www.w3.org/2000/svg'); + svgRoot.setAttribute('version', '1.1'); + svgRoot.setAttribute('baseProfile', 'full'); + svgRoot.style.cssText = 'user-select:none;position:absolute;left:0;top:0;'; + + this.gradientManager = new GradientManager(zrId, svgRoot); + this.clipPathManager = new ClippathManager(zrId, svgRoot); + this.shadowManager = new ShadowManager(zrId, svgRoot); + + var viewport = document.createElement('div'); + viewport.style.cssText = 'overflow:hidden;position:relative'; + + this._svgRoot = svgRoot; + this._viewport = viewport; + + root.appendChild(viewport); + viewport.appendChild(svgRoot); + + this.resize(opts.width, opts.height); + + this._visibleList = []; +}; + +SVGPainter.prototype = { + + constructor: SVGPainter, + + getType: function () { + return 'svg'; + }, + + getViewportRoot: function () { + return this._viewport; + }, + + getViewportRootOffset: function () { + var viewportRoot = this.getViewportRoot(); + if (viewportRoot) { + return { + offsetLeft: viewportRoot.offsetLeft || 0, + offsetTop: viewportRoot.offsetTop || 0 + }; + } + }, + + refresh: function () { + + var list = this.storage.getDisplayList(true); + + this._paintList(list); + }, + + setBackgroundColor: function (backgroundColor) { + // TODO gradient + this._viewport.style.background = backgroundColor; + }, + + _paintList: function (list) { + this.gradientManager.markAllUnused(); + this.clipPathManager.markAllUnused(); + this.shadowManager.markAllUnused(); + + var svgRoot = this._svgRoot; + var visibleList = this._visibleList; + var listLen = list.length; + + var newVisibleList = []; + var i; + for (i = 0; i < listLen; i++) { + var displayable = list[i]; + var svgProxy = getSvgProxy(displayable); + var svgElement = getSvgElement(displayable) + || getTextSvgElement(displayable); + if (!displayable.invisible) { + if (displayable.__dirty) { + svgProxy && svgProxy.brush(displayable); + + // Update clipPath + this.clipPathManager.update(displayable); + + // Update gradient and shadow + if (displayable.style) { + this.gradientManager + .update(displayable.style.fill); + this.gradientManager + .update(displayable.style.stroke); + + this.shadowManager + .update(svgElement, displayable); + } + + displayable.__dirty = false; + } + newVisibleList.push(displayable); + } + } + + var diff = arrayDiff$1(visibleList, newVisibleList); + var prevSvgElement; + + // First do remove, in case element moved to the head and do remove + // after add + for (i = 0; i < diff.length; i++) { + var item = diff[i]; + if (item.removed) { + for (var k = 0; k < item.count; k++) { + var displayable = visibleList[item.indices[k]]; + var svgElement = getSvgElement(displayable); + var textSvgElement = getTextSvgElement(displayable); + remove$1(svgRoot, svgElement); + remove$1(svgRoot, textSvgElement); + } + } + } + for (i = 0; i < diff.length; i++) { + var item = diff[i]; + if (item.added) { + for (var k = 0; k < item.count; k++) { + var displayable = newVisibleList[item.indices[k]]; + var svgElement = getSvgElement(displayable); + var textSvgElement = getTextSvgElement(displayable); + prevSvgElement + ? insertAfter(svgRoot, svgElement, prevSvgElement) + : prepend(svgRoot, svgElement); + if (svgElement) { + insertAfter(svgRoot, textSvgElement, svgElement); + } + else if (prevSvgElement) { + insertAfter( + svgRoot, textSvgElement, prevSvgElement + ); + } + else { + prepend(svgRoot, textSvgElement); + } + // Insert text + insertAfter(svgRoot, textSvgElement, svgElement); + prevSvgElement = textSvgElement || svgElement + || prevSvgElement; + + this.gradientManager + .addWithoutUpdate(svgElement, displayable); + this.shadowManager + .addWithoutUpdate(prevSvgElement, displayable); + this.clipPathManager.markUsed(displayable); + } + } + else if (!item.removed) { + for (var k = 0; k < item.count; k++) { + var displayable = newVisibleList[item.indices[k]]; + prevSvgElement + = svgElement + = getTextSvgElement(displayable) + || getSvgElement(displayable) + || prevSvgElement; + + this.gradientManager.markUsed(displayable); + this.gradientManager + .addWithoutUpdate(svgElement, displayable); + + this.shadowManager.markUsed(displayable); + this.shadowManager + .addWithoutUpdate(svgElement, displayable); + + this.clipPathManager.markUsed(displayable); + } + } + } + + this.gradientManager.removeUnused(); + this.clipPathManager.removeUnused(); + this.shadowManager.removeUnused(); + + this._visibleList = newVisibleList; + }, + + _getDefs: function (isForceCreating) { + var svgRoot = this._svgRoot; + var defs = this._svgRoot.getElementsByTagName('defs'); + if (defs.length === 0) { + // Not exist + if (isForceCreating) { + var defs = svgRoot.insertBefore( + createElement('defs'), // Create new tag + svgRoot.firstChild // Insert in the front of svg + ); + if (!defs.contains) { + // IE doesn't support contains method + defs.contains = function (el) { + var children = defs.children; + if (!children) { + return false; + } + for (var i = children.length - 1; i >= 0; --i) { + if (children[i] === el) { + return true; + } + } + return false; + }; + } + return defs; + } + else { + return null; + } + } + else { + return defs[0]; + } + }, + + resize: function (width, height) { + var viewport = this._viewport; + // FIXME Why ? + viewport.style.display = 'none'; + + // Save input w/h + var opts = this._opts; + width != null && (opts.width = width); + height != null && (opts.height = height); + + width = this._getSize(0); + height = this._getSize(1); + + viewport.style.display = ''; + + if (this._width !== width || this._height !== height) { + this._width = width; + this._height = height; + + var viewportStyle = viewport.style; + viewportStyle.width = width + 'px'; + viewportStyle.height = height + 'px'; + + var svgRoot = this._svgRoot; + // Set width by 'svgRoot.width = width' is invalid + svgRoot.setAttribute('width', width); + svgRoot.setAttribute('height', height); + } + }, + + /** + * 获取绘图区域宽度 + */ + getWidth: function () { + return this._width; + }, + + /** + * 获取绘图区域高度 + */ + getHeight: function () { + return this._height; + }, + + _getSize: function (whIdx) { + var opts = this._opts; + var wh = ['width', 'height'][whIdx]; + var cwh = ['clientWidth', 'clientHeight'][whIdx]; + var plt = ['paddingLeft', 'paddingTop'][whIdx]; + var prb = ['paddingRight', 'paddingBottom'][whIdx]; + + if (opts[wh] != null && opts[wh] !== 'auto') { + return parseFloat(opts[wh]); + } + + var root = this.root; + // IE8 does not support getComputedStyle, but it use VML. + var stl = document.defaultView.getComputedStyle(root); + + return ( + (root[cwh] || parseInt10$2(stl[wh]) || parseInt10$2(root.style[wh])) + - (parseInt10$2(stl[plt]) || 0) + - (parseInt10$2(stl[prb]) || 0) + ) | 0; + }, + + dispose: function () { + this.root.innerHTML = ''; + + this._svgRoot + = this._viewport + = this.storage + = null; + }, + + clear: function () { + if (this._viewport) { + this.root.removeChild(this._viewport); + } + }, + + pathToDataUrl: function () { + this.refresh(); + var html = this._svgRoot.outerHTML; + return 'data:image/svg+xml;charset=UTF-8,' + html; + } +}; + +// Not supported methods +function createMethodNotSupport$1(method) { + return function () { + zrLog('In SVG mode painter not support method "' + method + '"'); + }; +} + +// Unsuppoted methods +each$1([ + 'getLayer', 'insertLayer', 'eachLayer', 'eachBuiltinLayer', + 'eachOtherLayer', 'getLayers', 'modLayer', 'delLayer', 'clearLayer', + 'toDataURL', 'pathToImage' +], function (name) { + SVGPainter.prototype[name] = createMethodNotSupport$1(name); +}); + +registerPainter('svg', SVGPainter); + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +// Import all charts and components + +exports.version = version; +exports.dependencies = dependencies; +exports.PRIORITY = PRIORITY; +exports.init = init; +exports.connect = connect; +exports.disConnect = disConnect; +exports.disconnect = disconnect; +exports.dispose = dispose; +exports.getInstanceByDom = getInstanceByDom; +exports.getInstanceById = getInstanceById; +exports.registerTheme = registerTheme; +exports.registerPreprocessor = registerPreprocessor; +exports.registerProcessor = registerProcessor; +exports.registerPostUpdate = registerPostUpdate; +exports.registerAction = registerAction; +exports.registerCoordinateSystem = registerCoordinateSystem; +exports.getCoordinateSystemDimensions = getCoordinateSystemDimensions; +exports.registerLayout = registerLayout; +exports.registerVisual = registerVisual; +exports.registerLoading = registerLoading; +exports.extendComponentModel = extendComponentModel; +exports.extendComponentView = extendComponentView; +exports.extendSeriesModel = extendSeriesModel; +exports.extendChartView = extendChartView; +exports.setCanvasCreator = setCanvasCreator; +exports.registerMap = registerMap; +exports.getMap = getMap; +exports.dataTool = dataTool; +exports.zrender = zrender; +exports.graphic = graphic; +exports.number = number; +exports.format = format; +exports.throttle = throttle; +exports.helper = helper; +exports.matrix = matrix; +exports.vector = vector; +exports.color = color; +exports.parseGeoJSON = parseGeoJson$1; +exports.parseGeoJson = parseGeoJson; +exports.util = ecUtil; +exports.List = List; +exports.Model = Model; +exports.Axis = Axis; +exports.env = env$1; + +}))); +//# sourceMappingURL=echarts.js.map diff --git a/main/webapp/static/js/jedate.js b/main/webapp/static/js/jedate.js new file mode 100644 index 0000000..9cb63b6 --- /dev/null +++ b/main/webapp/static/js/jedate.js @@ -0,0 +1,1524 @@ +/** + @Name : jeDate v6.5.0 日期控件 + @Author: chen guojun + @Date: 2020-04-30 + @QQ群:516754269 + @官网:http://www.jemui.com/ 或 https://github.com/singod/jeDate + */ +;(function(window, factory) { + //amd + if (typeof define === 'function' && define.amd) { + define(factory); + } else if (typeof exports === 'object') { //umd + module.exports = factory(); + } else { + window.jeDate = factory(); + } +})(this, function() { + var doc = document, win = window; + var jet = {}, doc = document, regymdzz = "YYYY|MM|DD|hh|mm|ss|zz", gr = /\-/g, + regymd = "YYYY|MM|DD|hh|mm|ss|zz".replace("|zz",""), parseInt = function (n) { return window.parseInt(n, 10);}; + var $Q = function (selector,content) { + content = content || document; + return selector.nodeType ? selector : content.querySelector(selector); + }; + var jeDate = function(elem,options){ + var opts = typeof (options) === "function" ? options() : options; + return new jeDatePick(elem,opts); + }; + //日期控件版本 + jeDate.dateVer = "V6.5.0"; + //用一个或多个其他对象来扩展一个对象,返回被扩展的对象 + jeDate.extend = jet.extend = function () { + var options, name, src, copy,deep = false, target = arguments[0], i = 1, length = arguments.length; + if (typeof (target) === "boolean") deep = target, target = arguments[1] || {}, i = 2; + if (typeof (target) !== "object" && typeof (target) !== "function") target = {}; + if (length === i) target = this, --i; + for (; i < length; i++) { + if ((options = arguments[i]) != null) { + for (name in options) { + src = target[name], copy = options[name]; + if (target === copy) continue; + if (copy !== undefined) target[name] = copy; + } + } + } + return target; + }; + //返回指定日期 + jeDate.nowDate = function (val,format) { + format = format || 'YYYY-MM-DD hh:mm:ss'; + if (!isNaN(val)) val = {DD: val}; + return jet.parse(jet.getDateTime(val),format); + }; + //日期转换 + jeDate.convert = function (obj) { + obj.format = obj.format || 'YYYY-MM-DD hh:mm:ss'; + obj.addval = obj.addval || []; + var mats = jet.reMatch(obj.format),objVal = {}; + jet.each(jet.reMatch(obj.val),function (i,cval) { + objVal[mats[i]] = parseInt(cval); + }); + var result = new DateTime(obj.addval,objVal), redate = { + YYYY:result.GetYear(), MM:result.GetMonth(), DD:result.GetDate(), + hh:result.GetHours(), mm:result.GetMinutes(), ss:result.GetSeconds() + }; + return redate; + }; + jeDate.valText = function (elem,value) { + return jet.valText(elem,value); + } + //日期时间戳相互转换 + jeDate.timeStampDate = function (date,format) { + format = format || 'YYYY-MM-DD hh:mm:ss'; + var dateTest = (/^(-)?\d{1,10}$/.test(date) || /^(-)?\d{1,13}$/.test(date)); + if(/^[1-9]*[1-9][0-9]*$/.test(date) && dateTest){ + var vdate = parseInt(date); + if (/^(-)?\d{1,10}$/.test(vdate)) { + vdate = vdate * 1000; + } else if (/^(-)?\d{1,13}$/.test(vdate)) { + vdate = vdate * 1000; + } else if (/^(-)?\d{1,14}$/.test(vdate)) { + vdate = vdate * 100; + } else { + alert("时间戳格式不正确"); + return; + } + var setdate = new Date(vdate); + return jet.parse({ + YYYY:setdate.getFullYear(), MM:jet.digit(setdate.getMonth()+1), DD:jet.digit(setdate.getDate()) , + hh:jet.digit(setdate.getHours()), mm:jet.digit(setdate.getMinutes()), ss:jet.digit(setdate.getSeconds()) + }, format); + }else { + //将日期转换成时间戳 + var arrs = jet.reMatch(date), + newdate = new Date(arrs[0],arrs[1]-1,arrs[2],arrs[3]||0,arrs[4]||0,arrs[5]||0), + timeStr = Math.round(newdate.getTime() / 1000); + return timeStr; + } + }; + //获取年月日星期 + jeDate.getLunar = function(obj){ + //如果为数字类型的日期对获取到日期的进行替换 + var lunars = jeLunar(obj.YYYY, parseInt(obj.MM) - 1, obj.DD); + return{ + nM: lunars.lnongMonth, //农历月 + nD: lunars.lnongDate, //农历日 + cY: parseInt(lunars.solarYear), //阳历年 + cM: parseInt(lunars.solarMonth), //阳历月 + cD: parseInt(lunars.solarDate), //阳历日 + cW: lunars.inWeekDays, //汉字星期几 + nW: lunars.solarWeekDay //数字星期几 + }; + }; + //转换日期格式 + jeDate.parse = jet.parse = function(ymdhms, format) { + return format.replace(new RegExp(regymdzz,"g"), function(str, index) { + return str == "zz" ? "00":jet.digit(ymdhms[str]); + }); + } + //返回日期 + function DateTime(arr,valObj) { + var that = this,newdate = new Date(), narr = ["FullYear","Month","Date","Hours","Minutes","Seconds"]; + var vb = jet.extend({YYYY:null,MM:null,DD:null,hh:newdate.getHours(),mm:newdate.getMinutes(),ss:newdate.getSeconds()},valObj); + var ND = valObj == undefined ? newdate : new Date(vb.YYYY,vb.MM,vb.DD,vb.hh,vb.mm,vb.ss); + if((arr||[]).length>0) jet.each(arr,function (i,par) { + ND["set"+narr[i]](narr[i] == "Month" ? parseInt(par)-1:parseInt(par)); + }); + //返回一个数值相同的新DateTime对象 + that.reDate = function () { + return new DateTime(); + }; + //返回此实例的Date值 + that.GetValue = function () { + return ND; + }; + //获取此实例所表示日期的年份部分。 + that.GetYear = function () { + return ND.getFullYear(); + }; + //获取此实例所表示日期的月份部分。 + that.GetMonth = function () { + return ND.getMonth() + 1; + }; + //获取此实例所表示的日期为该月中的第几天。 + that.GetDate = function () { + return ND.getDate(); + }; + //获取此实例所表示日期的小时部分。 + that.GetHours = function () { + return ND.getHours(); + }; + //获取此实例所表示日期的分钟部分。 + that.GetMinutes = function () { + return ND.getMinutes(); + }; + //获取此实例所表示日期的秒部分。 + that.GetSeconds = function () { + return ND.getSeconds(); + }; + }; + + jet.extend(jet,{ + isType : function (obj,type) { + var firstUper = function (str) { + str = str.toLowerCase(); + return str.replace(/\b(\w)|\s(\w)/g, function (m) { + return m.toUpperCase(); + }); + } + return Object.prototype.toString.call(obj) == "[object " + firstUper(type) + "]"; + }, + each : function (obj, callback, args) { + var name, i = 0, length = obj.length, iselem = (length === undefined || obj === "function"); + if (iselem) { + for (name in obj) { if (callback.call(obj[name], name, obj[name]) === false) { break } } + } else { + for (; i < length;) { if (callback.call(obj[i], i, obj[i++]) === false) { break } } + } + return obj; + }, + on : function (elm, type, fn) { + if (elm.addEventListener) { + elm.addEventListener(type, fn, false);//DOM2.0 + return true; + }else if (elm.attachEvent) { + return elm.attachEvent("on" + type, fn);//IE5+ + }else { + elm["on" + type] = fn;//DOM 0 + } + }, + isObj : function (obj){ + for(var i in obj){return true;} + return false; + }, + trim : function (str){ return str.replace(/(^\s*)|(\s*$)/g, ""); }, + reMatch : function (str) { + var smarr = [],maStr = "", parti = /(^\w{4}|\w{2}\B)/g; + if(jet.isNum(str)){ + maStr = str.replace(parti,"$1-"); + }else{ + maStr = /^[A-Za-z]+$/.test(str) ? str.replace(parti,"$1-") : str; + } + jet.each(maStr.match(/\w+|d+/g),function (i,val) { + smarr.push(jet.isNum(val) ? parseInt(val):val); + }); + return smarr; + }, + equals : function (arrA,arrB) { + if (!arrB) return false; + if (arrA.length != arrB.length) return false; + for (var i = 0, l = arrA.length; i < l; i++) { + if (arrA[i] instanceof Array && arrB[i] instanceof Array) { + if (!arrA[i].equals(arrB[i])) return false; + } else if (arrA[i] != arrB[i]) { + return false; + } + } + return true; + }, + docScroll : function(type) { + type = type ? "scrollLeft" :"scrollTop"; + return document.body[type] | document.documentElement[type]; + }, + docArea : function(type) { + return document.documentElement[type ? "clientWidth" :"clientHeight"]; + }, + //补齐数位 + digit : function(num) { + return num < 10 ? "0" + (num | 0) :num; + }, + //判断是否为数字 + isNum : function(value){ + return /^[+-]?\d*\.?\d*$/.test(value) ? true : false; + }, + //获取本月的总天数 + getDaysNum : function(y, m) { + var num = 31,isLeap = (y % 100 !== 0 && y % 4 === 0) || (y % 400 === 0); + switch (parseInt(m)) { + case 2: num = isLeap ? 29 : 28; break; + case 4: case 6: case 9: case 11: num = 30; break; + } + return num; + }, + //获取月与年 + getYM : function(y, m, n) { + var nd = new Date(y, m - 1); + nd.setMonth(m - 1 + n); + return { + y: nd.getFullYear(), + m: nd.getMonth() + 1 + }; + }, + //获取上个月 + prevMonth : function(y, m, n) { + return jet.getYM(y, m, 0 - (n || 1)); + }, + //获取下个月 + nextMonth : function(y, m, n) { + return jet.getYM(y, m, n || 1); + }, + setCss:function(elem,obj) { + for (var x in obj) elem.style[x] = obj[x]; + }, + html : function (elem,html) { + return typeof html === "undefined" ? elem && elem.nodeType === 1 ? elem.innerHTML :undefined :typeof html !== "undefined" && html == true ? elem && elem.nodeType === 1 ? elem.outerHTML :undefined : elem.innerHTML = html; + + }, + // 读取设置节点文本内容 + text : function(elem,value) { + var innText = document.all ? "innerText" :"textContent"; + return typeof value === "undefined" ? elem && elem.nodeType === 1 ? elem[innText] :undefined : elem[innText] = value; + }, + //设置值 + val : function (elem,value) { + if (typeof value === "undefined") { + return elem && elem.nodeType === 1 && typeof elem.value !== "undefined" ? elem.value :undefined; + } + // 将value转化为string + value = value == null ? "" :value + ""; + elem.value = value; + }, + attr : function(elem,value){ + return elem.getAttribute(value); + }, + hasClass : function (obj, cls) { + return obj.className.match(new RegExp('(\\s|^)' + cls + '(\\s|$)')); + }, + stopPropagation : function (ev) { + (ev && ev.stopPropagation) ? ev.stopPropagation() : window.event.cancelBubble = true; + }, + template : function (str, data) { + var strCell = !/[^\w\-\.:]/.test(str) ? document.getElementById(str).innerHTML : str; + var keys = function (obj){ + var arr = []; + for(arr[arr.length] in obj); + return arr ; + }, dataVar = function (obj) { + var vars = ''; + for (var key in obj) { + vars += 'var ' + key + '= $D["' + key + '"];'; + } + return vars; + }, compile = function (source,data) { + var code = "var $out='" + source.replace(/[\r\n]/g, '').replace(/^(.+?)\{\%|\%\}(.+?)\{\%|\%\}(.+?)$/g, function (val) { + return val.replace(/(['"])/g, '\\\$1'); + }).replace(/\{\%\s*=\s*(.+?)\%\}/g, "';$out+=$1;$out+='").replace(/\{\%(.+?)\%\}/g, "';$1;$out+='") + "';return new String($out);"; + var vars = dataVar(data), Render = new Function('$D',vars + code); + return new Render(data) + ''; + }; + return compile(strCell,data); + }, + + //判断元素类型 + isValDiv : function(elem) { + return /textarea|input/.test(elem.tagName.toLocaleLowerCase()); + }, + valText : function (elem,value) { + var cell = $Q(elem) ,type = jet.isValDiv(cell) ? "val" : "text"; + if(value != undefined){ + jet[type](cell,value); + }else{ + return jet[type](cell); + } + }, + isBool : function(obj){ return (obj == undefined || obj == true ? true : false); }, + //获取返回的日期 + getDateTime : function (obj) { + var result = new DateTime(), objVal = jet.extend({YYYY:null,MM:null,DD:null,hh:0,mm:0,ss:0},obj), + matArr = {YYYY:"FullYear",MM:"Month",DD:"Date",hh:"Hours",mm:"Minutes",ss:"Seconds"}; + jet.each(["ss","mm","hh","DD","MM","YYYY"],function (i,mat) { + if (!jet.isNum(parseInt(objVal[mat]))) return null; + var reVal = result.GetValue(); + if (parseInt(objVal[mat]) || parseInt(objVal[mat]) == 0){ + reVal["set"+matArr[mat]](result["Get"+matArr[mat]]() + (mat == "MM" ? -1 : 0) + parseInt(objVal[mat])); + } + }); + //获取格式化后的日期 + var redate = { + YYYY:result.GetYear(), MM:result.GetMonth(), DD:result.GetDate(), + hh:result.GetHours(), mm:result.GetMinutes(), ss:result.GetSeconds() + }; + return redate; + } + }); + + function jeDatePick(elem, options){ + var config = { + language:{ + name : "cn", + month : ["01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12"], + weeks : [ "日", "一", "二", "三", "四", "五", "六" ], + times : ["小时","分钟","秒数"], + timetxt: ["时间选择","开始时间","结束时间"], + backtxt:"返回日期", + clear : "清空", + today : "现在", + yes : "确定" + }, + format:"YYYY-MM-DD hh:mm:ss", //日期格式 + minDate:"1900-01-01 00:00:00", //最小日期 + maxDate:"2099-12-31 23:59:59", //最大日期 + isShow:true, //是否显示为固定日历,为false的时候固定显示 + multiPane:true, //是否为双面板,为false是展示双面板 + onClose:true, //是否为选中日期后关闭弹层,为false时选中日期后关闭弹层 + range:false, //如果不为空且不为false,则会进行区域选择,例如 " 至 "," ~ "," To " + trigger:"click", //是否为内部触发事件,默认为内部触发事件 + position:[], //自定义日期弹层的偏移位置,长度为0,弹层自动查找位置 + valiDate:[], //有效日期与非有效日期,例如 ["0[4-7]$,1[1-5]$,2[58]$",true] + isinitVal:false, //是否初始化时间,默认不初始化时间 + initDate:{}, //初始化时间,加减 天 时 分 + isTime:true, //是否开启时间选择 + isClear:true, //是否显示清空按钮 + isToday:true, //是否显示今天或本月按钮 + isYes:true, //是否显示确定按钮 + festival:false, //是否显示农历节日 + fixed:true, //是否静止定位,为true时定位在输入框,为false时居中定位 + zIndex:2099, //弹出层的层级高度 + method:{}, //自定义方法 + theme:{}, //自定义主题色 + shortcut:[], //日期选择的快捷方式 + donefun:null, //选中日期完成的回调 + before:null, //在界面加载之前执行 + succeed:null //在界面加载之后执行 + }; + this.$opts = jet.extend(config,options||{}); + this.valCell = $Q(elem); + this.format = this.$opts.format; + this.valCell != null ? this.init() : alert(elem+" ID\u6216\u7C7B\u540D\u4E0D\u5B58\u5728!"); + jet.extend(this,this.$opts.method); + delete this.$opts.method; + } + var searandom = function (){ + var str = "",arr = [1,2,3,4,5,6,7,8,9,0]; + for(var i=0; i<8; i++) str += arr[Math.round(Math.random() * (arr.length-1))]; + return str; + }; + + var jefix = "jefixed",ymdzArr = jet.reMatch(regymdzz),elx = "#jedate"; + jet.extend(jeDatePick.prototype,{ + init : function () { + var that = this, opts = that.$opts, newDate = new Date(), shortArr = [], + trigges = opts.trigger,ndate = opts.initDate || [], inVal, range = opts.range, + zIndex = opts.zIndex == undefined ? 10000 : opts.zIndex,isShow = jet.isBool(opts.isShow), + isinitVal = (opts.isinitVal == undefined || opts.isinitVal == false) ? false : true; + that.setDatas(); + opts.before && opts.before(that.valCell); + //为开启初始化的时间设置值 + if (isinitVal && trigges && isShow) { + if (ndate[1]){ + var addval = jet.getDateTime(ndate[0]); + inVal = [{ + YYYY:addval.YYYY, MM:jet.digit(addval.MM), DD:jet.digit(addval.DD) , + hh:jet.digit(addval.hh), mm:jet.digit(addval.mm), ss:jet.digit(addval.ss) + }]; + }else { + inVal = that.getValue(jet.isObj(ndate[0]) ? ndate[0] : {}); + } + if(!range) that.setValue([inVal[0]],opts.format,true); + } + + var getCurrValue = function () { + var mats = jet.reMatch(that.format), isEmpty = that.getValue() != "",curVal = [], + parmat = that.dlen == 7 ? "hh:mm:ss" : "YYYY-MM"+ (that.dlen <= 2 ? "":"-DD"); + that.selectValue = [jet.parse(jet.getDateTime({}), parmat)]; + if(isEmpty && isShow){ + var getVal = that.getValue().split(range); + jet.each(new Array(range ? 2 : 1),function (a) { + curVal[a] = {}; + jet.each(jet.reMatch(getVal[a]),function (i,val) { + curVal[a][mats[i]] = parseInt(val); + }); + }); + if(range) that.selectValue = getVal; + }else{ + var parr = that.getValue({})[0], nmVal = jet.nextMonth(parr.YYYY,parr.MM||jet.getDateTime({}).MM), + narr = (that.dlen>2 && that.dlen <=6) ? {YYYY:nmVal.y,MM:nmVal.m} : {}; + curVal = [parr]; + } + that.selectDate = curVal; + return curVal; + },ymarr = []; + that.minDate = ""; that.maxDate = ""; + if(!isShow || !trigges) ymarr = getCurrValue(); + if(!isShow || !trigges){ + that.minDate = jet.isType(opts.minDate,"function") ? opts.minDate(that) : opts.minDate; + that.maxDate = jet.isType(opts.maxDate,"function") ? opts.maxDate(that) : opts.maxDate; + that.storeData(ymarr[0],ymarr[1]); + that.renderDate(); + opts.succeed && opts.succeed(that.dateCell); + }else{ + if (trigges) { + jet.on(that.valCell,trigges,function(){ + if (document.querySelectorAll(elx).length > 0) return; + var gvarr = getCurrValue(); + that.minDate = jet.isType(opts.minDate,"function") ? opts.minDate(that) : opts.minDate; + that.maxDate = jet.isType(opts.maxDate,"function") ? opts.maxDate(that) : opts.maxDate; + that.storeData(gvarr[0],gvarr[1]); + that.renderDate(); + }); + } + } + }, + setDatas : function(){ + var that = this, opts = that.$opts,range = opts.range,shortArr = [],isShow = jet.isBool(opts.isShow),multi = opts.multiPane; + that.$data = jet.extend({year:false,month:false,day:true,time:false,timebtn:false},{ + shortcut:[],lang:opts.language,yaerlist:[],monthlist:[[],[]],ymlist:[[],[]], daylist:[[],[]], + clear:opts.isClear,today:range ? false:opts.isToday,yes:opts.isYes,pane:multi ? 1:2 + }); + if(opts.shortcut.length>0){ + jet.each(opts.shortcut,function (i,short) { + var tarr = [], shval = jet.isType(short.val,"function") ? short.val() : short.val; + if(jet.isType(shval,"object")){ + for (var s in shval) tarr.push(s + ':' + shval[s]); + shortArr.push(jet.extend({},{name:short.name,val:"{" + tarr.join('#') + "}"})); + } + }); + that.$data.shortcut = shortArr; + } + that.dlen = (function () { + var mats = jet.reMatch(that.format),marr = []; + jet.each(ymdzArr,function(i,val){ + jet.each(mats,function(m,mval){ + if(val == mval) marr.push(mval); + }); + }); + var matlen = marr.length, lens = (marr[0] == "hh")&&matlen<=3 ? 7 : matlen; + return lens; + })(); + that.$data.dlen = that.dlen; + that.timeInspect = false; + if(that.dlen == 1){ + jet.extend(that.$data,{year:true,day:false}); + }else if(that.dlen == 2){ + jet.extend(that.$data,{month:true,day:false}); + }else if(that.dlen>3 && that.dlen <=6){ + that.$data.timebtn = true; + }else if(that.dlen == 7){ + jet.extend(that.$data,{day:false,time:true}); + } + if(!isShow){ + that.$data.clear = false; + that.$data.yes = false; + } + }, + renderDate : function () { + var that= this, opts = that.$opts,isShow = jet.isBool(opts.isShow), + elxID = !isShow ? elx+searandom() : elx, setzin = {"zIndex": (opts.zIndex == undefined ? 10000 : opts.zIndex)}; + if(that.dateCell == undefined){ + that.dateCell = document.createElement("div"); + that.dateCell.id = elxID.replace(/\#/g,""); + that.dateCell.className = elx.replace(/\#/g,"")+" "+(opts.shortcut.length>0?" leftmenu":""); + that.dateCell.setAttribute("author","chen guojun"); + } + jet.html(that.dateCell,jet.template(that.dateTemplate(),that.$data)); + //自定义主题色 + if(jet.isObj(opts.theme)){ + var styleDiv = document.createElement("style"),stCell = ".jedate"+searandom(), t = opts.theme, + BG = "background-color:"+t.bgcolor, WC = "color:"+(t.color == undefined ? "#FFFFFF":t.color), + OTH = (t.pnColor == undefined ? "":"color:"+t.pnColor+";"); + that.dateCell.className = that.dateCell.className+" "+stCell.replace(/^./g,""); styleDiv.setAttribute("type","text/css"); + styleDiv.innerHTML = stCell+" .jedate-menu p:hover{"+BG+";"+WC+";}"+stCell+" .jedate-header em{"+WC+";}"+ + stCell+" .jedate-content .yeartable td.action span,"+stCell+" .jedate-content .monthtable td.action span,"+ + stCell+" .jedate-content .yeartable td.action span:hover,"+stCell+" .jedate-content .monthtable td.action span:hover{"+BG+";border:1px "+t.bgcolor+" solid;"+WC+";}"+stCell+" .jedate-content .daystable td.action,"+stCell+" .jedate-content .daystable td.action:hover,"+ + stCell+" .jedate-content .daystable td.action .lunar,"+stCell+" .jedate-header,"+stCell+" .jedate-time .timeheader,"+ + stCell+" .jedate-time .hmslist ul li.action,"+stCell+" .jedate-time .hmslist ul li.action:hover,"+ + stCell+" .jedate-time .hmslist ul li.disabled.action,"+stCell+" .jedate-footbtn .timecon,"+stCell+" .jedate-footbtn .btnscon span{"+BG+";"+WC+";}"+ + stCell+" .jedate-content .daystable td.other,"+stCell+" .jedate-content .daystable td.other .nolunar,"+stCell+" .jedate-content .daystable td.other .lunar{"+OTH+"}"+stCell+" .jedate-content .daystable td.contain,"+stCell+" .jedate-content .daystable td.contain:hover{background-"+OTH+"}"; + that.dateCell.appendChild(styleDiv); + } + + that.compileBindNode(that.dateCell); + if (document.querySelectorAll(elxID).length > 0) document.body.removeChild($Q(elxID)); + !isShow ? that.valCell.appendChild(that.dateCell) : document.body.appendChild(that.dateCell); + jet.setCss(that.dateCell,jet.extend({position:(!isShow ? "relative" : (opts.fixed == true ? "absolute" :"fixed"))},isShow ? setzin:{})); + that.methodEventBind(); + if(that.dlen == 7 || (that.dlen>3 && that.dlen <=6)) that.locateScroll(); + if(opts.festival && opts.language.name == "cn") that.showFestival(); + if(isShow){ + that.dateOrien(that.dateCell,that.valCell); + that.blankArea(); + } + + }, + //设置日期值 + setValue : function (fnStr,matStr,bool) { + var that = this, valCell = that.valCell,strVal; + matStr = matStr || that.format; + if((typeof fnStr=='string')&&fnStr!=''){ + var sprange = fnStr.split(that.$opts.range), inArr=[]; + jet.each(sprange,function (i,sval) { + var reVal = jet.reMatch(sval), inObj={}; + jet.each(jet.reMatch(matStr),function (r,val) { + inObj[val] = reVal[r]; + }); + inArr.push(inObj); + }); + strVal = inArr; + }else { + strVal = fnStr; + } + var vals = that.parseValue(strVal,matStr); + if (bool != false) jet.valText(valCell,vals); + return vals; + }, + //获取日期值 + getValue : function (valobj) { + var that = this, valCell = that.valCell, + opts = that.$opts, reObj, result = new DateTime().reDate(), + dateY = result.GetYear(),dateM = result.GetMonth(),dateD = result.GetDate(), + timeh = result.GetHours(),timem = result.GetMinutes(),times = result.GetSeconds(); + if (valobj == undefined && jet.isBool(opts.isShow)){ + reObj = jet.valText(valCell); + }else { + var isValShow = jet.isBool(opts.isShow) ? (jet.valText(valCell) == "") : !jet.isBool(opts.isShow), + objarr = jet.extend({YYYY:null,MM:null,DD:null},valobj||{}), + ranMat = [],newArr = new Array(2),unObj = function (obj) { + return [(objarr[obj] == undefined || objarr[obj] == null),objarr[obj]]; + }, defObj = [{ YYYY:dateY,MM:dateM,DD:dateD, hh:timeh,mm:timem,ss:times,zz:00}, + { YYYY:dateY,MM:dateM,DD:dateD, hh:timeh,mm:timem,ss:times,zz:00}]; + if (isValShow) { + //目标为空值则获取当前日期时间 + jet.each(newArr,function (i) { + var inObj = {}; + jet.each(ymdzArr, function (r, val) { + inObj[val] = parseInt(unObj(val)[0] ? defObj[i][val] : unObj(val)[1]); + }); + ranMat.push(jet.extend(defObj[i], inObj)); + }); + } else { + var isunRange = opts.range != false, initVal = that.getValue(), + spVal = initVal.split(opts.range), reMat = jet.reMatch(that.format); + jet.each(newArr,function (i) { + var inObj = {}, reVal = isunRange ? jet.reMatch(spVal[i]) : jet.reMatch(initVal); + jet.each(reMat,function (r,val) { + inObj[val] = reVal[r]; + }); + var exVal = jet.extend(inObj,valobj||{}); + ranMat.push(jet.extend(defObj[i],exVal)); + }); + } + reObj = ranMat; + } + return reObj; + }, + storeData:function (curr,next) { + next = next || {}; + var that = this, opts = that.$opts,multi = opts.multiPane,valCell = that.valCell, + days = new Date().getDate(), DTS = that.$data,isnext = jet.isObj(next), + RES = {yearlist:[],monthlist:[[],[]],daylist:[],daytit:[],timelist:[]},seltime, + cday = curr.DD == null ? days : curr.DD, nday = next.DD == null ? days : next.DD, + timeA = {hh:curr.hh,mm:curr.mm,ss:curr.ss}, timeB = {hh:next.hh||0,mm:next.mm||0,ss:next.ss||0}; + //设置年的数据 + RES.yearlist.push(that.eachYear(parseInt(curr.YYYY),1)); + if(multi == false){ + var yearNext = isnext ? next.YYYY : curr.YYYY; + RES.yearlist.push(that.eachYear(parseInt(yearNext),2)); + } + //设置月的数据 + RES.monthlist[0] = that.eachMonth(curr.YYYY,0); + if(multi == false){ + var monthNext = isnext ? next.YYYY : curr.YYYY+1; + RES.monthlist[1] = that.eachMonth(curr.YYYY+1,1); + } + //设置天的数据 + RES.daylist.push(that.eachDays(curr.YYYY,curr.MM,cday,0)); + RES.daytit.push({YYYY:curr.YYYY,MM:curr.MM}); + if(multi == false){ + var dayNext = jet.nextMonth(curr.YYYY,curr.MM); + RES.daylist.push(that.eachDays(dayNext.y,dayNext.m,nday,1)); + RES.daytit.push({YYYY:dayNext.y,MM:dayNext.m}); + } + //设置时间数据 + that.selectTime = [timeA,timeB]; + RES.timelist.push(that.eachTime(timeA,1)); + if(multi == false){ + seltime = that.dlen == 7 && opts.range && !isnext ? timeA : timeB; + if(that.dlen == 7 && opts.range && jet.valText(valCell) == ""){ + that.selectTime[1] = jet.extend(timeB,timeA); + } + RES.timelist.push(that.eachTime(seltime,2)); + } + //最后将数据合并于总数据中 + jet.extend(that.$data,RES); + }, + dateTemplate : function() { + var that = this, opts = that.$opts, multi = opts.multiPane,YMDStr = "",hmsStr = "",lang = opts.language, + ytxt = lang.name == "cn" ? "年":"", mtxt = lang.name == "cn" ? "月":""; + var ymvals = multi ? '{%=ymlist[0].YYYY%}-{%=ymlist[0].MM%}':'{%=ymlist[0].YYYY%}-{%=ymlist[0].MM%}#{%=ymlist[ynidx].YYYY%}-{%=ymlist[ynidx].MM%}'; + var aowArr = (function () { + var butArr = [], ismu = multi ? "11":"23"; + if(that.dlen == 1){ + butArr = ['{%=yearlist[i][0].y-'+ismu+'%}','{%=yearlist[i][yearlist[i].length-1].y%}']; + }else if(that.dlen == 2){ + butArr = multi ? ['{%=yearlist[0][0].y-1%}','{%=yearlist[0][0].y+1%}']:['{%=yearlist[i][0].y-'+ismu+'%}','{%=yearlist[i][yearlist[i].length-1].y%}']; + }else if(that.dlen>2 && that.dlen <=6){ + butArr = ['{%=yearlist[0][0].y-1%}','{%=yearlist[0][0].y+1%}']; + } + return butArr; + })(); + var lyPrev = '󭛂', + lyNext = '󭛅', + ryPrev = '󭛂', + ryNext = '󭛅', + mPrev = '{% if(dlen>2){ %}󭘂{% } %}', + mNext = '{% if(dlen>2){ %}󭘅{% } %}'; + //循环年的模板 + var yaerHtml = ''+ + '{% for(var y=0;y<=11;y++){ %}{% if((y+1)%3==0){ %} {% } %} {% } %}
{%=yearlist[i][y].y%}'+ytxt+'
'; + //循环月的模板 + var monthHtml = ''+ + '{% for(var m=0;m<=11;m++){ %}{% if((m+1)%3==0){ %} {% } %} {% } %}
{%=monthlist[i][m].m%}'+mtxt+'
'; + //循环天的模板 + var daysHtml = ''+ + '{% for(var w=0;w{%=lang.weeks[w]%} {% } %}'+ + '{% for(var d=0;d<=41;d++){ %}{% if((d+1)%7==0){ %} {% } %} {% } %}
{%=daylist[i][d].day%}
'; + //循环时间模板 + var hmsHtml = '
{% for(var h=0;h
{%= timelist.length == 1 ? lang.timetxt[0]:lang.timetxt[h+1]%}
'+ + '

{%=lang.times[0]%}

{%=lang.times[1]%}

{%=lang.times[2]%}

'+ + '
{% for(var t=0;t<3;t++){ %}
    {% for(var s=0;s{%= timelist[h][t][s].hms < 10 ? "0" + timelist[h][t][s].hms :timelist[h][t][s].hms %}{% } %}
{% } %}
'+'
{% } %}'; + //左边选择模板 + var shortHtml = opts.shortcut.length > 0 ? "{% for(var s=0;s{%=shortcut[s].name%}

{% } %}":''; + + var ymtitHtml = (function () { + var ymtitStr = ""; + if(that.dlen == 1){ + ymtitStr = '{%=yearlist[i][0].y%}'+ytxt+' ~ {%=yearlist[i][yearlist[i].length-1].y%}'+ytxt+''; + }else if(that.dlen == 2){ + ymtitStr = '{%=yearlist[0][i].y%}'+ytxt+''; + }else if(that.dlen>2 && that.dlen <=6){ + ymtitStr = '{%=daytit[i].MM%}'+mtxt+''+ + '{%=daytit[i].YYYY%}'+ytxt+''; + } + return ymtitStr; + })(); + + var ymButton = (function () { + var titStrBut = ""; + if(that.dlen==1){ + titStrBut = multi ? [lyPrev+ryNext]:[lyPrev,ryNext]; + }else if(that.dlen==2){ + titStrBut = multi ? [lyPrev+ryNext] : [lyPrev,ryNext]; + }else if(that.dlen>2 && that.dlen <=6){ + titStrBut = multi ? [lyPrev+mPrev+mNext+ryNext] : [lyPrev+mPrev,mNext+ryNext]; + }else if(that.dlen==7){ + titStrBut = ""; + } + return titStrBut; + })(); + + if(that.dlen == 1){ + YMDStr = yaerHtml; + }else if(that.dlen == 2){ + YMDStr = yaerHtml + monthHtml; + }else if(that.dlen == 3){ + YMDStr = yaerHtml + monthHtml + daysHtml; + }else if(that.dlen > 3 && that.dlen <= 6){ + YMDStr = yaerHtml + monthHtml + daysHtml; + hmsStr = hmsHtml; + }else if(that.dlen == 7){ + hmsStr = hmsHtml; + } + var paneHtml = '{% for(var i=0;i'+ + '
{% if(i==0){ %}'+ymButton[0]+'{% }else{ %}'+ymButton[1]+'{% } %}'+ymtitHtml+'
'+ + '
'+YMDStr+'
'+ + '{% } %}'; + var btnStr = '{% if(timebtn){%}
{%=lang.timetxt[0]%}
{% } %}
{% if(clear){ %}{%=lang.clear%}{% } %}{% if(today){ %}{%=lang.today%}{% } %}{% if(yes){ %}{%=lang.yes%}{% } %}
'; + + return '
'+shortHtml+'
'+paneHtml+'
'+hmsStr+'
'+btnStr+'
'; + }, + //递归绑定事件 + compileBindNode : function (dom) { + var self = this, aton = "@on"; + var acquireAttr = function (atVal){ + var args=/\(.*\)/.exec(atVal); + if(args) { //如果函数带参数,将参数字符串转换为参数数组 + args = args[0]; + atVal = atVal.replace(args,""); + args = args.replace(/[\(\)\'\"]/g,'').split(","); + }else args = []; + return [atVal,args]; + }; + jet.each(dom.childNodes,function (i,node) { + if (node.nodeType === 1) { + if(!self.$opts.festival) node.removeAttribute("ymd"); + self.compileBindNode(node); + var geton = node.getAttribute(aton); + if(geton != null){ + var onarr = acquireAttr(geton); + jet.on(node,"click",function () { + self[onarr[0]] && self[onarr[0]].apply(node,onarr[1]); + }); + node.removeAttribute(aton); + } + } + }); + }, + methodEventBind : function() { + var that = this, opts = that.$opts, multi = opts.multiPane, DTS = that.$data, + result = new DateTime().reDate(),dateY = result.GetYear(),dateM = result.GetMonth(),dateD = result.GetDate(), + range = opts.range,elCell = that.dateCell; + jet.extend(that,{ + yearBtn:function (type,val) { + var yarr = val.split("#"), pval = jet.reMatch(yarr[0]), tmval = that.selectTime; + exarr = [jet.extend({YYYY:parseInt(val),MM:dateM,DD:dateD},tmval[0]),{}]; + var dateVal = that.parseValue([exarr[0]],that.format); + that.storeData(exarr[0],exarr[1]); + that.renderDate(); + opts.toggle && opts.toggle({elem:that.valCell,val:dateVal,date:exarr[0]}); + }, + yearShow:function (val) { + DTS.year = DTS.year ? false : true; + DTS.month = that.dlen < 3 ? true : false; + if(that.dlen > 2 && that.dlen <= 6){ + var dayCell = $Q(".daystable",elCell); + DTS.day = dayCell.style.display == "none" ? true : false; + } + that.renderDate(); + }, + monthBtn:function (type,val) { + var ymarr = jet.reMatch(val),tmval = that.selectTime, exarr=[], PrevYM , NextYM, + year = parseInt(ymarr[0]),month = parseInt(ymarr[1]); + if(range){ + if(type == "mprev"){ + PrevYM = jet.prevMonth(year, month); + NextYM = jet.nextMonth(PrevYM.y, PrevYM.m); + }else{ + NextYM = jet.nextMonth(year, month); + PrevYM = jet.prevMonth(NextYM.y, NextYM.m); + } + exarr = [jet.extend({YYYY:PrevYM.y,MM:PrevYM.m,DD:dateD},tmval[0]),{YYYY:NextYM.y,MM:NextYM.m,DD:dateD}]; + }else{ + var PNYM = (type == "mprev") ? jet.prevMonth(year, month) : jet.nextMonth(year, month); + exarr = [jet.extend({YYYY:PNYM.y,MM:PNYM.m,DD:dateD},tmval[0]),{}]; + } + var dateVal = that.parseValue([exarr[0]],that.format); + that.storeData(exarr[0],exarr[1]); + that.renderDate(); + opts.toggle && opts.toggle({elem:that.valCell,val:dateVal,date:exarr[0]}); + }, + monthShow:function (val) { + DTS.year = false; + DTS.month = DTS.month ? false : true; + if(that.dlen > 2 && that.dlen <= 6){ + var dayCell = $Q(".daystable",elCell); + DTS.day = dayCell.style.display == "none" ? true : false; + } + that.renderDate(); + }, + shortClick:function (val) { + var reval = val.replace(/\#/g,','),evobj = eval("("+reval+")"), + gval = jet.getDateTime(evobj),tmval = that.selectTime; + that.selectValue = [jet.parse(gval,"YYYY-MM-DD")]; + that.selectDate = [{YYYY:gval.YYYY,MM:gval.MM,DD:gval.DD}]; + if(opts.onClose){ + var nYM = jet.nextMonth(gval.YYYY,gval.MM), + ymarr = [{YYYY:gval.YYYY,MM:gval.MM,DD:gval.DD},{YYYY:nYM.y,MM:nYM.m,DD:null}]; + that.storeData(jet.extend(ymarr[0],tmval[0]),jet.extend(ymarr[1],tmval[1])); + that.renderDate(); + }else{ + that.setValue(gval,that.format); + that.closeDate(); + } + }, + yearClick:function (val) { + if(jet.hasClass(this,"disabled")) return; + var yearVal = "",lens = that.dlen; + if(range && lens == 1){ + var ylen = that.selectValue.length; + that.selectDate = (ylen == 2) ? [{YYYY:parseInt(val),MM:dateM}] : + [{YYYY:that.selectDate[0].YYYY,MM:that.selectDate[0].MM},{YYYY:parseInt(val),MM:dateM}]; + that.selectValue = (ylen == 2) ? [val+"-"+jet.digit(dateM)] : [that.selectValue[0],val+"-"+jet.digit(dateM)]; + + if(that.selectValue.length == 2){ + var svalarr = [that.selectValue[0],that.selectValue[1]],newArr = [{},{}]; + svalarr.sort(function(a, b){ return a > b ? 1 : -1; }); + that.selectValue = svalarr; + jet.each(svalarr,function(i,strval) { + jet.each(jet.reMatch(strval),function(s,dval) { + newArr[i][ymdzArr[s]] = dval; + }); + }); + that.selectDate = newArr; + } + }else if(lens>1 && lens <=6){ + yearVal = parseInt(val); + }else{ + that.selectValue = [val+"-"+jet.digit(dateM)]; + that.selectDate = [{YYYY:parseInt(val),MM:dateM}]; + } + DTS.year = (lens == 1) ? true : false; + DTS.month = (lens < 3) ? true : false; + DTS.day = (lens > 2 && lens <= 6) ? true : false; + var electVal = (lens>1 && lens <=6) ? yearVal : parseInt(that.selectDate[0].YYYY); + that.storeData(jet.extend({YYYY:electVal,MM:dateM,DD:dateD},that.selectTime[0]),{}); + that.renderDate(); + }, + monthClick:function (val) { + if(jet.hasClass(this,"disabled")) return; + var ymval = jet.reMatch(val),newArr = [{},{}],mlen = that.selectValue.length ; + if(range){ + that.selectDate = (mlen == 2) ? [{YYYY:ymval[0],MM:ymval[1]}] : + [{YYYY:that.selectDate[0].YYYY,MM:that.selectDate[0].MM},{YYYY:parseInt(val),MM:ymval[1]}]; + that.selectValue = (mlen == 2) ? [val] : [that.selectValue[0],val]; + + if(that.selectValue.length == 2){ + var svalarr = [that.selectValue[0],that.selectValue[1]]; + svalarr.sort(function(a, b){ return a > b ? 1 : -1; }); + that.selectValue = svalarr; + jet.each(svalarr,function(i,strval) { + jet.each(jet.reMatch(strval),function(s,dval) { + newArr[i][ymdzArr[s]] = dval; + }); + }); + that.selectDate = newArr; + } + }else{ + that.selectValue = [val]; + that.selectDate = [{YYYY:ymval[0],MM:ymval[1]}]; + } + if(that.dlen > 2){ + DTS.year = false; + DTS.month = false; + } + DTS.day = (that.dlen > 2 && that.dlen <= 6) ? true : false; + that.storeData(jet.extend({ + YYYY:parseInt(that.selectDate[0].YYYY), + MM:parseInt(that.selectDate[0].MM), + DD:dateD + },that.selectTime[0]),{}); + that.renderDate(); + }, + daysClick:function (val) { + if(jet.hasClass(this,"disabled")) return; + var tmval = that.selectTime, matVal = jet.reMatch(val), + slen = that.selectValue.length,dateVal = "", + newArr = [{},{}],sday,nYM,ymarr; + if(range){ + + if(slen == 1){ + var svalarr = [that.selectValue[0],val]; + svalarr.sort(function(a, b){ return a > b ? 1 : -1; }); + that.selectValue = svalarr; + jet.each(svalarr,function(i,strval) { + jet.each(jet.reMatch(strval),function(s,dval) { + newArr[i][ymdzArr[s]] = dval; + }); + }); + that.selectDate = newArr; + }else{ + that.selectValue = [val]; + newArr = [{YYYY:matVal[0],MM:matVal[1],DD:matVal[2]}]; + that.selectDate = [{YYYY:matVal[0],MM:matVal[1],DD:matVal[2]},{}]; + } + nYM = jet.nextMonth(newArr[0].YYYY,newArr[0].MM); + ymarr = [{YYYY:newArr[0].YYYY,MM:newArr[0].MM,DD:newArr[0].DD},{YYYY:nYM.y,MM:nYM.m,DD:null}]; + that.storeData(jet.extend(ymarr[0],tmval[0]),jet.extend(ymarr[1],tmval[1])); + that.renderDate(); + }else{ + that.selectValue = [val]; + that.selectDate = [{YYYY:matVal[0],MM:matVal[1],DD:matVal[2]},{YYYY:matVal[0],MM:matVal[1],DD:matVal[2]}]; + jet.each(new Array(range == false ? 1 : 2),function (a) { + jet.each(matVal,function (i,val) { + newArr[a][ymdzArr[i]] = val; + }); + jet.extend(newArr[a],tmval[a]); + }); + if(opts.onClose){ + that.storeData(jet.extend(newArr[0],tmval[0]),jet.extend(newArr[1],tmval[1])); + that.renderDate(); + }else{ + dateVal = that.setValue(newArr,that.format); + that.closeDate(); + opts.donefun && opts.donefun.call(that,{elem:that.valCell,val:dateVal,date:newArr}); + } + } + }, + hmsClick:function(idx,num) { + var pidx = parseInt(num), vals = parseInt(jet.text(this)), + paridx = parseInt(idx), act = "action",mhms = ["hh","mm","ss"], + ulCell = $Q(".jedate-time",that.dateCell).querySelectorAll("ul")[pidx], + tlen = that.$data.timelist[0].length; + if(jet.hasClass(this,"disabled")) return; + jet.each(ulCell.childNodes,function (i,node) { + var reg = new RegExp("(^|\\s+)" + act + "(\\s+|$)", "g"); + node.className = reg.test(node.className) ? node.className.replace(reg, '') : node.className; + }); + that.selectTime[paridx][paridx == 1 ? mhms[pidx-tlen]:mhms[pidx]] = vals; + this.className = this.className + act; + var hmsCls = ulCell.querySelector("."+act); + ulCell.scrollTop = hmsCls ? (hmsCls.offsetTop-145):0; + if(that.dlen == 7 && idx == 0 && range && !multi){ + var nVal = that.getValue({}), nYM = jet.nextMonth(nVal[0].YYYY,nVal[0].MM),st = that.selectTime; + that.storeData( + {YYYY:nVal[0].YYYY,MM:nVal[0].MM,DD:null,hh:st[0].hh,mm:st[0].mm,ss:st[0].ss}, + {YYYY:nYM.y,MM:nYM.m,DD:null,hh:st[1].hh,mm:st[1].mm,ss:st[1].ss} + ); + that.renderDate(); + } + }, + timeBtn:function() { + var timeCell = $Q(".jedate-time",elCell), disNo = timeCell.style.display == "none"; + jet.text(this,disNo ? opts.language.backtxt:opts.language.timetxt[0]); + jet.setCss(timeCell,{display: disNo ? "block":"none"}); + }, + //清空按钮函数 + clearBtn:function () { + jet.valText(that.valCell,""); + that.selectDate = [jet.parse(jet.getDateTime({}),"YYYY-MM-DD hh:mm:ss")]; + that.closeDate(); + opts.clearfun && opts.clearfun.call(that); + }, + //现在按钮函数 + nowBtn:function () { + var newArr = jet.getDateTime({}), nYM = jet.nextMonth(newArr.YYYY,newArr.MM), dateVal; + that.selectDate = [newArr]; + dateVal = opts.isShow ? that.setValue([newArr],that.format,true) : jet.parse(newArr,that.format); + if(opts.onClose && range || !opts.isShow){ + that.storeData(newArr,{YYYY:nYM.y,MM:nYM.m,DD:null,hh:0,mm:0,ss:0}); + that.renderDate(); + }else{ + that.closeDate(); + } + opts.donefun && opts.donefun.call(that,{elem:that.valCell,val:dateVal,date:newArr}); + }, + //确认按钮函数 + sureBtn:function () { + var newArr = that.selectValue.length > 1 ? [{},{}]: [{}], dateVal = "", tmval = that.selectTime; + var equal = function (o) { + var h = o.hh == undefined ? 0:o.hh, m = o.mm == undefined ? 0:o.mm, s = o.ss == undefined ? 0:o.ss; + return parseInt(jet.digit(h)+""+jet.digit(m)+""+jet.digit(s)); + }; + if(range){ + if(that.selectValue.length > 1){ + var sortarr = that.selectValue; + sortarr.sort(function(a, b){ return a > b ? 1 : -1; }); + jet.each(sortarr,function (i,arr) { + jet.each(jet.reMatch(arr),function (a,val) { + newArr[i][ymdzArr[a]] = val; + }); + jet.extend(newArr[i],tmval[i]); + }); + }else if(that.dlen == 7 && tmval.length>1){ + newArr = tmval; + } + var sameTime = equal(tmval[0]) >= equal(tmval[1]),selVal = that.selectValue, sameDate = ""; + if(selVal[1] != undefined) sameDate = selVal[0].replace(/\-/g,"") == selVal[1].replace(/\-/g,""); + if(selVal.length == 1 && that.dlen < 7){ + that.tips(opts.language.name == "cn" ? "未选结束日期" : "Please select the end date"); return; + }else if((that.dlen == 7 && sameTime) || (sameDate && sameTime)){ + that.tips(opts.language.name == "cn" ? "结束时间必须大于开始时间" : "The end time must be greater than the start time"); return; + } + }else{ + jet.each(new Array(range == false ? 1 : 2),function (i) { + if(that.dlen != 7) jet.each(jet.reMatch(that.selectValue[0]),function (a,val) { + newArr[i][ymdzArr[a]] = val; + }); + jet.extend(newArr[i],tmval[i]); + }); + } + dateVal = that.setValue(newArr,that.format,opts.isShow ? true:false); + opts.isShow && that.closeDate(); + opts.donefun && opts.donefun.call(that,{elem:that.valCell,val:dateVal,date:newArr}); + }, + blankArea:function () { + jet.on(document,"mouseup",function (ev) { + jet.stopPropagation(ev); + that.closeDate(); + }); + jet.on($Q(elx),"mouseup", function(ev) { + jet.stopPropagation(ev); + }); + } + }); + }, + //循环生成年数据 + eachYear : function (val,type) { + var that = this,opts = that.$opts, yNum = parseInt(val),yarr = [],seCls='', selYear = that.selectDate,i, + mins = jet.reMatch(that.minDate), maxs = jet.reMatch(that.maxDate); + i = type == 1 ? yNum : that.yindex; + that.yindex = type == 1 ? 12+yNum : 12+that.yindex; + var endDate = selYear[1] == undefined ? "":selYear[1].YYYY; + for(; i < that.yindex; i++){ + if(i == selYear[0].YYYY || i == endDate){ + seCls = "action"; + }else if(i>selYear[0].YYYY && i maxs[0]){ + seCls = "disabled"; + }else{ + seCls = ""; + } + yarr.push({style:seCls,y:i}); + } + return yarr; + }, + //循环生成月数据 + eachMonth : function (val,type) { + var that = this,opts = that.$opts, range = opts.range, marr = [], + selMonth = that.selectDate, seCls='',monthArr = opts.language.month , + mins = jet.reMatch(that.minDate), maxs = jet.reMatch(that.maxDate), + minym = parseInt(mins[0]+""+jet.digit(mins[1])), + maxym = parseInt(maxs[0]+""+jet.digit(maxs[1])), + currStart = parseInt(selMonth[0].YYYY+""+jet.digit(selMonth[0].MM)), + currEnd = selMonth[1] ? parseInt(selMonth[1].YYYY+""+jet.digit(selMonth[1].MM)) : 0; + jet.each(monthArr,function (i,months) { + var ival = parseInt(val+""+jet.digit(months)); + if(ival == currStart || ival == currEnd){ + seCls = "action"; + }else if(ival > currStart && ival < currEnd){ + seCls = "contain"; + }else if(ival < minym || ival > maxym){ + seCls = "disabled"; + }else{ + seCls = ""; + } + marr.push({style:seCls ,y:val ,m:months}); + }); + return marr; + }, + //循环生成天数据 + eachDays : function (yd,md,ds,idx) { + var that = this, count = 0, daysArr = [],opts = that.$opts, multiPane = jet.isBool(opts.multiPane), + firstWeek = new Date(yd, md - 1, 1).getDay() || 7,valrange = opts.range != false, + daysNum = jet.getDaysNum(yd, md), didx = 0, sDate = that.selectDate, + prevM = jet.prevMonth(yd, md),isShow = jet.isBool(opts.isShow), + prevDaysNum = jet.getDaysNum(yd, prevM.m),nextM = jet.nextMonth(yd, md), objCell = that.valCell, + lang = opts.language, endval = opts.valiDate||[], + minArr = jet.reMatch(that.minDate), minNum = parseInt(minArr[0]+""+jet.digit(minArr[1])+""+jet.digit(minArr[2])), + maxArr = jet.reMatch(that.maxDate), maxNum = parseInt(maxArr[0]+""+jet.digit(maxArr[1])+""+jet.digit(maxArr[2])); + var startDate = sDate[0] ? parseInt(sDate[0].YYYY+""+jet.digit(sDate[0].MM)+""+jet.digit(sDate[0].DD)) : ""; + var endDate = sDate[1] ? parseInt(sDate[1].YYYY+""+jet.digit(sDate[1].MM)+""+jet.digit(sDate[1].DD)) : ""; + //设置时间标注 + var setMark = function (my, mm, md) { + var Marks = opts.marks, contains = function(arr, obj) { + var clen = arr.length; + while (clen--) { if (arr[clen] === obj) return true; } + return false; + },isArr = jet.isType(Marks,"array"); + return isArr && Marks.length > 0 && contains(Marks, my + "-" + jet.digit(mm) + "-" + jet.digit(md)) ? '' :""; + }; + //是否显示节日 + var isfestival = function(y, m ,d) { + var festivalStr = ''; + if(opts.festival == true && lang.name == "cn"){ + var lunar = jeLunar(y, m - 1, d), feslunar = (lunar.solarFestival || lunar.lunarFestival), + lunartext = (feslunar && lunar.jieqi) != "" ? feslunar : (lunar.jieqi || lunar.showInLunar); + festivalStr = '

' + d + '' + lunartext + '

'; + }else{ + festivalStr = '

' + d + '

'; + } + return festivalStr; + }; + //判断是否在限制的日期之中 + var dateLimit = function(Y, M, D, isMonth){ + var thatNum = parseInt(Y + "" + jet.digit(M) + "" + jet.digit(D)); + if(isMonth){ + if (thatNum >= minNum && thatNum <= maxNum) return true; + }else { + if (minNum > thatNum || maxNum < thatNum) return true; + } + }; + + var regExpDate = function (date,cls) { + var inArray = function (search,array){ + for(var i in array) if(array[i]==search) return true; + return false; + }; + if(endval.length > 0 && endval[0]!=""){ + if(/\%/g.test(endval[0])){ + var reval = endval[0].replace(/\%/g,"").split(","), enArr = []; + jet.each(reval,function (r,rel) { + enArr.push(jet.digit(parseInt(rel))); + }); + var isfind = inArray(jet.digit(date), enArr) == false; + cls = jet.isBool(endval[1]) ? (isfind ? " disabled" :cls) : (isfind ? cls :" disabled"); + }else { + var valreg = that.dateRegExp(endval[0]), regday = valreg.test(jet.digit(date)); + cls = jet.isBool(endval[1]) ? (regday ? " disabled" : cls) : (regday ? cls : " disabled"); + } + } + return cls; + }; + + //var ymds = ymdarr[1] + //上一月剩余天数 + for (var p = prevDaysNum - firstWeek + 1; p <= prevDaysNum; p++, count++) { + var pmark = setMark(prevM.y,prevM.m,p), pcls = dateLimit(prevM.y, prevM.m, p, false) ? "disabled" : "other"; + pcls = regExpDate(p,pcls); + daysArr.push({style:pcls,ymd:prevM.y+'-'+jet.digit(prevM.m)+'-'+jet.digit(p),day:(isfestival(prevM.y,prevM.m,p) + pmark)}); + } + //本月的天数 + for(var b = 1; b <= daysNum; b++, count++){ + var bmark = setMark(yd,md,b), bcls = ""; + var dateval = parseInt(yd+""+jet.digit(md)+""+jet.digit(b)), + parsdate = dateval > startDate, rangdate = dateval < endDate; + if(dateLimit(yd, md, b, true)){ + if(dateval == startDate || dateval == endDate){ + bcls = " action"; + }else if(parsdate&&rangdate){ + bcls = " contain"; + }else { + bcls = ""; + } + }else { + bcls = " disabled"; + } + bcls = regExpDate(b,bcls); + daysArr.push({style:"normal"+bcls,ymd:yd+'-'+jet.digit(md)+'-'+jet.digit(b),day:(isfestival(yd,md,b) + bmark)}); + } + //下一月开始天数 + for(var n = 1, nlen = 42 - count; n <= nlen; n++){ + var nmark = setMark(nextM.y,nextM.m,n); + var ncls = dateLimit(nextM.y, nextM.m, n, false) ? "disabled" : "other"; + ncls = regExpDate(n,ncls); + daysArr.push({style:ncls,ymd:nextM.y+'-'+jet.digit(nextM.m)+'-'+jet.digit(n),day:(isfestival(nextM.y,nextM.m,n) + nmark)}); + } + //将星期与日期拼接起来 + return daysArr; + }, + eachTime : function (hmsArr,type) { + var that = this,opts = that.$opts, range = opts.range,multi = opts.multiPane, minVal = [], maxVal = [], + mhms = ["hh","mm","ss"], timeArr = [],hmsCls = '',format = that.format, + ntVal = jet.trim(that.minDate).replace(/\s+/g," "), + xtVal = jet.trim(that.maxDate).replace(/\s+/g," "), + nVal = ntVal.split(" "), xVal = xtVal.split(" "); + if(that.dlen>3 && /\:/.test(nVal) && /\:/.test(xVal)){ + minVal = jet.reMatch(/\s/.test(ntVal)&&that.dlen>3 ? nVal[1] : ntVal); + maxVal = jet.reMatch(/\s/.test(xtVal)&&that.dlen>3 ? xVal[1] : xtVal); + } + jet.each([24,60,60],function (s,lens) { + timeArr[s] = []; + var unhmsVal = minVal[s] == undefined || minVal[s] == 0 ? hmsArr[mhms[s]] : minVal[s], + currVal = that.getValue() == "" ? unhmsVal : hmsArr[mhms[s]]; + if(that.dlen>3 && /\:/.test(nVal) && type==1){ + that.selectTime[0][mhms[s]] = currVal; + } + for (var h = 0; h < lens; h++) { + var exists = new RegExp(mhms[s],"g").test(format); + if(h == currVal){ + hmsCls = exists ? "action" : "disabled"; + }else if(!exists || !range && multi &&(hmaxVal[s])){ + hmsCls = "disabled"; + }else if(!multi){ + hmsCls = type == 1&&hmaxVal[s] ? "disabled" : ""; + }else{ + hmsCls = ""; + } + timeArr[s].push({style:hmsCls,hms:h}); + } + }); + return timeArr; + }, + //关闭日期控件 + closeDate : function () { + var elem = $Q(elx), tipelem = $Q("#jedatetipscon"); + elem && document.body.removeChild(elem); + tipelem && document.body.removeChild(tipelem); + //再次初始化值 + this.setDatas(); + }, + //转换日期值 + parseValue : function (fnObj,matStr) { + var that = this, valArr=[],opts = that.$opts, range = opts.range; + jet.each(fnObj,function (i,val) { + valArr.push(jet.parse(val, matStr)); + }); + return range == false ? valArr[0] : valArr.join(range); + }, + //初始验证正则 + dateRegExp : function(valArr) { + var enval = valArr.split(",")||[], regs = ""; + var doExp = function (val) { + var arr, tmpEval, regs = /#?\{(.*?)\}/; + val = val + ""; + while ((arr = regs.exec(val)) != null) { + arr.lastIndex = arr.index + arr[1].length + arr[0].length - arr[1].length - 1; + tmpEval = parseInt(eval(arr[1])); + if (tmpEval < 0) tmpEval = "9700" + -tmpEval; + val = val.substring(0, arr.index) + tmpEval + val.substring(arr.lastIndex + 1); + } + return val; + }; + if (enval && enval.length > 0) { + for (var i = 0; i < enval.length; i++) { + regs += doExp(enval[i]); + if (i != enval.length - 1) regs += "|"; + } + regs = regs ? new RegExp("(?:" + regs + ")") : null; + } else { + regs = null; + } + //re = new RegExp((re + "").replace(/^\/\(\?:(.*)\)\/.*/, "$1")); + return regs; + }, + //显示农历节日 + showFestival:function () { + var that = this, opts = that.$opts; + jet.each(that.dateCell.querySelectorAll(".daystable td"),function (i,node) { + var tval = jet.reMatch(jet.attr(node,"ymd")),tipDiv = document.createElement("div"); + node.removeAttribute("ymd"); + //鼠标进入提示框出现 + jet.on(node,"mouseover",function () { + var lunar = new jeLunar(tval[0], tval[1] - 1, tval[2]); + if($Q("#jedatetipscon")) return; + tipDiv.id = tipDiv.className = "jedatetipscon"; + var tiphtml = '

' + lunar.solarYear + '\u5E74' + lunar.solarMonth + '\u6708' + lunar.solarDate + '\u65E5 ' + lunar.inWeekDays + '

\u519C\u5386:' + lunar.shengxiao + '\u5E74 ' + lunar.lnongMonth + '\u6708' + lunar.lnongDate + '

' + lunar.ganzhiYear + '\u5E74 ' + lunar.ganzhiMonth + '\u6708 ' + lunar.ganzhiDate + '\u65E5

'; + var Fesjieri = (lunar.solarFestival || lunar.lunarFestival) != "" ? '

' + ("\u8282\u65E5:"+lunar.solarFestival + lunar.lunarFestival) + '

' : ""; + var Fesjieqi = lunar.jieqi != "" ? '

'+(lunar.jieqi != "" ? "\u8282\u6C14:"+lunar.jieqi : "") + '

': ""; + var tiptext = (lunar.solarFestival || lunar.lunarFestival || lunar.jieqi) != "" ? (Fesjieri + Fesjieqi) : ""; + jet.html(tipDiv,tiphtml + tiptext); + document.body.appendChild(tipDiv); + //获取并设置农历提示框出现的位置 + var tipPos = that.lunarOrien(tipDiv, this); + jet.setCss(tipDiv,{"zIndex": (opts.zIndex == undefined ? 10000 + 5 : opts.zIndex + 5),top:tipPos.top,left:tipPos.left,position:"absolute",display:"block"}); + }); + //鼠标移除提示框消失 + jet.on(node,"mouseout",function () { + document.body.removeChild($Q("#jedatetipscon")); + }); + }); + if (that.dateCell.nodeType === 1 && !jet.hasClass(that.dateCell,"grid")) that.dateCell.className = that.dateCell.className + " grid"; + }, + //农历方位辨别 + lunarOrien : function(obj, self, pos) { + var tops, leris, ortop, orleri, rect =self.getBoundingClientRect(), boxW = obj.offsetWidth, boxH = obj.offsetHeight; + leris = rect.right + boxW / 1.5 >= jet.docArea(true) ? rect.right - boxW : rect.left + (pos ? 0 : jet.docScroll(true)); + tops = rect.bottom + boxH / 1 <= jet.docArea() ? rect.bottom - 1 : rect.top > boxH / 1.5 ? rect.top - boxH - 1 : jet.docArea() - boxH; + if(leris + boxW > jet.docArea(true)) leris = rect.left - (boxW - rect.width); + ortop = Math.max(tops + (pos ? 0 :jet.docScroll()) + 1, 1) + "px", orleri = leris + "px"; + return {top: ortop, left: orleri } + }, + //辨别控件的方位 + dateOrien : function(elbox, valCls, pos) { + var that = this, tops, leris, ortop, orleri, + rect = that.$opts.fixed ? valCls.getBoundingClientRect() : elbox.getBoundingClientRect(), + leris = rect.left, tops = rect.bottom; + if(that.$opts.fixed) { + var boxW = elbox.offsetWidth, boxH = elbox.offsetHeight; + //如果右侧超出边界 + if(leris + boxW > jet.docArea(true)){ + leris = leris - (boxW - rect.width); + } + //如果底部超出边界 + if(tops + boxH > jet.docArea()){ + tops = rect.top > boxH ? rect.top - boxH -2 : jet.docArea() - boxH -1; + } + //根据目标元素计算弹层位置 + ortop = Math.max(tops + (pos ? 0 :jet.docScroll())+1, 1) + "px"; orleri = leris + "px"; + }else{ + //弹层位置位于页面上下左右居中 + ortop = "50%"; orleri = "50%"; + elbox.style.cssText = "marginTop:"+-(rect.height / 2)+";marginLeft:"+-(rect.width / 2); + } + jet.setCss(elbox,{top:ortop,left:orleri}); + }, + tips : function (text, time) { + var that = this, tipCls = $Q(".jedate-tips",that.dateCell),tipTime; + jet.html(tipCls,text||""); jet.setCss(tipCls,{display:"block"}); + clearTimeout(tipTime); + tipTime = setTimeout(function(){ + jet.html(tipCls,""); jet.setCss(tipCls,{display:"none"}); + }, (time||2.5)*1000); + }, + locateScroll : function () { + var that = this, ulCell = $Q(".jedate-time",that.dateCell).querySelectorAll("ul"); + jet.each(ulCell, function(i,cell) { + var hmsCls = cell.querySelector(".action"); + cell.scrollTop = hmsCls ? (hmsCls.offsetTop-145):0; + }); + if(that.dlen != 7) jet.setCss($Q(".jedate-time",that.dateCell),{display:'none'}); + } + }); + + //农历数据 + function jeLunar(ly,lm,ld) { + var lunarInfo=[19416,19168,42352,21717,53856,55632,91476,22176,39632,21970,19168,42422,42192,53840,119381,46400,54944,44450,38320,84343,18800,42160,46261,27216,27968,109396,11104,38256,21234,18800,25958,54432,59984,28309,23248,11104,100067,37600,116951,51536,54432,120998,46416,22176,107956,9680,37584,53938,43344,46423,27808,46416,86869,19872,42448,83315,21200,43432,59728,27296,44710,43856,19296,43748,42352,21088,62051,55632,23383,22176,38608,19925,19152,42192,54484,53840,54616,46400,46496,103846,38320,18864,43380,42160,45690,27216,27968,44870,43872,38256,19189,18800,25776,29859,59984,27480,21952,43872,38613,37600,51552,55636,54432,55888,30034,22176,43959,9680,37584,51893,43344,46240,47780,44368,21977,19360,42416,86390,21168,43312,31060,27296,44368,23378,19296,42726,42208,53856,60005,54576,23200,30371,38608,19415,19152,42192,118966,53840,54560,56645,46496,22224,21938,18864,42359,42160,43600,111189,27936,44448], + sTermInfo = [ 0, 21208, 43467, 63836, 85337, 107014, 128867, 150921, 173149, 195551, 218072, 240693, 263343, 285989, 308563, 331033, 353350, 375494, 397447, 419210, 440795, 462224, 483532, 504758 ]; + var Gan = "甲乙丙丁戊己庚辛壬癸", Zhi = "子丑寅卯辰巳午未申酉戌亥", Animals = "鼠牛虎兔龙蛇马羊猴鸡狗猪"; + var solarTerm = [ "小寒", "大寒", "立春", "雨水", "惊蛰", "春分", "清明", "谷雨", "立夏", "小满", + "芒种", "夏至", "小暑", "大暑", "立秋", "处暑", "白露", "秋分", "寒露", "霜降", "立冬", "小雪", "大雪", "冬至" ]; + var nStr1 = "日一二三四五六七八九十", nStr2 = "初十廿卅", nStr3 = [ "正", "二", "三", "四", "五", "六", "七", "八", "九", "十", "十一", "腊"], + sFtv1 = { + "0101" : "*1元旦节", "0202" : "湿地日", + "0214" : "情人节", "0308" : "妇女节", + "0312" : "植树节", "0315" : "消费者权益日", + "0401" : "愚人节", "0422" : "地球日", + "0501" : "*1劳动节", "0504" : "青年节", + "0512" : "护士节", "0518" : "博物馆日", + "0520" : "母亲节", "0601" : "儿童节", + "0623" : "奥林匹克日", "0630" : "父亲节", + "0701" : "建党节", "0801" : "建军节", + "0903" : "抗战胜利日", "0910" : "教师节", + "1001" : "*3国庆节", "1201" : "艾滋病日", + "1224" : "平安夜", "1225" : "圣诞节" + }, + sFtv2 = { + "0100" : "除夕", "0101" : "*2春节", + "0115" : "元宵节", "0505" : "*1端午节", + "0707" : "七夕节", "0715" : "中元节", + "0815" : "*1中秋节", "0909" : "*1重阳节", + "1015" : "下元节", "1208" : "腊八节", + "1223" : "小年" + + }; + function flunar(Y) { + var sTerm = function (j, i) { + var h = new Date((31556925974.7 * (j - 1900) + sTermInfo[i] * 60000) + Date.UTC(1900, 0, 6, 2, 5)); + return (h.getUTCDate()); + }, + d = function (k) { + var h, j = 348; + for (h = 32768; h > 8; h >>= 1) j += (lunarInfo[k - 1900] & h) ? 1 : 0; + return (j + b(k)); + }, + ymdCyl = function (h) { + return (Gan.charAt(h % 10) + Zhi.charAt(h % 12)); + }, + b = function (h) { + var islp = (g(h)) ? ((lunarInfo[h - 1900] & 65536) ? 30 : 29) : (0); + return islp; + }, + g = function (h) { + return (lunarInfo[h - 1900] & 15) + }, + e = function (i, h) { + return ((lunarInfo[i - 1900] & (65536 >> h)) ? 30 : 29); + }, + newymd = function (m) { + var k, j = 0, h = 0, l = new Date(1900, 0, 31), n = (m - l) / 86400000; + this.dayCyl = n + 40; + this.monCyl = 14; + for (k = 1900; k < 2050 && n > 0; k++) { + h = d(k); n -= h; + this.monCyl += 12; + } + if (n < 0) { + n += h; k--; + this.monCyl -= 12; + } + this.year = k; + this.yearCyl = k - 1864; + j = g(k); + this.isLeap = false; + for (k = 1; k < 13 && n > 0; k++) { + if (j > 0 && k == (j + 1) && this.isLeap == false) { + --k; + this.isLeap = true; + h = b(this.year); + } else { + h = e(this.year, k); + } + if (this.isLeap == true && k == (j + 1)) { + this.isLeap = false; + } + n -= h; + if (this.isLeap == false) this.monCyl++; + } + if (n == 0 && j > 0 && k == j + 1) { + if (this.isLeap) { + this.isLeap = false; + } else { + this.isLeap = true; + --k; + --this.monCyl; + } + } + if (n < 0) { + n += h; --k; + --this.monCyl; + } + this.month = k; + this.day = n + 1; + }, + digit = function (num) { + return num < 10 ? "0" + (num | 0) : num; + }, + reymd = function (i, j) { + var h = i; + return j.replace(/dd?d?d?|MM?M?M?|yy?y?y?/g, function (k) { + switch (k) { + case "yyyy": + var l = "000" + h.getFullYear(); + return l.substring(l.length - 4); + case "dd": return digit(h.getDate()); + case "d": return h.getDate().toString(); + case "MM": return digit((h.getMonth() + 1)); + case "M": return h.getMonth() + 1; + } + }); + }, + lunarMD = function (i, h) { + var j; + switch (i, h) { + case 10: j = "初十"; break; + case 20: j = "二十"; break; + case 30: j = "三十"; break; + default: + j = nStr2.charAt(Math.floor(h / 10)); + j += nStr1.charAt(h % 10); + } + return j; + }; + this.isToday = false; + this.isRestDay = false; + this.solarYear = reymd(Y, "yyyy"); + this.solarMonth = reymd(Y, "M"); + this.solarDate = reymd(Y, "d"); + this.solarWeekDay = Y.getDay(); + this.inWeekDays = "星期" + nStr1.charAt(this.solarWeekDay); + var X = new newymd(Y); + this.lunarYear = X.year; + this.shengxiao = Animals.charAt((this.lunarYear - 4) % 12); + this.lunarMonth = X.month; + this.lunarIsLeapMonth = X.isLeap; + this.lnongMonth = this.lunarIsLeapMonth ? "闰" + nStr3[X.month - 1] : nStr3[X.month - 1]; + this.lunarDate = X.day; + this.showInLunar = this.lnongDate = lunarMD(this.lunarMonth, this.lunarDate); + if (this.lunarDate == 1) { + this.showInLunar = this.lnongMonth + "月"; + } + this.ganzhiYear = ymdCyl(X.yearCyl); + this.ganzhiMonth = ymdCyl(X.monCyl); + this.ganzhiDate = ymdCyl(X.dayCyl++); + this.jieqi = ""; + this.restDays = 0; + if (sTerm(this.solarYear, (this.solarMonth - 1) * 2) == reymd(Y, "d")) { + this.showInLunar = this.jieqi = solarTerm[(this.solarMonth - 1) * 2]; + } + if (sTerm(this.solarYear, (this.solarMonth - 1) * 2 + 1) == reymd(Y, "d")) { + this.showInLunar = this.jieqi = solarTerm[(this.solarMonth - 1) * 2 + 1]; + } + if (this.showInLunar == "清明") { + this.showInLunar = "清明节"; + this.restDays = 1; + } + this.solarFestival = sFtv1[reymd(Y, "MM") + reymd(Y, "dd")]; + if (typeof this.solarFestival == "undefined") { + this.solarFestival = ""; + } else { + if (/\*(\d)/.test(this.solarFestival)) { + this.restDays = parseInt(RegExp.$1); + this.solarFestival = this.solarFestival.replace(/\*\d/, ""); + } + } + this.showInLunar = (this.solarFestival == "") ? this.showInLunar : this.solarFestival; + this.lunarFestival = sFtv2[this.lunarIsLeapMonth ? "00" : digit(this.lunarMonth) + digit(this.lunarDate)]; + if (typeof this.lunarFestival == "undefined") { + this.lunarFestival = ""; + } else { + if (/\*(\d)/.test(this.lunarFestival)) { + this.restDays = (this.restDays > parseInt(RegExp.$1)) ? this.restDays : parseInt(RegExp.$1); + this.lunarFestival = this.lunarFestival.replace(/\*\d/, ""); + } + } + if (this.lunarMonth == 12 && this.lunarDate == e(this.lunarYear, 12)) { + this.lunarFestival = sFtv2["0100"]; + this.restDays = 1; + } + this.showInLunar = (this.lunarFestival == "") ? this.showInLunar : this.lunarFestival; + } + return new flunar(new Date(ly, lm, ld)); + } + return jeDate; +}); diff --git a/main/webapp/static/js/jquery-1.12.4.js b/main/webapp/static/js/jquery-1.12.4.js new file mode 100644 index 0000000..7fc60fc --- /dev/null +++ b/main/webapp/static/js/jquery-1.12.4.js @@ -0,0 +1,11008 @@ +/*! + * jQuery JavaScript Library v1.12.4 + * http://jquery.com/ + * + * Includes Sizzle.js + * http://sizzlejs.com/ + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license + * http://jquery.org/license + * + * Date: 2016-05-20T17:17Z + */ + +(function( global, factory ) { + + if ( typeof module === "object" && typeof module.exports === "object" ) { + // For CommonJS and CommonJS-like environments where a proper `window` + // is present, execute the factory and get jQuery. + // For environments that do not have a `window` with a `document` + // (such as Node.js), expose a factory as module.exports. + // This accentuates the need for the creation of a real `window`. + // e.g. var jQuery = require("jquery")(window); + // See ticket #14549 for more info. + module.exports = global.document ? + factory( global, true ) : + function( w ) { + if ( !w.document ) { + throw new Error( "jQuery requires a window with a document" ); + } + return factory( w ); + }; + } else { + factory( global ); + } + +// Pass this if window is not defined yet +}(typeof window !== "undefined" ? window : this, function( window, noGlobal ) { + +// Support: Firefox 18+ +// Can't be in strict mode, several libs including ASP.NET trace +// the stack via arguments.caller.callee and Firefox dies if +// you try to trace through "use strict" call chains. (#13335) +//"use strict"; +var deletedIds = []; + +var document = window.document; + +var slice = deletedIds.slice; + +var concat = deletedIds.concat; + +var push = deletedIds.push; + +var indexOf = deletedIds.indexOf; + +var class2type = {}; + +var toString = class2type.toString; + +var hasOwn = class2type.hasOwnProperty; + +var support = {}; + + + +var + version = "1.12.4", + + // Define a local copy of jQuery + jQuery = function( selector, context ) { + + // The jQuery object is actually just the init constructor 'enhanced' + // Need init if jQuery is called (just allow error to be thrown if not included) + return new jQuery.fn.init( selector, context ); + }, + + // Support: Android<4.1, IE<9 + // Make sure we trim BOM and NBSP + rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, + + // Matches dashed string for camelizing + rmsPrefix = /^-ms-/, + rdashAlpha = /-([\da-z])/gi, + + // Used by jQuery.camelCase as callback to replace() + fcamelCase = function( all, letter ) { + return letter.toUpperCase(); + }; + +jQuery.fn = jQuery.prototype = { + + // The current version of jQuery being used + jquery: version, + + constructor: jQuery, + + // Start with an empty selector + selector: "", + + // The default length of a jQuery object is 0 + length: 0, + + toArray: function() { + return slice.call( this ); + }, + + // Get the Nth element in the matched element set OR + // Get the whole matched element set as a clean array + get: function( num ) { + return num != null ? + + // Return just the one element from the set + ( num < 0 ? this[ num + this.length ] : this[ num ] ) : + + // Return all the elements in a clean array + slice.call( this ); + }, + + // Take an array of elements and push it onto the stack + // (returning the new matched element set) + pushStack: function( elems ) { + + // Build a new jQuery matched element set + var ret = jQuery.merge( this.constructor(), elems ); + + // Add the old object onto the stack (as a reference) + ret.prevObject = this; + ret.context = this.context; + + // Return the newly-formed element set + return ret; + }, + + // Execute a callback for every element in the matched set. + each: function( callback ) { + return jQuery.each( this, callback ); + }, + + map: function( callback ) { + return this.pushStack( jQuery.map( this, function( elem, i ) { + return callback.call( elem, i, elem ); + } ) ); + }, + + slice: function() { + return this.pushStack( slice.apply( this, arguments ) ); + }, + + first: function() { + return this.eq( 0 ); + }, + + last: function() { + return this.eq( -1 ); + }, + + eq: function( i ) { + var len = this.length, + j = +i + ( i < 0 ? len : 0 ); + return this.pushStack( j >= 0 && j < len ? [ this[ j ] ] : [] ); + }, + + end: function() { + return this.prevObject || this.constructor(); + }, + + // For internal use only. + // Behaves like an Array's method, not like a jQuery method. + push: push, + sort: deletedIds.sort, + splice: deletedIds.splice +}; + +jQuery.extend = jQuery.fn.extend = function() { + var src, copyIsArray, copy, name, options, clone, + target = arguments[ 0 ] || {}, + i = 1, + length = arguments.length, + deep = false; + + // Handle a deep copy situation + if ( typeof target === "boolean" ) { + deep = target; + + // skip the boolean and the target + target = arguments[ i ] || {}; + i++; + } + + // Handle case when target is a string or something (possible in deep copy) + if ( typeof target !== "object" && !jQuery.isFunction( target ) ) { + target = {}; + } + + // extend jQuery itself if only one argument is passed + if ( i === length ) { + target = this; + i--; + } + + for ( ; i < length; i++ ) { + + // Only deal with non-null/undefined values + if ( ( options = arguments[ i ] ) != null ) { + + // Extend the base object + for ( name in options ) { + src = target[ name ]; + copy = options[ name ]; + + // Prevent never-ending loop + if ( target === copy ) { + continue; + } + + // Recurse if we're merging plain objects or arrays + if ( deep && copy && ( jQuery.isPlainObject( copy ) || + ( copyIsArray = jQuery.isArray( copy ) ) ) ) { + + if ( copyIsArray ) { + copyIsArray = false; + clone = src && jQuery.isArray( src ) ? src : []; + + } else { + clone = src && jQuery.isPlainObject( src ) ? src : {}; + } + + // Never move original objects, clone them + target[ name ] = jQuery.extend( deep, clone, copy ); + + // Don't bring in undefined values + } else if ( copy !== undefined ) { + target[ name ] = copy; + } + } + } + } + + // Return the modified object + return target; +}; + +jQuery.extend( { + + // Unique for each copy of jQuery on the page + expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ), + + // Assume jQuery is ready without the ready module + isReady: true, + + error: function( msg ) { + throw new Error( msg ); + }, + + noop: function() {}, + + // See test/unit/core.js for details concerning isFunction. + // Since version 1.3, DOM methods and functions like alert + // aren't supported. They return false on IE (#2968). + isFunction: function( obj ) { + return jQuery.type( obj ) === "function"; + }, + + isArray: Array.isArray || function( obj ) { + return jQuery.type( obj ) === "array"; + }, + + isWindow: function( obj ) { + /* jshint eqeqeq: false */ + return obj != null && obj == obj.window; + }, + + isNumeric: function( obj ) { + + // parseFloat NaNs numeric-cast false positives (null|true|false|"") + // ...but misinterprets leading-number strings, particularly hex literals ("0x...") + // subtraction forces infinities to NaN + // adding 1 corrects loss of precision from parseFloat (#15100) + var realStringObj = obj && obj.toString(); + return !jQuery.isArray( obj ) && ( realStringObj - parseFloat( realStringObj ) + 1 ) >= 0; + }, + + isEmptyObject: function( obj ) { + var name; + for ( name in obj ) { + return false; + } + return true; + }, + + isPlainObject: function( obj ) { + var key; + + // Must be an Object. + // Because of IE, we also have to check the presence of the constructor property. + // Make sure that DOM nodes and window objects don't pass through, as well + if ( !obj || jQuery.type( obj ) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) { + return false; + } + + try { + + // Not own constructor property must be Object + if ( obj.constructor && + !hasOwn.call( obj, "constructor" ) && + !hasOwn.call( obj.constructor.prototype, "isPrototypeOf" ) ) { + return false; + } + } catch ( e ) { + + // IE8,9 Will throw exceptions on certain host objects #9897 + return false; + } + + // Support: IE<9 + // Handle iteration over inherited properties before own properties. + if ( !support.ownFirst ) { + for ( key in obj ) { + return hasOwn.call( obj, key ); + } + } + + // Own properties are enumerated firstly, so to speed up, + // if last one is own, then all properties are own. + for ( key in obj ) {} + + return key === undefined || hasOwn.call( obj, key ); + }, + + type: function( obj ) { + if ( obj == null ) { + return obj + ""; + } + return typeof obj === "object" || typeof obj === "function" ? + class2type[ toString.call( obj ) ] || "object" : + typeof obj; + }, + + // Workarounds based on findings by Jim Driscoll + // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context + globalEval: function( data ) { + if ( data && jQuery.trim( data ) ) { + + // We use execScript on Internet Explorer + // We use an anonymous function so that context is window + // rather than jQuery in Firefox + ( window.execScript || function( data ) { + window[ "eval" ].call( window, data ); // jscs:ignore requireDotNotation + } )( data ); + } + }, + + // Convert dashed to camelCase; used by the css and data modules + // Microsoft forgot to hump their vendor prefix (#9572) + camelCase: function( string ) { + return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); + }, + + nodeName: function( elem, name ) { + return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); + }, + + each: function( obj, callback ) { + var length, i = 0; + + if ( isArrayLike( obj ) ) { + length = obj.length; + for ( ; i < length; i++ ) { + if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { + break; + } + } + } else { + for ( i in obj ) { + if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { + break; + } + } + } + + return obj; + }, + + // Support: Android<4.1, IE<9 + trim: function( text ) { + return text == null ? + "" : + ( text + "" ).replace( rtrim, "" ); + }, + + // results is for internal usage only + makeArray: function( arr, results ) { + var ret = results || []; + + if ( arr != null ) { + if ( isArrayLike( Object( arr ) ) ) { + jQuery.merge( ret, + typeof arr === "string" ? + [ arr ] : arr + ); + } else { + push.call( ret, arr ); + } + } + + return ret; + }, + + inArray: function( elem, arr, i ) { + var len; + + if ( arr ) { + if ( indexOf ) { + return indexOf.call( arr, elem, i ); + } + + len = arr.length; + i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0; + + for ( ; i < len; i++ ) { + + // Skip accessing in sparse arrays + if ( i in arr && arr[ i ] === elem ) { + return i; + } + } + } + + return -1; + }, + + merge: function( first, second ) { + var len = +second.length, + j = 0, + i = first.length; + + while ( j < len ) { + first[ i++ ] = second[ j++ ]; + } + + // Support: IE<9 + // Workaround casting of .length to NaN on otherwise arraylike objects (e.g., NodeLists) + if ( len !== len ) { + while ( second[ j ] !== undefined ) { + first[ i++ ] = second[ j++ ]; + } + } + + first.length = i; + + return first; + }, + + grep: function( elems, callback, invert ) { + var callbackInverse, + matches = [], + i = 0, + length = elems.length, + callbackExpect = !invert; + + // Go through the array, only saving the items + // that pass the validator function + for ( ; i < length; i++ ) { + callbackInverse = !callback( elems[ i ], i ); + if ( callbackInverse !== callbackExpect ) { + matches.push( elems[ i ] ); + } + } + + return matches; + }, + + // arg is for internal usage only + map: function( elems, callback, arg ) { + var length, value, + i = 0, + ret = []; + + // Go through the array, translating each of the items to their new values + if ( isArrayLike( elems ) ) { + length = elems.length; + for ( ; i < length; i++ ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + + // Go through every key on the object, + } else { + for ( i in elems ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + } + + // Flatten any nested arrays + return concat.apply( [], ret ); + }, + + // A global GUID counter for objects + guid: 1, + + // Bind a function to a context, optionally partially applying any + // arguments. + proxy: function( fn, context ) { + var args, proxy, tmp; + + if ( typeof context === "string" ) { + tmp = fn[ context ]; + context = fn; + fn = tmp; + } + + // Quick check to determine if target is callable, in the spec + // this throws a TypeError, but we will just return undefined. + if ( !jQuery.isFunction( fn ) ) { + return undefined; + } + + // Simulated bind + args = slice.call( arguments, 2 ); + proxy = function() { + return fn.apply( context || this, args.concat( slice.call( arguments ) ) ); + }; + + // Set the guid of unique handler to the same of original handler, so it can be removed + proxy.guid = fn.guid = fn.guid || jQuery.guid++; + + return proxy; + }, + + now: function() { + return +( new Date() ); + }, + + // jQuery.support is not used in Core but other projects attach their + // properties to it so it needs to exist. + support: support +} ); + +// JSHint would error on this code due to the Symbol not being defined in ES5. +// Defining this global in .jshintrc would create a danger of using the global +// unguarded in another place, it seems safer to just disable JSHint for these +// three lines. +/* jshint ignore: start */ +if ( typeof Symbol === "function" ) { + jQuery.fn[ Symbol.iterator ] = deletedIds[ Symbol.iterator ]; +} +/* jshint ignore: end */ + +// Populate the class2type map +jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol".split( " " ), +function( i, name ) { + class2type[ "[object " + name + "]" ] = name.toLowerCase(); +} ); + +function isArrayLike( obj ) { + + // Support: iOS 8.2 (not reproducible in simulator) + // `in` check used to prevent JIT error (gh-2145) + // hasOwn isn't used here due to false negatives + // regarding Nodelist length in IE + var length = !!obj && "length" in obj && obj.length, + type = jQuery.type( obj ); + + if ( type === "function" || jQuery.isWindow( obj ) ) { + return false; + } + + return type === "array" || length === 0 || + typeof length === "number" && length > 0 && ( length - 1 ) in obj; +} +var Sizzle = +/*! + * Sizzle CSS Selector Engine v2.2.1 + * http://sizzlejs.com/ + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license + * http://jquery.org/license + * + * Date: 2015-10-17 + */ +(function( window ) { + +var i, + support, + Expr, + getText, + isXML, + tokenize, + compile, + select, + outermostContext, + sortInput, + hasDuplicate, + + // Local document vars + setDocument, + document, + docElem, + documentIsHTML, + rbuggyQSA, + rbuggyMatches, + matches, + contains, + + // Instance-specific data + expando = "sizzle" + 1 * new Date(), + preferredDoc = window.document, + dirruns = 0, + done = 0, + classCache = createCache(), + tokenCache = createCache(), + compilerCache = createCache(), + sortOrder = function( a, b ) { + if ( a === b ) { + hasDuplicate = true; + } + return 0; + }, + + // General-purpose constants + MAX_NEGATIVE = 1 << 31, + + // Instance methods + hasOwn = ({}).hasOwnProperty, + arr = [], + pop = arr.pop, + push_native = arr.push, + push = arr.push, + slice = arr.slice, + // Use a stripped-down indexOf as it's faster than native + // http://jsperf.com/thor-indexof-vs-for/5 + indexOf = function( list, elem ) { + var i = 0, + len = list.length; + for ( ; i < len; i++ ) { + if ( list[i] === elem ) { + return i; + } + } + return -1; + }, + + booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped", + + // Regular expressions + + // http://www.w3.org/TR/css3-selectors/#whitespace + whitespace = "[\\x20\\t\\r\\n\\f]", + + // http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier + identifier = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+", + + // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors + attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace + + // Operator (capture 2) + "*([*^$|!~]?=)" + whitespace + + // "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]" + "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace + + "*\\]", + + pseudos = ":(" + identifier + ")(?:\\((" + + // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments: + // 1. quoted (capture 3; capture 4 or capture 5) + "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" + + // 2. simple (capture 6) + "((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" + + // 3. anything else (capture 2) + ".*" + + ")\\)|)", + + // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter + rwhitespace = new RegExp( whitespace + "+", "g" ), + rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ), + + rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), + rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ), + + rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*?)" + whitespace + "*\\]", "g" ), + + rpseudo = new RegExp( pseudos ), + ridentifier = new RegExp( "^" + identifier + "$" ), + + matchExpr = { + "ID": new RegExp( "^#(" + identifier + ")" ), + "CLASS": new RegExp( "^\\.(" + identifier + ")" ), + "TAG": new RegExp( "^(" + identifier + "|[*])" ), + "ATTR": new RegExp( "^" + attributes ), + "PSEUDO": new RegExp( "^" + pseudos ), + "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace + + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + + "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), + "bool": new RegExp( "^(?:" + booleans + ")$", "i" ), + // For use in libraries implementing .is() + // We use this for POS matching in `select` + "needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + + whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" ) + }, + + rinputs = /^(?:input|select|textarea|button)$/i, + rheader = /^h\d$/i, + + rnative = /^[^{]+\{\s*\[native \w/, + + // Easily-parseable/retrievable ID or TAG or CLASS selectors + rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, + + rsibling = /[+~]/, + rescape = /'|\\/g, + + // CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters + runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ), + funescape = function( _, escaped, escapedWhitespace ) { + var high = "0x" + escaped - 0x10000; + // NaN means non-codepoint + // Support: Firefox<24 + // Workaround erroneous numeric interpretation of +"0x" + return high !== high || escapedWhitespace ? + escaped : + high < 0 ? + // BMP codepoint + String.fromCharCode( high + 0x10000 ) : + // Supplemental Plane codepoint (surrogate pair) + String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); + }, + + // Used for iframes + // See setDocument() + // Removing the function wrapper causes a "Permission Denied" + // error in IE + unloadHandler = function() { + setDocument(); + }; + +// Optimize for push.apply( _, NodeList ) +try { + push.apply( + (arr = slice.call( preferredDoc.childNodes )), + preferredDoc.childNodes + ); + // Support: Android<4.0 + // Detect silently failing push.apply + arr[ preferredDoc.childNodes.length ].nodeType; +} catch ( e ) { + push = { apply: arr.length ? + + // Leverage slice if possible + function( target, els ) { + push_native.apply( target, slice.call(els) ); + } : + + // Support: IE<9 + // Otherwise append directly + function( target, els ) { + var j = target.length, + i = 0; + // Can't trust NodeList.length + while ( (target[j++] = els[i++]) ) {} + target.length = j - 1; + } + }; +} + +function Sizzle( selector, context, results, seed ) { + var m, i, elem, nid, nidselect, match, groups, newSelector, + newContext = context && context.ownerDocument, + + // nodeType defaults to 9, since context defaults to document + nodeType = context ? context.nodeType : 9; + + results = results || []; + + // Return early from calls with invalid selector or context + if ( typeof selector !== "string" || !selector || + nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) { + + return results; + } + + // Try to shortcut find operations (as opposed to filters) in HTML documents + if ( !seed ) { + + if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) { + setDocument( context ); + } + context = context || document; + + if ( documentIsHTML ) { + + // If the selector is sufficiently simple, try using a "get*By*" DOM method + // (excepting DocumentFragment context, where the methods don't exist) + if ( nodeType !== 11 && (match = rquickExpr.exec( selector )) ) { + + // ID selector + if ( (m = match[1]) ) { + + // Document context + if ( nodeType === 9 ) { + if ( (elem = context.getElementById( m )) ) { + + // Support: IE, Opera, Webkit + // TODO: identify versions + // getElementById can match elements by name instead of ID + if ( elem.id === m ) { + results.push( elem ); + return results; + } + } else { + return results; + } + + // Element context + } else { + + // Support: IE, Opera, Webkit + // TODO: identify versions + // getElementById can match elements by name instead of ID + if ( newContext && (elem = newContext.getElementById( m )) && + contains( context, elem ) && + elem.id === m ) { + + results.push( elem ); + return results; + } + } + + // Type selector + } else if ( match[2] ) { + push.apply( results, context.getElementsByTagName( selector ) ); + return results; + + // Class selector + } else if ( (m = match[3]) && support.getElementsByClassName && + context.getElementsByClassName ) { + + push.apply( results, context.getElementsByClassName( m ) ); + return results; + } + } + + // Take advantage of querySelectorAll + if ( support.qsa && + !compilerCache[ selector + " " ] && + (!rbuggyQSA || !rbuggyQSA.test( selector )) ) { + + if ( nodeType !== 1 ) { + newContext = context; + newSelector = selector; + + // qSA looks outside Element context, which is not what we want + // Thanks to Andrew Dupont for this workaround technique + // Support: IE <=8 + // Exclude object elements + } else if ( context.nodeName.toLowerCase() !== "object" ) { + + // Capture the context ID, setting it first if necessary + if ( (nid = context.getAttribute( "id" )) ) { + nid = nid.replace( rescape, "\\$&" ); + } else { + context.setAttribute( "id", (nid = expando) ); + } + + // Prefix every selector in the list + groups = tokenize( selector ); + i = groups.length; + nidselect = ridentifier.test( nid ) ? "#" + nid : "[id='" + nid + "']"; + while ( i-- ) { + groups[i] = nidselect + " " + toSelector( groups[i] ); + } + newSelector = groups.join( "," ); + + // Expand context for sibling selectors + newContext = rsibling.test( selector ) && testContext( context.parentNode ) || + context; + } + + if ( newSelector ) { + try { + push.apply( results, + newContext.querySelectorAll( newSelector ) + ); + return results; + } catch ( qsaError ) { + } finally { + if ( nid === expando ) { + context.removeAttribute( "id" ); + } + } + } + } + } + } + + // All others + return select( selector.replace( rtrim, "$1" ), context, results, seed ); +} + +/** + * Create key-value caches of limited size + * @returns {function(string, object)} Returns the Object data after storing it on itself with + * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) + * deleting the oldest entry + */ +function createCache() { + var keys = []; + + function cache( key, value ) { + // Use (key + " ") to avoid collision with native prototype properties (see Issue #157) + if ( keys.push( key + " " ) > Expr.cacheLength ) { + // Only keep the most recent entries + delete cache[ keys.shift() ]; + } + return (cache[ key + " " ] = value); + } + return cache; +} + +/** + * Mark a function for special use by Sizzle + * @param {Function} fn The function to mark + */ +function markFunction( fn ) { + fn[ expando ] = true; + return fn; +} + +/** + * Support testing using an element + * @param {Function} fn Passed the created div and expects a boolean result + */ +function assert( fn ) { + var div = document.createElement("div"); + + try { + return !!fn( div ); + } catch (e) { + return false; + } finally { + // Remove from its parent by default + if ( div.parentNode ) { + div.parentNode.removeChild( div ); + } + // release memory in IE + div = null; + } +} + +/** + * Adds the same handler for all of the specified attrs + * @param {String} attrs Pipe-separated list of attributes + * @param {Function} handler The method that will be applied + */ +function addHandle( attrs, handler ) { + var arr = attrs.split("|"), + i = arr.length; + + while ( i-- ) { + Expr.attrHandle[ arr[i] ] = handler; + } +} + +/** + * Checks document order of two siblings + * @param {Element} a + * @param {Element} b + * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b + */ +function siblingCheck( a, b ) { + var cur = b && a, + diff = cur && a.nodeType === 1 && b.nodeType === 1 && + ( ~b.sourceIndex || MAX_NEGATIVE ) - + ( ~a.sourceIndex || MAX_NEGATIVE ); + + // Use IE sourceIndex if available on both nodes + if ( diff ) { + return diff; + } + + // Check if b follows a + if ( cur ) { + while ( (cur = cur.nextSibling) ) { + if ( cur === b ) { + return -1; + } + } + } + + return a ? 1 : -1; +} + +/** + * Returns a function to use in pseudos for input types + * @param {String} type + */ +function createInputPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for buttons + * @param {String} type + */ +function createButtonPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return (name === "input" || name === "button") && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for positionals + * @param {Function} fn + */ +function createPositionalPseudo( fn ) { + return markFunction(function( argument ) { + argument = +argument; + return markFunction(function( seed, matches ) { + var j, + matchIndexes = fn( [], seed.length, argument ), + i = matchIndexes.length; + + // Match elements found at the specified indexes + while ( i-- ) { + if ( seed[ (j = matchIndexes[i]) ] ) { + seed[j] = !(matches[j] = seed[j]); + } + } + }); + }); +} + +/** + * Checks a node for validity as a Sizzle context + * @param {Element|Object=} context + * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value + */ +function testContext( context ) { + return context && typeof context.getElementsByTagName !== "undefined" && context; +} + +// Expose support vars for convenience +support = Sizzle.support = {}; + +/** + * Detects XML nodes + * @param {Element|Object} elem An element or a document + * @returns {Boolean} True iff elem is a non-HTML XML node + */ +isXML = Sizzle.isXML = function( elem ) { + // documentElement is verified for cases where it doesn't yet exist + // (such as loading iframes in IE - #4833) + var documentElement = elem && (elem.ownerDocument || elem).documentElement; + return documentElement ? documentElement.nodeName !== "HTML" : false; +}; + +/** + * Sets document-related variables once based on the current document + * @param {Element|Object} [doc] An element or document object to use to set the document + * @returns {Object} Returns the current document + */ +setDocument = Sizzle.setDocument = function( node ) { + var hasCompare, parent, + doc = node ? node.ownerDocument || node : preferredDoc; + + // Return early if doc is invalid or already selected + if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) { + return document; + } + + // Update global variables + document = doc; + docElem = document.documentElement; + documentIsHTML = !isXML( document ); + + // Support: IE 9-11, Edge + // Accessing iframe documents after unload throws "permission denied" errors (jQuery #13936) + if ( (parent = document.defaultView) && parent.top !== parent ) { + // Support: IE 11 + if ( parent.addEventListener ) { + parent.addEventListener( "unload", unloadHandler, false ); + + // Support: IE 9 - 10 only + } else if ( parent.attachEvent ) { + parent.attachEvent( "onunload", unloadHandler ); + } + } + + /* Attributes + ---------------------------------------------------------------------- */ + + // Support: IE<8 + // Verify that getAttribute really returns attributes and not properties + // (excepting IE8 booleans) + support.attributes = assert(function( div ) { + div.className = "i"; + return !div.getAttribute("className"); + }); + + /* getElement(s)By* + ---------------------------------------------------------------------- */ + + // Check if getElementsByTagName("*") returns only elements + support.getElementsByTagName = assert(function( div ) { + div.appendChild( document.createComment("") ); + return !div.getElementsByTagName("*").length; + }); + + // Support: IE<9 + support.getElementsByClassName = rnative.test( document.getElementsByClassName ); + + // Support: IE<10 + // Check if getElementById returns elements by name + // The broken getElementById methods don't pick up programatically-set names, + // so use a roundabout getElementsByName test + support.getById = assert(function( div ) { + docElem.appendChild( div ).id = expando; + return !document.getElementsByName || !document.getElementsByName( expando ).length; + }); + + // ID find and filter + if ( support.getById ) { + Expr.find["ID"] = function( id, context ) { + if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { + var m = context.getElementById( id ); + return m ? [ m ] : []; + } + }; + Expr.filter["ID"] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + return elem.getAttribute("id") === attrId; + }; + }; + } else { + // Support: IE6/7 + // getElementById is not reliable as a find shortcut + delete Expr.find["ID"]; + + Expr.filter["ID"] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + var node = typeof elem.getAttributeNode !== "undefined" && + elem.getAttributeNode("id"); + return node && node.value === attrId; + }; + }; + } + + // Tag + Expr.find["TAG"] = support.getElementsByTagName ? + function( tag, context ) { + if ( typeof context.getElementsByTagName !== "undefined" ) { + return context.getElementsByTagName( tag ); + + // DocumentFragment nodes don't have gEBTN + } else if ( support.qsa ) { + return context.querySelectorAll( tag ); + } + } : + + function( tag, context ) { + var elem, + tmp = [], + i = 0, + // By happy coincidence, a (broken) gEBTN appears on DocumentFragment nodes too + results = context.getElementsByTagName( tag ); + + // Filter out possible comments + if ( tag === "*" ) { + while ( (elem = results[i++]) ) { + if ( elem.nodeType === 1 ) { + tmp.push( elem ); + } + } + + return tmp; + } + return results; + }; + + // Class + Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) { + if ( typeof context.getElementsByClassName !== "undefined" && documentIsHTML ) { + return context.getElementsByClassName( className ); + } + }; + + /* QSA/matchesSelector + ---------------------------------------------------------------------- */ + + // QSA and matchesSelector support + + // matchesSelector(:active) reports false when true (IE9/Opera 11.5) + rbuggyMatches = []; + + // qSa(:focus) reports false when true (Chrome 21) + // We allow this because of a bug in IE8/9 that throws an error + // whenever `document.activeElement` is accessed on an iframe + // So, we allow :focus to pass through QSA all the time to avoid the IE error + // See http://bugs.jquery.com/ticket/13378 + rbuggyQSA = []; + + if ( (support.qsa = rnative.test( document.querySelectorAll )) ) { + // Build QSA regex + // Regex strategy adopted from Diego Perini + assert(function( div ) { + // Select is set to empty string on purpose + // This is to test IE's treatment of not explicitly + // setting a boolean content attribute, + // since its presence should be enough + // http://bugs.jquery.com/ticket/12359 + docElem.appendChild( div ).innerHTML = "" + + ""; + + // Support: IE8, Opera 11-12.16 + // Nothing should be selected when empty strings follow ^= or $= or *= + // The test attribute must be unknown in Opera but "safe" for WinRT + // http://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section + if ( div.querySelectorAll("[msallowcapture^='']").length ) { + rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" ); + } + + // Support: IE8 + // Boolean attributes and "value" are not treated correctly + if ( !div.querySelectorAll("[selected]").length ) { + rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); + } + + // Support: Chrome<29, Android<4.4, Safari<7.0+, iOS<7.0+, PhantomJS<1.9.8+ + if ( !div.querySelectorAll( "[id~=" + expando + "-]" ).length ) { + rbuggyQSA.push("~="); + } + + // Webkit/Opera - :checked should return selected option elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + // IE8 throws error here and will not see later tests + if ( !div.querySelectorAll(":checked").length ) { + rbuggyQSA.push(":checked"); + } + + // Support: Safari 8+, iOS 8+ + // https://bugs.webkit.org/show_bug.cgi?id=136851 + // In-page `selector#id sibing-combinator selector` fails + if ( !div.querySelectorAll( "a#" + expando + "+*" ).length ) { + rbuggyQSA.push(".#.+[+~]"); + } + }); + + assert(function( div ) { + // Support: Windows 8 Native Apps + // The type and name attributes are restricted during .innerHTML assignment + var input = document.createElement("input"); + input.setAttribute( "type", "hidden" ); + div.appendChild( input ).setAttribute( "name", "D" ); + + // Support: IE8 + // Enforce case-sensitivity of name attribute + if ( div.querySelectorAll("[name=d]").length ) { + rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" ); + } + + // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) + // IE8 throws error here and will not see later tests + if ( !div.querySelectorAll(":enabled").length ) { + rbuggyQSA.push( ":enabled", ":disabled" ); + } + + // Opera 10-11 does not throw on post-comma invalid pseudos + div.querySelectorAll("*,:x"); + rbuggyQSA.push(",.*:"); + }); + } + + if ( (support.matchesSelector = rnative.test( (matches = docElem.matches || + docElem.webkitMatchesSelector || + docElem.mozMatchesSelector || + docElem.oMatchesSelector || + docElem.msMatchesSelector) )) ) { + + assert(function( div ) { + // Check to see if it's possible to do matchesSelector + // on a disconnected node (IE 9) + support.disconnectedMatch = matches.call( div, "div" ); + + // This should fail with an exception + // Gecko does not error, returns false instead + matches.call( div, "[s!='']:x" ); + rbuggyMatches.push( "!=", pseudos ); + }); + } + + rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") ); + rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") ); + + /* Contains + ---------------------------------------------------------------------- */ + hasCompare = rnative.test( docElem.compareDocumentPosition ); + + // Element contains another + // Purposefully self-exclusive + // As in, an element does not contain itself + contains = hasCompare || rnative.test( docElem.contains ) ? + function( a, b ) { + var adown = a.nodeType === 9 ? a.documentElement : a, + bup = b && b.parentNode; + return a === bup || !!( bup && bup.nodeType === 1 && ( + adown.contains ? + adown.contains( bup ) : + a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16 + )); + } : + function( a, b ) { + if ( b ) { + while ( (b = b.parentNode) ) { + if ( b === a ) { + return true; + } + } + } + return false; + }; + + /* Sorting + ---------------------------------------------------------------------- */ + + // Document order sorting + sortOrder = hasCompare ? + function( a, b ) { + + // Flag for duplicate removal + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + // Sort on method existence if only one input has compareDocumentPosition + var compare = !a.compareDocumentPosition - !b.compareDocumentPosition; + if ( compare ) { + return compare; + } + + // Calculate position if both inputs belong to the same document + compare = ( a.ownerDocument || a ) === ( b.ownerDocument || b ) ? + a.compareDocumentPosition( b ) : + + // Otherwise we know they are disconnected + 1; + + // Disconnected nodes + if ( compare & 1 || + (!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) { + + // Choose the first element that is related to our preferred document + if ( a === document || a.ownerDocument === preferredDoc && contains(preferredDoc, a) ) { + return -1; + } + if ( b === document || b.ownerDocument === preferredDoc && contains(preferredDoc, b) ) { + return 1; + } + + // Maintain original order + return sortInput ? + ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : + 0; + } + + return compare & 4 ? -1 : 1; + } : + function( a, b ) { + // Exit early if the nodes are identical + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + var cur, + i = 0, + aup = a.parentNode, + bup = b.parentNode, + ap = [ a ], + bp = [ b ]; + + // Parentless nodes are either documents or disconnected + if ( !aup || !bup ) { + return a === document ? -1 : + b === document ? 1 : + aup ? -1 : + bup ? 1 : + sortInput ? + ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : + 0; + + // If the nodes are siblings, we can do a quick check + } else if ( aup === bup ) { + return siblingCheck( a, b ); + } + + // Otherwise we need full lists of their ancestors for comparison + cur = a; + while ( (cur = cur.parentNode) ) { + ap.unshift( cur ); + } + cur = b; + while ( (cur = cur.parentNode) ) { + bp.unshift( cur ); + } + + // Walk down the tree looking for a discrepancy + while ( ap[i] === bp[i] ) { + i++; + } + + return i ? + // Do a sibling check if the nodes have a common ancestor + siblingCheck( ap[i], bp[i] ) : + + // Otherwise nodes in our document sort first + ap[i] === preferredDoc ? -1 : + bp[i] === preferredDoc ? 1 : + 0; + }; + + return document; +}; + +Sizzle.matches = function( expr, elements ) { + return Sizzle( expr, null, null, elements ); +}; + +Sizzle.matchesSelector = function( elem, expr ) { + // Set document vars if needed + if ( ( elem.ownerDocument || elem ) !== document ) { + setDocument( elem ); + } + + // Make sure that attribute selectors are quoted + expr = expr.replace( rattributeQuotes, "='$1']" ); + + if ( support.matchesSelector && documentIsHTML && + !compilerCache[ expr + " " ] && + ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) && + ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) { + + try { + var ret = matches.call( elem, expr ); + + // IE 9's matchesSelector returns false on disconnected nodes + if ( ret || support.disconnectedMatch || + // As well, disconnected nodes are said to be in a document + // fragment in IE 9 + elem.document && elem.document.nodeType !== 11 ) { + return ret; + } + } catch (e) {} + } + + return Sizzle( expr, document, null, [ elem ] ).length > 0; +}; + +Sizzle.contains = function( context, elem ) { + // Set document vars if needed + if ( ( context.ownerDocument || context ) !== document ) { + setDocument( context ); + } + return contains( context, elem ); +}; + +Sizzle.attr = function( elem, name ) { + // Set document vars if needed + if ( ( elem.ownerDocument || elem ) !== document ) { + setDocument( elem ); + } + + var fn = Expr.attrHandle[ name.toLowerCase() ], + // Don't get fooled by Object.prototype properties (jQuery #13807) + val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ? + fn( elem, name, !documentIsHTML ) : + undefined; + + return val !== undefined ? + val : + support.attributes || !documentIsHTML ? + elem.getAttribute( name ) : + (val = elem.getAttributeNode(name)) && val.specified ? + val.value : + null; +}; + +Sizzle.error = function( msg ) { + throw new Error( "Syntax error, unrecognized expression: " + msg ); +}; + +/** + * Document sorting and removing duplicates + * @param {ArrayLike} results + */ +Sizzle.uniqueSort = function( results ) { + var elem, + duplicates = [], + j = 0, + i = 0; + + // Unless we *know* we can detect duplicates, assume their presence + hasDuplicate = !support.detectDuplicates; + sortInput = !support.sortStable && results.slice( 0 ); + results.sort( sortOrder ); + + if ( hasDuplicate ) { + while ( (elem = results[i++]) ) { + if ( elem === results[ i ] ) { + j = duplicates.push( i ); + } + } + while ( j-- ) { + results.splice( duplicates[ j ], 1 ); + } + } + + // Clear input after sorting to release objects + // See https://github.com/jquery/sizzle/pull/225 + sortInput = null; + + return results; +}; + +/** + * Utility function for retrieving the text value of an array of DOM nodes + * @param {Array|Element} elem + */ +getText = Sizzle.getText = function( elem ) { + var node, + ret = "", + i = 0, + nodeType = elem.nodeType; + + if ( !nodeType ) { + // If no nodeType, this is expected to be an array + while ( (node = elem[i++]) ) { + // Do not traverse comment nodes + ret += getText( node ); + } + } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { + // Use textContent for elements + // innerText usage removed for consistency of new lines (jQuery #11153) + if ( typeof elem.textContent === "string" ) { + return elem.textContent; + } else { + // Traverse its children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + ret += getText( elem ); + } + } + } else if ( nodeType === 3 || nodeType === 4 ) { + return elem.nodeValue; + } + // Do not include comment or processing instruction nodes + + return ret; +}; + +Expr = Sizzle.selectors = { + + // Can be adjusted by the user + cacheLength: 50, + + createPseudo: markFunction, + + match: matchExpr, + + attrHandle: {}, + + find: {}, + + relative: { + ">": { dir: "parentNode", first: true }, + " ": { dir: "parentNode" }, + "+": { dir: "previousSibling", first: true }, + "~": { dir: "previousSibling" } + }, + + preFilter: { + "ATTR": function( match ) { + match[1] = match[1].replace( runescape, funescape ); + + // Move the given value to match[3] whether quoted or unquoted + match[3] = ( match[3] || match[4] || match[5] || "" ).replace( runescape, funescape ); + + if ( match[2] === "~=" ) { + match[3] = " " + match[3] + " "; + } + + return match.slice( 0, 4 ); + }, + + "CHILD": function( match ) { + /* matches from matchExpr["CHILD"] + 1 type (only|nth|...) + 2 what (child|of-type) + 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...) + 4 xn-component of xn+y argument ([+-]?\d*n|) + 5 sign of xn-component + 6 x of xn-component + 7 sign of y-component + 8 y of y-component + */ + match[1] = match[1].toLowerCase(); + + if ( match[1].slice( 0, 3 ) === "nth" ) { + // nth-* requires argument + if ( !match[3] ) { + Sizzle.error( match[0] ); + } + + // numeric x and y parameters for Expr.filter.CHILD + // remember that false/true cast respectively to 0/1 + match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) ); + match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" ); + + // other types prohibit arguments + } else if ( match[3] ) { + Sizzle.error( match[0] ); + } + + return match; + }, + + "PSEUDO": function( match ) { + var excess, + unquoted = !match[6] && match[2]; + + if ( matchExpr["CHILD"].test( match[0] ) ) { + return null; + } + + // Accept quoted arguments as-is + if ( match[3] ) { + match[2] = match[4] || match[5] || ""; + + // Strip excess characters from unquoted arguments + } else if ( unquoted && rpseudo.test( unquoted ) && + // Get excess from tokenize (recursively) + (excess = tokenize( unquoted, true )) && + // advance to the next closing parenthesis + (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) { + + // excess is a negative index + match[0] = match[0].slice( 0, excess ); + match[2] = unquoted.slice( 0, excess ); + } + + // Return only captures needed by the pseudo filter method (type and argument) + return match.slice( 0, 3 ); + } + }, + + filter: { + + "TAG": function( nodeNameSelector ) { + var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase(); + return nodeNameSelector === "*" ? + function() { return true; } : + function( elem ) { + return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; + }; + }, + + "CLASS": function( className ) { + var pattern = classCache[ className + " " ]; + + return pattern || + (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) && + classCache( className, function( elem ) { + return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== "undefined" && elem.getAttribute("class") || "" ); + }); + }, + + "ATTR": function( name, operator, check ) { + return function( elem ) { + var result = Sizzle.attr( elem, name ); + + if ( result == null ) { + return operator === "!="; + } + if ( !operator ) { + return true; + } + + result += ""; + + return operator === "=" ? result === check : + operator === "!=" ? result !== check : + operator === "^=" ? check && result.indexOf( check ) === 0 : + operator === "*=" ? check && result.indexOf( check ) > -1 : + operator === "$=" ? check && result.slice( -check.length ) === check : + operator === "~=" ? ( " " + result.replace( rwhitespace, " " ) + " " ).indexOf( check ) > -1 : + operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" : + false; + }; + }, + + "CHILD": function( type, what, argument, first, last ) { + var simple = type.slice( 0, 3 ) !== "nth", + forward = type.slice( -4 ) !== "last", + ofType = what === "of-type"; + + return first === 1 && last === 0 ? + + // Shortcut for :nth-*(n) + function( elem ) { + return !!elem.parentNode; + } : + + function( elem, context, xml ) { + var cache, uniqueCache, outerCache, node, nodeIndex, start, + dir = simple !== forward ? "nextSibling" : "previousSibling", + parent = elem.parentNode, + name = ofType && elem.nodeName.toLowerCase(), + useCache = !xml && !ofType, + diff = false; + + if ( parent ) { + + // :(first|last|only)-(child|of-type) + if ( simple ) { + while ( dir ) { + node = elem; + while ( (node = node[ dir ]) ) { + if ( ofType ? + node.nodeName.toLowerCase() === name : + node.nodeType === 1 ) { + + return false; + } + } + // Reverse direction for :only-* (if we haven't yet done so) + start = dir = type === "only" && !start && "nextSibling"; + } + return true; + } + + start = [ forward ? parent.firstChild : parent.lastChild ]; + + // non-xml :nth-child(...) stores cache data on `parent` + if ( forward && useCache ) { + + // Seek `elem` from a previously-cached index + + // ...in a gzip-friendly way + node = parent; + outerCache = node[ expando ] || (node[ expando ] = {}); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + (outerCache[ node.uniqueID ] = {}); + + cache = uniqueCache[ type ] || []; + nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; + diff = nodeIndex && cache[ 2 ]; + node = nodeIndex && parent.childNodes[ nodeIndex ]; + + while ( (node = ++nodeIndex && node && node[ dir ] || + + // Fallback to seeking `elem` from the start + (diff = nodeIndex = 0) || start.pop()) ) { + + // When found, cache indexes on `parent` and break + if ( node.nodeType === 1 && ++diff && node === elem ) { + uniqueCache[ type ] = [ dirruns, nodeIndex, diff ]; + break; + } + } + + } else { + // Use previously-cached element index if available + if ( useCache ) { + // ...in a gzip-friendly way + node = elem; + outerCache = node[ expando ] || (node[ expando ] = {}); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + (outerCache[ node.uniqueID ] = {}); + + cache = uniqueCache[ type ] || []; + nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; + diff = nodeIndex; + } + + // xml :nth-child(...) + // or :nth-last-child(...) or :nth(-last)?-of-type(...) + if ( diff === false ) { + // Use the same loop as above to seek `elem` from the start + while ( (node = ++nodeIndex && node && node[ dir ] || + (diff = nodeIndex = 0) || start.pop()) ) { + + if ( ( ofType ? + node.nodeName.toLowerCase() === name : + node.nodeType === 1 ) && + ++diff ) { + + // Cache the index of each encountered element + if ( useCache ) { + outerCache = node[ expando ] || (node[ expando ] = {}); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + (outerCache[ node.uniqueID ] = {}); + + uniqueCache[ type ] = [ dirruns, diff ]; + } + + if ( node === elem ) { + break; + } + } + } + } + } + + // Incorporate the offset, then check against cycle size + diff -= last; + return diff === first || ( diff % first === 0 && diff / first >= 0 ); + } + }; + }, + + "PSEUDO": function( pseudo, argument ) { + // pseudo-class names are case-insensitive + // http://www.w3.org/TR/selectors/#pseudo-classes + // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters + // Remember that setFilters inherits from pseudos + var args, + fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] || + Sizzle.error( "unsupported pseudo: " + pseudo ); + + // The user may use createPseudo to indicate that + // arguments are needed to create the filter function + // just as Sizzle does + if ( fn[ expando ] ) { + return fn( argument ); + } + + // But maintain support for old signatures + if ( fn.length > 1 ) { + args = [ pseudo, pseudo, "", argument ]; + return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ? + markFunction(function( seed, matches ) { + var idx, + matched = fn( seed, argument ), + i = matched.length; + while ( i-- ) { + idx = indexOf( seed, matched[i] ); + seed[ idx ] = !( matches[ idx ] = matched[i] ); + } + }) : + function( elem ) { + return fn( elem, 0, args ); + }; + } + + return fn; + } + }, + + pseudos: { + // Potentially complex pseudos + "not": markFunction(function( selector ) { + // Trim the selector passed to compile + // to avoid treating leading and trailing + // spaces as combinators + var input = [], + results = [], + matcher = compile( selector.replace( rtrim, "$1" ) ); + + return matcher[ expando ] ? + markFunction(function( seed, matches, context, xml ) { + var elem, + unmatched = matcher( seed, null, xml, [] ), + i = seed.length; + + // Match elements unmatched by `matcher` + while ( i-- ) { + if ( (elem = unmatched[i]) ) { + seed[i] = !(matches[i] = elem); + } + } + }) : + function( elem, context, xml ) { + input[0] = elem; + matcher( input, null, xml, results ); + // Don't keep the element (issue #299) + input[0] = null; + return !results.pop(); + }; + }), + + "has": markFunction(function( selector ) { + return function( elem ) { + return Sizzle( selector, elem ).length > 0; + }; + }), + + "contains": markFunction(function( text ) { + text = text.replace( runescape, funescape ); + return function( elem ) { + return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1; + }; + }), + + // "Whether an element is represented by a :lang() selector + // is based solely on the element's language value + // being equal to the identifier C, + // or beginning with the identifier C immediately followed by "-". + // The matching of C against the element's language value is performed case-insensitively. + // The identifier C does not have to be a valid language name." + // http://www.w3.org/TR/selectors/#lang-pseudo + "lang": markFunction( function( lang ) { + // lang value must be a valid identifier + if ( !ridentifier.test(lang || "") ) { + Sizzle.error( "unsupported lang: " + lang ); + } + lang = lang.replace( runescape, funescape ).toLowerCase(); + return function( elem ) { + var elemLang; + do { + if ( (elemLang = documentIsHTML ? + elem.lang : + elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) { + + elemLang = elemLang.toLowerCase(); + return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0; + } + } while ( (elem = elem.parentNode) && elem.nodeType === 1 ); + return false; + }; + }), + + // Miscellaneous + "target": function( elem ) { + var hash = window.location && window.location.hash; + return hash && hash.slice( 1 ) === elem.id; + }, + + "root": function( elem ) { + return elem === docElem; + }, + + "focus": function( elem ) { + return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex); + }, + + // Boolean properties + "enabled": function( elem ) { + return elem.disabled === false; + }, + + "disabled": function( elem ) { + return elem.disabled === true; + }, + + "checked": function( elem ) { + // In CSS3, :checked should return both checked and selected elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + var nodeName = elem.nodeName.toLowerCase(); + return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected); + }, + + "selected": function( elem ) { + // Accessing this property makes selected-by-default + // options in Safari work properly + if ( elem.parentNode ) { + elem.parentNode.selectedIndex; + } + + return elem.selected === true; + }, + + // Contents + "empty": function( elem ) { + // http://www.w3.org/TR/selectors/#empty-pseudo + // :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5), + // but not by others (comment: 8; processing instruction: 7; etc.) + // nodeType < 6 works because attributes (2) do not appear as children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + if ( elem.nodeType < 6 ) { + return false; + } + } + return true; + }, + + "parent": function( elem ) { + return !Expr.pseudos["empty"]( elem ); + }, + + // Element/input types + "header": function( elem ) { + return rheader.test( elem.nodeName ); + }, + + "input": function( elem ) { + return rinputs.test( elem.nodeName ); + }, + + "button": function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === "button" || name === "button"; + }, + + "text": function( elem ) { + var attr; + return elem.nodeName.toLowerCase() === "input" && + elem.type === "text" && + + // Support: IE<8 + // New HTML5 attribute values (e.g., "search") appear with elem.type === "text" + ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === "text" ); + }, + + // Position-in-collection + "first": createPositionalPseudo(function() { + return [ 0 ]; + }), + + "last": createPositionalPseudo(function( matchIndexes, length ) { + return [ length - 1 ]; + }), + + "eq": createPositionalPseudo(function( matchIndexes, length, argument ) { + return [ argument < 0 ? argument + length : argument ]; + }), + + "even": createPositionalPseudo(function( matchIndexes, length ) { + var i = 0; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "odd": createPositionalPseudo(function( matchIndexes, length ) { + var i = 1; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "lt": createPositionalPseudo(function( matchIndexes, length, argument ) { + var i = argument < 0 ? argument + length : argument; + for ( ; --i >= 0; ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "gt": createPositionalPseudo(function( matchIndexes, length, argument ) { + var i = argument < 0 ? argument + length : argument; + for ( ; ++i < length; ) { + matchIndexes.push( i ); + } + return matchIndexes; + }) + } +}; + +Expr.pseudos["nth"] = Expr.pseudos["eq"]; + +// Add button/input type pseudos +for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) { + Expr.pseudos[ i ] = createInputPseudo( i ); +} +for ( i in { submit: true, reset: true } ) { + Expr.pseudos[ i ] = createButtonPseudo( i ); +} + +// Easy API for creating new setFilters +function setFilters() {} +setFilters.prototype = Expr.filters = Expr.pseudos; +Expr.setFilters = new setFilters(); + +tokenize = Sizzle.tokenize = function( selector, parseOnly ) { + var matched, match, tokens, type, + soFar, groups, preFilters, + cached = tokenCache[ selector + " " ]; + + if ( cached ) { + return parseOnly ? 0 : cached.slice( 0 ); + } + + soFar = selector; + groups = []; + preFilters = Expr.preFilter; + + while ( soFar ) { + + // Comma and first run + if ( !matched || (match = rcomma.exec( soFar )) ) { + if ( match ) { + // Don't consume trailing commas as valid + soFar = soFar.slice( match[0].length ) || soFar; + } + groups.push( (tokens = []) ); + } + + matched = false; + + // Combinators + if ( (match = rcombinators.exec( soFar )) ) { + matched = match.shift(); + tokens.push({ + value: matched, + // Cast descendant combinators to space + type: match[0].replace( rtrim, " " ) + }); + soFar = soFar.slice( matched.length ); + } + + // Filters + for ( type in Expr.filter ) { + if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] || + (match = preFilters[ type ]( match ))) ) { + matched = match.shift(); + tokens.push({ + value: matched, + type: type, + matches: match + }); + soFar = soFar.slice( matched.length ); + } + } + + if ( !matched ) { + break; + } + } + + // Return the length of the invalid excess + // if we're just parsing + // Otherwise, throw an error or return tokens + return parseOnly ? + soFar.length : + soFar ? + Sizzle.error( selector ) : + // Cache the tokens + tokenCache( selector, groups ).slice( 0 ); +}; + +function toSelector( tokens ) { + var i = 0, + len = tokens.length, + selector = ""; + for ( ; i < len; i++ ) { + selector += tokens[i].value; + } + return selector; +} + +function addCombinator( matcher, combinator, base ) { + var dir = combinator.dir, + checkNonElements = base && dir === "parentNode", + doneName = done++; + + return combinator.first ? + // Check against closest ancestor/preceding element + function( elem, context, xml ) { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + return matcher( elem, context, xml ); + } + } + } : + + // Check against all ancestor/preceding elements + function( elem, context, xml ) { + var oldCache, uniqueCache, outerCache, + newCache = [ dirruns, doneName ]; + + // We can't set arbitrary data on XML nodes, so they don't benefit from combinator caching + if ( xml ) { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + if ( matcher( elem, context, xml ) ) { + return true; + } + } + } + } else { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + outerCache = elem[ expando ] || (elem[ expando ] = {}); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ elem.uniqueID ] || (outerCache[ elem.uniqueID ] = {}); + + if ( (oldCache = uniqueCache[ dir ]) && + oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) { + + // Assign to newCache so results back-propagate to previous elements + return (newCache[ 2 ] = oldCache[ 2 ]); + } else { + // Reuse newcache so results back-propagate to previous elements + uniqueCache[ dir ] = newCache; + + // A match means we're done; a fail means we have to keep checking + if ( (newCache[ 2 ] = matcher( elem, context, xml )) ) { + return true; + } + } + } + } + } + }; +} + +function elementMatcher( matchers ) { + return matchers.length > 1 ? + function( elem, context, xml ) { + var i = matchers.length; + while ( i-- ) { + if ( !matchers[i]( elem, context, xml ) ) { + return false; + } + } + return true; + } : + matchers[0]; +} + +function multipleContexts( selector, contexts, results ) { + var i = 0, + len = contexts.length; + for ( ; i < len; i++ ) { + Sizzle( selector, contexts[i], results ); + } + return results; +} + +function condense( unmatched, map, filter, context, xml ) { + var elem, + newUnmatched = [], + i = 0, + len = unmatched.length, + mapped = map != null; + + for ( ; i < len; i++ ) { + if ( (elem = unmatched[i]) ) { + if ( !filter || filter( elem, context, xml ) ) { + newUnmatched.push( elem ); + if ( mapped ) { + map.push( i ); + } + } + } + } + + return newUnmatched; +} + +function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) { + if ( postFilter && !postFilter[ expando ] ) { + postFilter = setMatcher( postFilter ); + } + if ( postFinder && !postFinder[ expando ] ) { + postFinder = setMatcher( postFinder, postSelector ); + } + return markFunction(function( seed, results, context, xml ) { + var temp, i, elem, + preMap = [], + postMap = [], + preexisting = results.length, + + // Get initial elements from seed or context + elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ), + + // Prefilter to get matcher input, preserving a map for seed-results synchronization + matcherIn = preFilter && ( seed || !selector ) ? + condense( elems, preMap, preFilter, context, xml ) : + elems, + + matcherOut = matcher ? + // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, + postFinder || ( seed ? preFilter : preexisting || postFilter ) ? + + // ...intermediate processing is necessary + [] : + + // ...otherwise use results directly + results : + matcherIn; + + // Find primary matches + if ( matcher ) { + matcher( matcherIn, matcherOut, context, xml ); + } + + // Apply postFilter + if ( postFilter ) { + temp = condense( matcherOut, postMap ); + postFilter( temp, [], context, xml ); + + // Un-match failing elements by moving them back to matcherIn + i = temp.length; + while ( i-- ) { + if ( (elem = temp[i]) ) { + matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem); + } + } + } + + if ( seed ) { + if ( postFinder || preFilter ) { + if ( postFinder ) { + // Get the final matcherOut by condensing this intermediate into postFinder contexts + temp = []; + i = matcherOut.length; + while ( i-- ) { + if ( (elem = matcherOut[i]) ) { + // Restore matcherIn since elem is not yet a final match + temp.push( (matcherIn[i] = elem) ); + } + } + postFinder( null, (matcherOut = []), temp, xml ); + } + + // Move matched elements from seed to results to keep them synchronized + i = matcherOut.length; + while ( i-- ) { + if ( (elem = matcherOut[i]) && + (temp = postFinder ? indexOf( seed, elem ) : preMap[i]) > -1 ) { + + seed[temp] = !(results[temp] = elem); + } + } + } + + // Add elements to results, through postFinder if defined + } else { + matcherOut = condense( + matcherOut === results ? + matcherOut.splice( preexisting, matcherOut.length ) : + matcherOut + ); + if ( postFinder ) { + postFinder( null, results, matcherOut, xml ); + } else { + push.apply( results, matcherOut ); + } + } + }); +} + +function matcherFromTokens( tokens ) { + var checkContext, matcher, j, + len = tokens.length, + leadingRelative = Expr.relative[ tokens[0].type ], + implicitRelative = leadingRelative || Expr.relative[" "], + i = leadingRelative ? 1 : 0, + + // The foundational matcher ensures that elements are reachable from top-level context(s) + matchContext = addCombinator( function( elem ) { + return elem === checkContext; + }, implicitRelative, true ), + matchAnyContext = addCombinator( function( elem ) { + return indexOf( checkContext, elem ) > -1; + }, implicitRelative, true ), + matchers = [ function( elem, context, xml ) { + var ret = ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( + (checkContext = context).nodeType ? + matchContext( elem, context, xml ) : + matchAnyContext( elem, context, xml ) ); + // Avoid hanging onto element (issue #299) + checkContext = null; + return ret; + } ]; + + for ( ; i < len; i++ ) { + if ( (matcher = Expr.relative[ tokens[i].type ]) ) { + matchers = [ addCombinator(elementMatcher( matchers ), matcher) ]; + } else { + matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches ); + + // Return special upon seeing a positional matcher + if ( matcher[ expando ] ) { + // Find the next relative operator (if any) for proper handling + j = ++i; + for ( ; j < len; j++ ) { + if ( Expr.relative[ tokens[j].type ] ) { + break; + } + } + return setMatcher( + i > 1 && elementMatcher( matchers ), + i > 1 && toSelector( + // If the preceding token was a descendant combinator, insert an implicit any-element `*` + tokens.slice( 0, i - 1 ).concat({ value: tokens[ i - 2 ].type === " " ? "*" : "" }) + ).replace( rtrim, "$1" ), + matcher, + i < j && matcherFromTokens( tokens.slice( i, j ) ), + j < len && matcherFromTokens( (tokens = tokens.slice( j )) ), + j < len && toSelector( tokens ) + ); + } + matchers.push( matcher ); + } + } + + return elementMatcher( matchers ); +} + +function matcherFromGroupMatchers( elementMatchers, setMatchers ) { + var bySet = setMatchers.length > 0, + byElement = elementMatchers.length > 0, + superMatcher = function( seed, context, xml, results, outermost ) { + var elem, j, matcher, + matchedCount = 0, + i = "0", + unmatched = seed && [], + setMatched = [], + contextBackup = outermostContext, + // We must always have either seed elements or outermost context + elems = seed || byElement && Expr.find["TAG"]( "*", outermost ), + // Use integer dirruns iff this is the outermost matcher + dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1), + len = elems.length; + + if ( outermost ) { + outermostContext = context === document || context || outermost; + } + + // Add elements passing elementMatchers directly to results + // Support: IE<9, Safari + // Tolerate NodeList properties (IE: "length"; Safari: ) matching elements by id + for ( ; i !== len && (elem = elems[i]) != null; i++ ) { + if ( byElement && elem ) { + j = 0; + if ( !context && elem.ownerDocument !== document ) { + setDocument( elem ); + xml = !documentIsHTML; + } + while ( (matcher = elementMatchers[j++]) ) { + if ( matcher( elem, context || document, xml) ) { + results.push( elem ); + break; + } + } + if ( outermost ) { + dirruns = dirrunsUnique; + } + } + + // Track unmatched elements for set filters + if ( bySet ) { + // They will have gone through all possible matchers + if ( (elem = !matcher && elem) ) { + matchedCount--; + } + + // Lengthen the array for every element, matched or not + if ( seed ) { + unmatched.push( elem ); + } + } + } + + // `i` is now the count of elements visited above, and adding it to `matchedCount` + // makes the latter nonnegative. + matchedCount += i; + + // Apply set filters to unmatched elements + // NOTE: This can be skipped if there are no unmatched elements (i.e., `matchedCount` + // equals `i`), unless we didn't visit _any_ elements in the above loop because we have + // no element matchers and no seed. + // Incrementing an initially-string "0" `i` allows `i` to remain a string only in that + // case, which will result in a "00" `matchedCount` that differs from `i` but is also + // numerically zero. + if ( bySet && i !== matchedCount ) { + j = 0; + while ( (matcher = setMatchers[j++]) ) { + matcher( unmatched, setMatched, context, xml ); + } + + if ( seed ) { + // Reintegrate element matches to eliminate the need for sorting + if ( matchedCount > 0 ) { + while ( i-- ) { + if ( !(unmatched[i] || setMatched[i]) ) { + setMatched[i] = pop.call( results ); + } + } + } + + // Discard index placeholder values to get only actual matches + setMatched = condense( setMatched ); + } + + // Add matches to results + push.apply( results, setMatched ); + + // Seedless set matches succeeding multiple successful matchers stipulate sorting + if ( outermost && !seed && setMatched.length > 0 && + ( matchedCount + setMatchers.length ) > 1 ) { + + Sizzle.uniqueSort( results ); + } + } + + // Override manipulation of globals by nested matchers + if ( outermost ) { + dirruns = dirrunsUnique; + outermostContext = contextBackup; + } + + return unmatched; + }; + + return bySet ? + markFunction( superMatcher ) : + superMatcher; +} + +compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) { + var i, + setMatchers = [], + elementMatchers = [], + cached = compilerCache[ selector + " " ]; + + if ( !cached ) { + // Generate a function of recursive functions that can be used to check each element + if ( !match ) { + match = tokenize( selector ); + } + i = match.length; + while ( i-- ) { + cached = matcherFromTokens( match[i] ); + if ( cached[ expando ] ) { + setMatchers.push( cached ); + } else { + elementMatchers.push( cached ); + } + } + + // Cache the compiled function + cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) ); + + // Save selector and tokenization + cached.selector = selector; + } + return cached; +}; + +/** + * A low-level selection function that works with Sizzle's compiled + * selector functions + * @param {String|Function} selector A selector or a pre-compiled + * selector function built with Sizzle.compile + * @param {Element} context + * @param {Array} [results] + * @param {Array} [seed] A set of elements to match against + */ +select = Sizzle.select = function( selector, context, results, seed ) { + var i, tokens, token, type, find, + compiled = typeof selector === "function" && selector, + match = !seed && tokenize( (selector = compiled.selector || selector) ); + + results = results || []; + + // Try to minimize operations if there is only one selector in the list and no seed + // (the latter of which guarantees us context) + if ( match.length === 1 ) { + + // Reduce context if the leading compound selector is an ID + tokens = match[0] = match[0].slice( 0 ); + if ( tokens.length > 2 && (token = tokens[0]).type === "ID" && + support.getById && context.nodeType === 9 && documentIsHTML && + Expr.relative[ tokens[1].type ] ) { + + context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0]; + if ( !context ) { + return results; + + // Precompiled matchers will still verify ancestry, so step up a level + } else if ( compiled ) { + context = context.parentNode; + } + + selector = selector.slice( tokens.shift().value.length ); + } + + // Fetch a seed set for right-to-left matching + i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length; + while ( i-- ) { + token = tokens[i]; + + // Abort if we hit a combinator + if ( Expr.relative[ (type = token.type) ] ) { + break; + } + if ( (find = Expr.find[ type ]) ) { + // Search, expanding context for leading sibling combinators + if ( (seed = find( + token.matches[0].replace( runescape, funescape ), + rsibling.test( tokens[0].type ) && testContext( context.parentNode ) || context + )) ) { + + // If seed is empty or no tokens remain, we can return early + tokens.splice( i, 1 ); + selector = seed.length && toSelector( tokens ); + if ( !selector ) { + push.apply( results, seed ); + return results; + } + + break; + } + } + } + } + + // Compile and execute a filtering function if one is not provided + // Provide `match` to avoid retokenization if we modified the selector above + ( compiled || compile( selector, match ) )( + seed, + context, + !documentIsHTML, + results, + !context || rsibling.test( selector ) && testContext( context.parentNode ) || context + ); + return results; +}; + +// One-time assignments + +// Sort stability +support.sortStable = expando.split("").sort( sortOrder ).join("") === expando; + +// Support: Chrome 14-35+ +// Always assume duplicates if they aren't passed to the comparison function +support.detectDuplicates = !!hasDuplicate; + +// Initialize against the default document +setDocument(); + +// Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27) +// Detached nodes confoundingly follow *each other* +support.sortDetached = assert(function( div1 ) { + // Should return 1, but returns 4 (following) + return div1.compareDocumentPosition( document.createElement("div") ) & 1; +}); + +// Support: IE<8 +// Prevent attribute/property "interpolation" +// http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx +if ( !assert(function( div ) { + div.innerHTML = ""; + return div.firstChild.getAttribute("href") === "#" ; +}) ) { + addHandle( "type|href|height|width", function( elem, name, isXML ) { + if ( !isXML ) { + return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 ); + } + }); +} + +// Support: IE<9 +// Use defaultValue in place of getAttribute("value") +if ( !support.attributes || !assert(function( div ) { + div.innerHTML = ""; + div.firstChild.setAttribute( "value", "" ); + return div.firstChild.getAttribute( "value" ) === ""; +}) ) { + addHandle( "value", function( elem, name, isXML ) { + if ( !isXML && elem.nodeName.toLowerCase() === "input" ) { + return elem.defaultValue; + } + }); +} + +// Support: IE<9 +// Use getAttributeNode to fetch booleans when getAttribute lies +if ( !assert(function( div ) { + return div.getAttribute("disabled") == null; +}) ) { + addHandle( booleans, function( elem, name, isXML ) { + var val; + if ( !isXML ) { + return elem[ name ] === true ? name.toLowerCase() : + (val = elem.getAttributeNode( name )) && val.specified ? + val.value : + null; + } + }); +} + +return Sizzle; + +})( window ); + + + +jQuery.find = Sizzle; +jQuery.expr = Sizzle.selectors; +jQuery.expr[ ":" ] = jQuery.expr.pseudos; +jQuery.uniqueSort = jQuery.unique = Sizzle.uniqueSort; +jQuery.text = Sizzle.getText; +jQuery.isXMLDoc = Sizzle.isXML; +jQuery.contains = Sizzle.contains; + + + +var dir = function( elem, dir, until ) { + var matched = [], + truncate = until !== undefined; + + while ( ( elem = elem[ dir ] ) && elem.nodeType !== 9 ) { + if ( elem.nodeType === 1 ) { + if ( truncate && jQuery( elem ).is( until ) ) { + break; + } + matched.push( elem ); + } + } + return matched; +}; + + +var siblings = function( n, elem ) { + var matched = []; + + for ( ; n; n = n.nextSibling ) { + if ( n.nodeType === 1 && n !== elem ) { + matched.push( n ); + } + } + + return matched; +}; + + +var rneedsContext = jQuery.expr.match.needsContext; + +var rsingleTag = ( /^<([\w-]+)\s*\/?>(?:<\/\1>|)$/ ); + + + +var risSimple = /^.[^:#\[\.,]*$/; + +// Implement the identical functionality for filter and not +function winnow( elements, qualifier, not ) { + if ( jQuery.isFunction( qualifier ) ) { + return jQuery.grep( elements, function( elem, i ) { + /* jshint -W018 */ + return !!qualifier.call( elem, i, elem ) !== not; + } ); + + } + + if ( qualifier.nodeType ) { + return jQuery.grep( elements, function( elem ) { + return ( elem === qualifier ) !== not; + } ); + + } + + if ( typeof qualifier === "string" ) { + if ( risSimple.test( qualifier ) ) { + return jQuery.filter( qualifier, elements, not ); + } + + qualifier = jQuery.filter( qualifier, elements ); + } + + return jQuery.grep( elements, function( elem ) { + return ( jQuery.inArray( elem, qualifier ) > -1 ) !== not; + } ); +} + +jQuery.filter = function( expr, elems, not ) { + var elem = elems[ 0 ]; + + if ( not ) { + expr = ":not(" + expr + ")"; + } + + return elems.length === 1 && elem.nodeType === 1 ? + jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : [] : + jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) { + return elem.nodeType === 1; + } ) ); +}; + +jQuery.fn.extend( { + find: function( selector ) { + var i, + ret = [], + self = this, + len = self.length; + + if ( typeof selector !== "string" ) { + return this.pushStack( jQuery( selector ).filter( function() { + for ( i = 0; i < len; i++ ) { + if ( jQuery.contains( self[ i ], this ) ) { + return true; + } + } + } ) ); + } + + for ( i = 0; i < len; i++ ) { + jQuery.find( selector, self[ i ], ret ); + } + + // Needed because $( selector, context ) becomes $( context ).find( selector ) + ret = this.pushStack( len > 1 ? jQuery.unique( ret ) : ret ); + ret.selector = this.selector ? this.selector + " " + selector : selector; + return ret; + }, + filter: function( selector ) { + return this.pushStack( winnow( this, selector || [], false ) ); + }, + not: function( selector ) { + return this.pushStack( winnow( this, selector || [], true ) ); + }, + is: function( selector ) { + return !!winnow( + this, + + // If this is a positional/relative selector, check membership in the returned set + // so $("p:first").is("p:last") won't return true for a doc with two "p". + typeof selector === "string" && rneedsContext.test( selector ) ? + jQuery( selector ) : + selector || [], + false + ).length; + } +} ); + + +// Initialize a jQuery object + + +// A central reference to the root jQuery(document) +var rootjQuery, + + // A simple way to check for HTML strings + // Prioritize #id over to avoid XSS via location.hash (#9521) + // Strict HTML recognition (#11290: must start with <) + rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/, + + init = jQuery.fn.init = function( selector, context, root ) { + var match, elem; + + // HANDLE: $(""), $(null), $(undefined), $(false) + if ( !selector ) { + return this; + } + + // init accepts an alternate rootjQuery + // so migrate can support jQuery.sub (gh-2101) + root = root || rootjQuery; + + // Handle HTML strings + if ( typeof selector === "string" ) { + if ( selector.charAt( 0 ) === "<" && + selector.charAt( selector.length - 1 ) === ">" && + selector.length >= 3 ) { + + // Assume that strings that start and end with <> are HTML and skip the regex check + match = [ null, selector, null ]; + + } else { + match = rquickExpr.exec( selector ); + } + + // Match html or make sure no context is specified for #id + if ( match && ( match[ 1 ] || !context ) ) { + + // HANDLE: $(html) -> $(array) + if ( match[ 1 ] ) { + context = context instanceof jQuery ? context[ 0 ] : context; + + // scripts is true for back-compat + // Intentionally let the error be thrown if parseHTML is not present + jQuery.merge( this, jQuery.parseHTML( + match[ 1 ], + context && context.nodeType ? context.ownerDocument || context : document, + true + ) ); + + // HANDLE: $(html, props) + if ( rsingleTag.test( match[ 1 ] ) && jQuery.isPlainObject( context ) ) { + for ( match in context ) { + + // Properties of context are called as methods if possible + if ( jQuery.isFunction( this[ match ] ) ) { + this[ match ]( context[ match ] ); + + // ...and otherwise set as attributes + } else { + this.attr( match, context[ match ] ); + } + } + } + + return this; + + // HANDLE: $(#id) + } else { + elem = document.getElementById( match[ 2 ] ); + + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + if ( elem && elem.parentNode ) { + + // Handle the case where IE and Opera return items + // by name instead of ID + if ( elem.id !== match[ 2 ] ) { + return rootjQuery.find( selector ); + } + + // Otherwise, we inject the element directly into the jQuery object + this.length = 1; + this[ 0 ] = elem; + } + + this.context = document; + this.selector = selector; + return this; + } + + // HANDLE: $(expr, $(...)) + } else if ( !context || context.jquery ) { + return ( context || root ).find( selector ); + + // HANDLE: $(expr, context) + // (which is just equivalent to: $(context).find(expr) + } else { + return this.constructor( context ).find( selector ); + } + + // HANDLE: $(DOMElement) + } else if ( selector.nodeType ) { + this.context = this[ 0 ] = selector; + this.length = 1; + return this; + + // HANDLE: $(function) + // Shortcut for document ready + } else if ( jQuery.isFunction( selector ) ) { + return typeof root.ready !== "undefined" ? + root.ready( selector ) : + + // Execute immediately if ready is not present + selector( jQuery ); + } + + if ( selector.selector !== undefined ) { + this.selector = selector.selector; + this.context = selector.context; + } + + return jQuery.makeArray( selector, this ); + }; + +// Give the init function the jQuery prototype for later instantiation +init.prototype = jQuery.fn; + +// Initialize central reference +rootjQuery = jQuery( document ); + + +var rparentsprev = /^(?:parents|prev(?:Until|All))/, + + // methods guaranteed to produce a unique set when starting from a unique set + guaranteedUnique = { + children: true, + contents: true, + next: true, + prev: true + }; + +jQuery.fn.extend( { + has: function( target ) { + var i, + targets = jQuery( target, this ), + len = targets.length; + + return this.filter( function() { + for ( i = 0; i < len; i++ ) { + if ( jQuery.contains( this, targets[ i ] ) ) { + return true; + } + } + } ); + }, + + closest: function( selectors, context ) { + var cur, + i = 0, + l = this.length, + matched = [], + pos = rneedsContext.test( selectors ) || typeof selectors !== "string" ? + jQuery( selectors, context || this.context ) : + 0; + + for ( ; i < l; i++ ) { + for ( cur = this[ i ]; cur && cur !== context; cur = cur.parentNode ) { + + // Always skip document fragments + if ( cur.nodeType < 11 && ( pos ? + pos.index( cur ) > -1 : + + // Don't pass non-elements to Sizzle + cur.nodeType === 1 && + jQuery.find.matchesSelector( cur, selectors ) ) ) { + + matched.push( cur ); + break; + } + } + } + + return this.pushStack( matched.length > 1 ? jQuery.uniqueSort( matched ) : matched ); + }, + + // Determine the position of an element within + // the matched set of elements + index: function( elem ) { + + // No argument, return index in parent + if ( !elem ) { + return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1; + } + + // index in selector + if ( typeof elem === "string" ) { + return jQuery.inArray( this[ 0 ], jQuery( elem ) ); + } + + // Locate the position of the desired element + return jQuery.inArray( + + // If it receives a jQuery object, the first element is used + elem.jquery ? elem[ 0 ] : elem, this ); + }, + + add: function( selector, context ) { + return this.pushStack( + jQuery.uniqueSort( + jQuery.merge( this.get(), jQuery( selector, context ) ) + ) + ); + }, + + addBack: function( selector ) { + return this.add( selector == null ? + this.prevObject : this.prevObject.filter( selector ) + ); + } +} ); + +function sibling( cur, dir ) { + do { + cur = cur[ dir ]; + } while ( cur && cur.nodeType !== 1 ); + + return cur; +} + +jQuery.each( { + parent: function( elem ) { + var parent = elem.parentNode; + return parent && parent.nodeType !== 11 ? parent : null; + }, + parents: function( elem ) { + return dir( elem, "parentNode" ); + }, + parentsUntil: function( elem, i, until ) { + return dir( elem, "parentNode", until ); + }, + next: function( elem ) { + return sibling( elem, "nextSibling" ); + }, + prev: function( elem ) { + return sibling( elem, "previousSibling" ); + }, + nextAll: function( elem ) { + return dir( elem, "nextSibling" ); + }, + prevAll: function( elem ) { + return dir( elem, "previousSibling" ); + }, + nextUntil: function( elem, i, until ) { + return dir( elem, "nextSibling", until ); + }, + prevUntil: function( elem, i, until ) { + return dir( elem, "previousSibling", until ); + }, + siblings: function( elem ) { + return siblings( ( elem.parentNode || {} ).firstChild, elem ); + }, + children: function( elem ) { + return siblings( elem.firstChild ); + }, + contents: function( elem ) { + return jQuery.nodeName( elem, "iframe" ) ? + elem.contentDocument || elem.contentWindow.document : + jQuery.merge( [], elem.childNodes ); + } +}, function( name, fn ) { + jQuery.fn[ name ] = function( until, selector ) { + var ret = jQuery.map( this, fn, until ); + + if ( name.slice( -5 ) !== "Until" ) { + selector = until; + } + + if ( selector && typeof selector === "string" ) { + ret = jQuery.filter( selector, ret ); + } + + if ( this.length > 1 ) { + + // Remove duplicates + if ( !guaranteedUnique[ name ] ) { + ret = jQuery.uniqueSort( ret ); + } + + // Reverse order for parents* and prev-derivatives + if ( rparentsprev.test( name ) ) { + ret = ret.reverse(); + } + } + + return this.pushStack( ret ); + }; +} ); +var rnotwhite = ( /\S+/g ); + + + +// Convert String-formatted options into Object-formatted ones +function createOptions( options ) { + var object = {}; + jQuery.each( options.match( rnotwhite ) || [], function( _, flag ) { + object[ flag ] = true; + } ); + return object; +} + +/* + * Create a callback list using the following parameters: + * + * options: an optional list of space-separated options that will change how + * the callback list behaves or a more traditional option object + * + * By default a callback list will act like an event callback list and can be + * "fired" multiple times. + * + * Possible options: + * + * once: will ensure the callback list can only be fired once (like a Deferred) + * + * memory: will keep track of previous values and will call any callback added + * after the list has been fired right away with the latest "memorized" + * values (like a Deferred) + * + * unique: will ensure a callback can only be added once (no duplicate in the list) + * + * stopOnFalse: interrupt callings when a callback returns false + * + */ +jQuery.Callbacks = function( options ) { + + // Convert options from String-formatted to Object-formatted if needed + // (we check in cache first) + options = typeof options === "string" ? + createOptions( options ) : + jQuery.extend( {}, options ); + + var // Flag to know if list is currently firing + firing, + + // Last fire value for non-forgettable lists + memory, + + // Flag to know if list was already fired + fired, + + // Flag to prevent firing + locked, + + // Actual callback list + list = [], + + // Queue of execution data for repeatable lists + queue = [], + + // Index of currently firing callback (modified by add/remove as needed) + firingIndex = -1, + + // Fire callbacks + fire = function() { + + // Enforce single-firing + locked = options.once; + + // Execute callbacks for all pending executions, + // respecting firingIndex overrides and runtime changes + fired = firing = true; + for ( ; queue.length; firingIndex = -1 ) { + memory = queue.shift(); + while ( ++firingIndex < list.length ) { + + // Run callback and check for early termination + if ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false && + options.stopOnFalse ) { + + // Jump to end and forget the data so .add doesn't re-fire + firingIndex = list.length; + memory = false; + } + } + } + + // Forget the data if we're done with it + if ( !options.memory ) { + memory = false; + } + + firing = false; + + // Clean up if we're done firing for good + if ( locked ) { + + // Keep an empty list if we have data for future add calls + if ( memory ) { + list = []; + + // Otherwise, this object is spent + } else { + list = ""; + } + } + }, + + // Actual Callbacks object + self = { + + // Add a callback or a collection of callbacks to the list + add: function() { + if ( list ) { + + // If we have memory from a past run, we should fire after adding + if ( memory && !firing ) { + firingIndex = list.length - 1; + queue.push( memory ); + } + + ( function add( args ) { + jQuery.each( args, function( _, arg ) { + if ( jQuery.isFunction( arg ) ) { + if ( !options.unique || !self.has( arg ) ) { + list.push( arg ); + } + } else if ( arg && arg.length && jQuery.type( arg ) !== "string" ) { + + // Inspect recursively + add( arg ); + } + } ); + } )( arguments ); + + if ( memory && !firing ) { + fire(); + } + } + return this; + }, + + // Remove a callback from the list + remove: function() { + jQuery.each( arguments, function( _, arg ) { + var index; + while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { + list.splice( index, 1 ); + + // Handle firing indexes + if ( index <= firingIndex ) { + firingIndex--; + } + } + } ); + return this; + }, + + // Check if a given callback is in the list. + // If no argument is given, return whether or not list has callbacks attached. + has: function( fn ) { + return fn ? + jQuery.inArray( fn, list ) > -1 : + list.length > 0; + }, + + // Remove all callbacks from the list + empty: function() { + if ( list ) { + list = []; + } + return this; + }, + + // Disable .fire and .add + // Abort any current/pending executions + // Clear all callbacks and values + disable: function() { + locked = queue = []; + list = memory = ""; + return this; + }, + disabled: function() { + return !list; + }, + + // Disable .fire + // Also disable .add unless we have memory (since it would have no effect) + // Abort any pending executions + lock: function() { + locked = true; + if ( !memory ) { + self.disable(); + } + return this; + }, + locked: function() { + return !!locked; + }, + + // Call all callbacks with the given context and arguments + fireWith: function( context, args ) { + if ( !locked ) { + args = args || []; + args = [ context, args.slice ? args.slice() : args ]; + queue.push( args ); + if ( !firing ) { + fire(); + } + } + return this; + }, + + // Call all the callbacks with the given arguments + fire: function() { + self.fireWith( this, arguments ); + return this; + }, + + // To know if the callbacks have already been called at least once + fired: function() { + return !!fired; + } + }; + + return self; +}; + + +jQuery.extend( { + + Deferred: function( func ) { + var tuples = [ + + // action, add listener, listener list, final state + [ "resolve", "done", jQuery.Callbacks( "once memory" ), "resolved" ], + [ "reject", "fail", jQuery.Callbacks( "once memory" ), "rejected" ], + [ "notify", "progress", jQuery.Callbacks( "memory" ) ] + ], + state = "pending", + promise = { + state: function() { + return state; + }, + always: function() { + deferred.done( arguments ).fail( arguments ); + return this; + }, + then: function( /* fnDone, fnFail, fnProgress */ ) { + var fns = arguments; + return jQuery.Deferred( function( newDefer ) { + jQuery.each( tuples, function( i, tuple ) { + var fn = jQuery.isFunction( fns[ i ] ) && fns[ i ]; + + // deferred[ done | fail | progress ] for forwarding actions to newDefer + deferred[ tuple[ 1 ] ]( function() { + var returned = fn && fn.apply( this, arguments ); + if ( returned && jQuery.isFunction( returned.promise ) ) { + returned.promise() + .progress( newDefer.notify ) + .done( newDefer.resolve ) + .fail( newDefer.reject ); + } else { + newDefer[ tuple[ 0 ] + "With" ]( + this === promise ? newDefer.promise() : this, + fn ? [ returned ] : arguments + ); + } + } ); + } ); + fns = null; + } ).promise(); + }, + + // Get a promise for this deferred + // If obj is provided, the promise aspect is added to the object + promise: function( obj ) { + return obj != null ? jQuery.extend( obj, promise ) : promise; + } + }, + deferred = {}; + + // Keep pipe for back-compat + promise.pipe = promise.then; + + // Add list-specific methods + jQuery.each( tuples, function( i, tuple ) { + var list = tuple[ 2 ], + stateString = tuple[ 3 ]; + + // promise[ done | fail | progress ] = list.add + promise[ tuple[ 1 ] ] = list.add; + + // Handle state + if ( stateString ) { + list.add( function() { + + // state = [ resolved | rejected ] + state = stateString; + + // [ reject_list | resolve_list ].disable; progress_list.lock + }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock ); + } + + // deferred[ resolve | reject | notify ] + deferred[ tuple[ 0 ] ] = function() { + deferred[ tuple[ 0 ] + "With" ]( this === deferred ? promise : this, arguments ); + return this; + }; + deferred[ tuple[ 0 ] + "With" ] = list.fireWith; + } ); + + // Make the deferred a promise + promise.promise( deferred ); + + // Call given func if any + if ( func ) { + func.call( deferred, deferred ); + } + + // All done! + return deferred; + }, + + // Deferred helper + when: function( subordinate /* , ..., subordinateN */ ) { + var i = 0, + resolveValues = slice.call( arguments ), + length = resolveValues.length, + + // the count of uncompleted subordinates + remaining = length !== 1 || + ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0, + + // the master Deferred. + // If resolveValues consist of only a single Deferred, just use that. + deferred = remaining === 1 ? subordinate : jQuery.Deferred(), + + // Update function for both resolve and progress values + updateFunc = function( i, contexts, values ) { + return function( value ) { + contexts[ i ] = this; + values[ i ] = arguments.length > 1 ? slice.call( arguments ) : value; + if ( values === progressValues ) { + deferred.notifyWith( contexts, values ); + + } else if ( !( --remaining ) ) { + deferred.resolveWith( contexts, values ); + } + }; + }, + + progressValues, progressContexts, resolveContexts; + + // add listeners to Deferred subordinates; treat others as resolved + if ( length > 1 ) { + progressValues = new Array( length ); + progressContexts = new Array( length ); + resolveContexts = new Array( length ); + for ( ; i < length; i++ ) { + if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) { + resolveValues[ i ].promise() + .progress( updateFunc( i, progressContexts, progressValues ) ) + .done( updateFunc( i, resolveContexts, resolveValues ) ) + .fail( deferred.reject ); + } else { + --remaining; + } + } + } + + // if we're not waiting on anything, resolve the master + if ( !remaining ) { + deferred.resolveWith( resolveContexts, resolveValues ); + } + + return deferred.promise(); + } +} ); + + +// The deferred used on DOM ready +var readyList; + +jQuery.fn.ready = function( fn ) { + + // Add the callback + jQuery.ready.promise().done( fn ); + + return this; +}; + +jQuery.extend( { + + // Is the DOM ready to be used? Set to true once it occurs. + isReady: false, + + // A counter to track how many items to wait for before + // the ready event fires. See #6781 + readyWait: 1, + + // Hold (or release) the ready event + holdReady: function( hold ) { + if ( hold ) { + jQuery.readyWait++; + } else { + jQuery.ready( true ); + } + }, + + // Handle when the DOM is ready + ready: function( wait ) { + + // Abort if there are pending holds or we're already ready + if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) { + return; + } + + // Remember that the DOM is ready + jQuery.isReady = true; + + // If a normal DOM Ready event fired, decrement, and wait if need be + if ( wait !== true && --jQuery.readyWait > 0 ) { + return; + } + + // If there are functions bound, to execute + readyList.resolveWith( document, [ jQuery ] ); + + // Trigger any bound ready events + if ( jQuery.fn.triggerHandler ) { + jQuery( document ).triggerHandler( "ready" ); + jQuery( document ).off( "ready" ); + } + } +} ); + +/** + * Clean-up method for dom ready events + */ +function detach() { + if ( document.addEventListener ) { + document.removeEventListener( "DOMContentLoaded", completed ); + window.removeEventListener( "load", completed ); + + } else { + document.detachEvent( "onreadystatechange", completed ); + window.detachEvent( "onload", completed ); + } +} + +/** + * The ready event handler and self cleanup method + */ +function completed() { + + // readyState === "complete" is good enough for us to call the dom ready in oldIE + if ( document.addEventListener || + window.event.type === "load" || + document.readyState === "complete" ) { + + detach(); + jQuery.ready(); + } +} + +jQuery.ready.promise = function( obj ) { + if ( !readyList ) { + + readyList = jQuery.Deferred(); + + // Catch cases where $(document).ready() is called + // after the browser event has already occurred. + // Support: IE6-10 + // Older IE sometimes signals "interactive" too soon + if ( document.readyState === "complete" || + ( document.readyState !== "loading" && !document.documentElement.doScroll ) ) { + + // Handle it asynchronously to allow scripts the opportunity to delay ready + window.setTimeout( jQuery.ready ); + + // Standards-based browsers support DOMContentLoaded + } else if ( document.addEventListener ) { + + // Use the handy event callback + document.addEventListener( "DOMContentLoaded", completed ); + + // A fallback to window.onload, that will always work + window.addEventListener( "load", completed ); + + // If IE event model is used + } else { + + // Ensure firing before onload, maybe late but safe also for iframes + document.attachEvent( "onreadystatechange", completed ); + + // A fallback to window.onload, that will always work + window.attachEvent( "onload", completed ); + + // If IE and not a frame + // continually check to see if the document is ready + var top = false; + + try { + top = window.frameElement == null && document.documentElement; + } catch ( e ) {} + + if ( top && top.doScroll ) { + ( function doScrollCheck() { + if ( !jQuery.isReady ) { + + try { + + // Use the trick by Diego Perini + // http://javascript.nwbox.com/IEContentLoaded/ + top.doScroll( "left" ); + } catch ( e ) { + return window.setTimeout( doScrollCheck, 50 ); + } + + // detach all dom ready events + detach(); + + // and execute any waiting functions + jQuery.ready(); + } + } )(); + } + } + } + return readyList.promise( obj ); +}; + +// Kick off the DOM ready check even if the user does not +jQuery.ready.promise(); + + + + +// Support: IE<9 +// Iteration over object's inherited properties before its own +var i; +for ( i in jQuery( support ) ) { + break; +} +support.ownFirst = i === "0"; + +// Note: most support tests are defined in their respective modules. +// false until the test is run +support.inlineBlockNeedsLayout = false; + +// Execute ASAP in case we need to set body.style.zoom +jQuery( function() { + + // Minified: var a,b,c,d + var val, div, body, container; + + body = document.getElementsByTagName( "body" )[ 0 ]; + if ( !body || !body.style ) { + + // Return for frameset docs that don't have a body + return; + } + + // Setup + div = document.createElement( "div" ); + container = document.createElement( "div" ); + container.style.cssText = "position:absolute;border:0;width:0;height:0;top:0;left:-9999px"; + body.appendChild( container ).appendChild( div ); + + if ( typeof div.style.zoom !== "undefined" ) { + + // Support: IE<8 + // Check if natively block-level elements act like inline-block + // elements when setting their display to 'inline' and giving + // them layout + div.style.cssText = "display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1"; + + support.inlineBlockNeedsLayout = val = div.offsetWidth === 3; + if ( val ) { + + // Prevent IE 6 from affecting layout for positioned elements #11048 + // Prevent IE from shrinking the body in IE 7 mode #12869 + // Support: IE<8 + body.style.zoom = 1; + } + } + + body.removeChild( container ); +} ); + + +( function() { + var div = document.createElement( "div" ); + + // Support: IE<9 + support.deleteExpando = true; + try { + delete div.test; + } catch ( e ) { + support.deleteExpando = false; + } + + // Null elements to avoid leaks in IE. + div = null; +} )(); +var acceptData = function( elem ) { + var noData = jQuery.noData[ ( elem.nodeName + " " ).toLowerCase() ], + nodeType = +elem.nodeType || 1; + + // Do not set data on non-element DOM nodes because it will not be cleared (#8335). + return nodeType !== 1 && nodeType !== 9 ? + false : + + // Nodes accept data unless otherwise specified; rejection can be conditional + !noData || noData !== true && elem.getAttribute( "classid" ) === noData; +}; + + + + +var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/, + rmultiDash = /([A-Z])/g; + +function dataAttr( elem, key, data ) { + + // If nothing was found internally, try to fetch any + // data from the HTML5 data-* attribute + if ( data === undefined && elem.nodeType === 1 ) { + + var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase(); + + data = elem.getAttribute( name ); + + if ( typeof data === "string" ) { + try { + data = data === "true" ? true : + data === "false" ? false : + data === "null" ? null : + + // Only convert to a number if it doesn't change the string + +data + "" === data ? +data : + rbrace.test( data ) ? jQuery.parseJSON( data ) : + data; + } catch ( e ) {} + + // Make sure we set the data so it isn't changed later + jQuery.data( elem, key, data ); + + } else { + data = undefined; + } + } + + return data; +} + +// checks a cache object for emptiness +function isEmptyDataObject( obj ) { + var name; + for ( name in obj ) { + + // if the public data object is empty, the private is still empty + if ( name === "data" && jQuery.isEmptyObject( obj[ name ] ) ) { + continue; + } + if ( name !== "toJSON" ) { + return false; + } + } + + return true; +} + +function internalData( elem, name, data, pvt /* Internal Use Only */ ) { + if ( !acceptData( elem ) ) { + return; + } + + var ret, thisCache, + internalKey = jQuery.expando, + + // We have to handle DOM nodes and JS objects differently because IE6-7 + // can't GC object references properly across the DOM-JS boundary + isNode = elem.nodeType, + + // Only DOM nodes need the global jQuery cache; JS object data is + // attached directly to the object so GC can occur automatically + cache = isNode ? jQuery.cache : elem, + + // Only defining an ID for JS objects if its cache already exists allows + // the code to shortcut on the same path as a DOM node with no cache + id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey; + + // Avoid doing any more work than we need to when trying to get data on an + // object that has no data at all + if ( ( !id || !cache[ id ] || ( !pvt && !cache[ id ].data ) ) && + data === undefined && typeof name === "string" ) { + return; + } + + if ( !id ) { + + // Only DOM nodes need a new unique ID for each element since their data + // ends up in the global cache + if ( isNode ) { + id = elem[ internalKey ] = deletedIds.pop() || jQuery.guid++; + } else { + id = internalKey; + } + } + + if ( !cache[ id ] ) { + + // Avoid exposing jQuery metadata on plain JS objects when the object + // is serialized using JSON.stringify + cache[ id ] = isNode ? {} : { toJSON: jQuery.noop }; + } + + // An object can be passed to jQuery.data instead of a key/value pair; this gets + // shallow copied over onto the existing cache + if ( typeof name === "object" || typeof name === "function" ) { + if ( pvt ) { + cache[ id ] = jQuery.extend( cache[ id ], name ); + } else { + cache[ id ].data = jQuery.extend( cache[ id ].data, name ); + } + } + + thisCache = cache[ id ]; + + // jQuery data() is stored in a separate object inside the object's internal data + // cache in order to avoid key collisions between internal data and user-defined + // data. + if ( !pvt ) { + if ( !thisCache.data ) { + thisCache.data = {}; + } + + thisCache = thisCache.data; + } + + if ( data !== undefined ) { + thisCache[ jQuery.camelCase( name ) ] = data; + } + + // Check for both converted-to-camel and non-converted data property names + // If a data property was specified + if ( typeof name === "string" ) { + + // First Try to find as-is property data + ret = thisCache[ name ]; + + // Test for null|undefined property data + if ( ret == null ) { + + // Try to find the camelCased property + ret = thisCache[ jQuery.camelCase( name ) ]; + } + } else { + ret = thisCache; + } + + return ret; +} + +function internalRemoveData( elem, name, pvt ) { + if ( !acceptData( elem ) ) { + return; + } + + var thisCache, i, + isNode = elem.nodeType, + + // See jQuery.data for more information + cache = isNode ? jQuery.cache : elem, + id = isNode ? elem[ jQuery.expando ] : jQuery.expando; + + // If there is already no cache entry for this object, there is no + // purpose in continuing + if ( !cache[ id ] ) { + return; + } + + if ( name ) { + + thisCache = pvt ? cache[ id ] : cache[ id ].data; + + if ( thisCache ) { + + // Support array or space separated string names for data keys + if ( !jQuery.isArray( name ) ) { + + // try the string as a key before any manipulation + if ( name in thisCache ) { + name = [ name ]; + } else { + + // split the camel cased version by spaces unless a key with the spaces exists + name = jQuery.camelCase( name ); + if ( name in thisCache ) { + name = [ name ]; + } else { + name = name.split( " " ); + } + } + } else { + + // If "name" is an array of keys... + // When data is initially created, via ("key", "val") signature, + // keys will be converted to camelCase. + // Since there is no way to tell _how_ a key was added, remove + // both plain key and camelCase key. #12786 + // This will only penalize the array argument path. + name = name.concat( jQuery.map( name, jQuery.camelCase ) ); + } + + i = name.length; + while ( i-- ) { + delete thisCache[ name[ i ] ]; + } + + // If there is no data left in the cache, we want to continue + // and let the cache object itself get destroyed + if ( pvt ? !isEmptyDataObject( thisCache ) : !jQuery.isEmptyObject( thisCache ) ) { + return; + } + } + } + + // See jQuery.data for more information + if ( !pvt ) { + delete cache[ id ].data; + + // Don't destroy the parent cache unless the internal data object + // had been the only thing left in it + if ( !isEmptyDataObject( cache[ id ] ) ) { + return; + } + } + + // Destroy the cache + if ( isNode ) { + jQuery.cleanData( [ elem ], true ); + + // Use delete when supported for expandos or `cache` is not a window per isWindow (#10080) + /* jshint eqeqeq: false */ + } else if ( support.deleteExpando || cache != cache.window ) { + /* jshint eqeqeq: true */ + delete cache[ id ]; + + // When all else fails, undefined + } else { + cache[ id ] = undefined; + } +} + +jQuery.extend( { + cache: {}, + + // The following elements (space-suffixed to avoid Object.prototype collisions) + // throw uncatchable exceptions if you attempt to set expando properties + noData: { + "applet ": true, + "embed ": true, + + // ...but Flash objects (which have this classid) *can* handle expandos + "object ": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" + }, + + hasData: function( elem ) { + elem = elem.nodeType ? jQuery.cache[ elem[ jQuery.expando ] ] : elem[ jQuery.expando ]; + return !!elem && !isEmptyDataObject( elem ); + }, + + data: function( elem, name, data ) { + return internalData( elem, name, data ); + }, + + removeData: function( elem, name ) { + return internalRemoveData( elem, name ); + }, + + // For internal use only. + _data: function( elem, name, data ) { + return internalData( elem, name, data, true ); + }, + + _removeData: function( elem, name ) { + return internalRemoveData( elem, name, true ); + } +} ); + +jQuery.fn.extend( { + data: function( key, value ) { + var i, name, data, + elem = this[ 0 ], + attrs = elem && elem.attributes; + + // Special expections of .data basically thwart jQuery.access, + // so implement the relevant behavior ourselves + + // Gets all values + if ( key === undefined ) { + if ( this.length ) { + data = jQuery.data( elem ); + + if ( elem.nodeType === 1 && !jQuery._data( elem, "parsedAttrs" ) ) { + i = attrs.length; + while ( i-- ) { + + // Support: IE11+ + // The attrs elements can be null (#14894) + if ( attrs[ i ] ) { + name = attrs[ i ].name; + if ( name.indexOf( "data-" ) === 0 ) { + name = jQuery.camelCase( name.slice( 5 ) ); + dataAttr( elem, name, data[ name ] ); + } + } + } + jQuery._data( elem, "parsedAttrs", true ); + } + } + + return data; + } + + // Sets multiple values + if ( typeof key === "object" ) { + return this.each( function() { + jQuery.data( this, key ); + } ); + } + + return arguments.length > 1 ? + + // Sets one value + this.each( function() { + jQuery.data( this, key, value ); + } ) : + + // Gets one value + // Try to fetch any internally stored data first + elem ? dataAttr( elem, key, jQuery.data( elem, key ) ) : undefined; + }, + + removeData: function( key ) { + return this.each( function() { + jQuery.removeData( this, key ); + } ); + } +} ); + + +jQuery.extend( { + queue: function( elem, type, data ) { + var queue; + + if ( elem ) { + type = ( type || "fx" ) + "queue"; + queue = jQuery._data( elem, type ); + + // Speed up dequeue by getting out quickly if this is just a lookup + if ( data ) { + if ( !queue || jQuery.isArray( data ) ) { + queue = jQuery._data( elem, type, jQuery.makeArray( data ) ); + } else { + queue.push( data ); + } + } + return queue || []; + } + }, + + dequeue: function( elem, type ) { + type = type || "fx"; + + var queue = jQuery.queue( elem, type ), + startLength = queue.length, + fn = queue.shift(), + hooks = jQuery._queueHooks( elem, type ), + next = function() { + jQuery.dequeue( elem, type ); + }; + + // If the fx queue is dequeued, always remove the progress sentinel + if ( fn === "inprogress" ) { + fn = queue.shift(); + startLength--; + } + + if ( fn ) { + + // Add a progress sentinel to prevent the fx queue from being + // automatically dequeued + if ( type === "fx" ) { + queue.unshift( "inprogress" ); + } + + // clear up the last queue stop function + delete hooks.stop; + fn.call( elem, next, hooks ); + } + + if ( !startLength && hooks ) { + hooks.empty.fire(); + } + }, + + // not intended for public consumption - generates a queueHooks object, + // or returns the current one + _queueHooks: function( elem, type ) { + var key = type + "queueHooks"; + return jQuery._data( elem, key ) || jQuery._data( elem, key, { + empty: jQuery.Callbacks( "once memory" ).add( function() { + jQuery._removeData( elem, type + "queue" ); + jQuery._removeData( elem, key ); + } ) + } ); + } +} ); + +jQuery.fn.extend( { + queue: function( type, data ) { + var setter = 2; + + if ( typeof type !== "string" ) { + data = type; + type = "fx"; + setter--; + } + + if ( arguments.length < setter ) { + return jQuery.queue( this[ 0 ], type ); + } + + return data === undefined ? + this : + this.each( function() { + var queue = jQuery.queue( this, type, data ); + + // ensure a hooks for this queue + jQuery._queueHooks( this, type ); + + if ( type === "fx" && queue[ 0 ] !== "inprogress" ) { + jQuery.dequeue( this, type ); + } + } ); + }, + dequeue: function( type ) { + return this.each( function() { + jQuery.dequeue( this, type ); + } ); + }, + clearQueue: function( type ) { + return this.queue( type || "fx", [] ); + }, + + // Get a promise resolved when queues of a certain type + // are emptied (fx is the type by default) + promise: function( type, obj ) { + var tmp, + count = 1, + defer = jQuery.Deferred(), + elements = this, + i = this.length, + resolve = function() { + if ( !( --count ) ) { + defer.resolveWith( elements, [ elements ] ); + } + }; + + if ( typeof type !== "string" ) { + obj = type; + type = undefined; + } + type = type || "fx"; + + while ( i-- ) { + tmp = jQuery._data( elements[ i ], type + "queueHooks" ); + if ( tmp && tmp.empty ) { + count++; + tmp.empty.add( resolve ); + } + } + resolve(); + return defer.promise( obj ); + } +} ); + + +( function() { + var shrinkWrapBlocksVal; + + support.shrinkWrapBlocks = function() { + if ( shrinkWrapBlocksVal != null ) { + return shrinkWrapBlocksVal; + } + + // Will be changed later if needed. + shrinkWrapBlocksVal = false; + + // Minified: var b,c,d + var div, body, container; + + body = document.getElementsByTagName( "body" )[ 0 ]; + if ( !body || !body.style ) { + + // Test fired too early or in an unsupported environment, exit. + return; + } + + // Setup + div = document.createElement( "div" ); + container = document.createElement( "div" ); + container.style.cssText = "position:absolute;border:0;width:0;height:0;top:0;left:-9999px"; + body.appendChild( container ).appendChild( div ); + + // Support: IE6 + // Check if elements with layout shrink-wrap their children + if ( typeof div.style.zoom !== "undefined" ) { + + // Reset CSS: box-sizing; display; margin; border + div.style.cssText = + + // Support: Firefox<29, Android 2.3 + // Vendor-prefix box-sizing + "-webkit-box-sizing:content-box;-moz-box-sizing:content-box;" + + "box-sizing:content-box;display:block;margin:0;border:0;" + + "padding:1px;width:1px;zoom:1"; + div.appendChild( document.createElement( "div" ) ).style.width = "5px"; + shrinkWrapBlocksVal = div.offsetWidth !== 3; + } + + body.removeChild( container ); + + return shrinkWrapBlocksVal; + }; + +} )(); +var pnum = ( /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/ ).source; + +var rcssNum = new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" ); + + +var cssExpand = [ "Top", "Right", "Bottom", "Left" ]; + +var isHidden = function( elem, el ) { + + // isHidden might be called from jQuery#filter function; + // in that case, element will be second argument + elem = el || elem; + return jQuery.css( elem, "display" ) === "none" || + !jQuery.contains( elem.ownerDocument, elem ); + }; + + + +function adjustCSS( elem, prop, valueParts, tween ) { + var adjusted, + scale = 1, + maxIterations = 20, + currentValue = tween ? + function() { return tween.cur(); } : + function() { return jQuery.css( elem, prop, "" ); }, + initial = currentValue(), + unit = valueParts && valueParts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ), + + // Starting value computation is required for potential unit mismatches + initialInUnit = ( jQuery.cssNumber[ prop ] || unit !== "px" && +initial ) && + rcssNum.exec( jQuery.css( elem, prop ) ); + + if ( initialInUnit && initialInUnit[ 3 ] !== unit ) { + + // Trust units reported by jQuery.css + unit = unit || initialInUnit[ 3 ]; + + // Make sure we update the tween properties later on + valueParts = valueParts || []; + + // Iteratively approximate from a nonzero starting point + initialInUnit = +initial || 1; + + do { + + // If previous iteration zeroed out, double until we get *something*. + // Use string for doubling so we don't accidentally see scale as unchanged below + scale = scale || ".5"; + + // Adjust and apply + initialInUnit = initialInUnit / scale; + jQuery.style( elem, prop, initialInUnit + unit ); + + // Update scale, tolerating zero or NaN from tween.cur() + // Break the loop if scale is unchanged or perfect, or if we've just had enough. + } while ( + scale !== ( scale = currentValue() / initial ) && scale !== 1 && --maxIterations + ); + } + + if ( valueParts ) { + initialInUnit = +initialInUnit || +initial || 0; + + // Apply relative offset (+=/-=) if specified + adjusted = valueParts[ 1 ] ? + initialInUnit + ( valueParts[ 1 ] + 1 ) * valueParts[ 2 ] : + +valueParts[ 2 ]; + if ( tween ) { + tween.unit = unit; + tween.start = initialInUnit; + tween.end = adjusted; + } + } + return adjusted; +} + + +// Multifunctional method to get and set values of a collection +// The value/s can optionally be executed if it's a function +var access = function( elems, fn, key, value, chainable, emptyGet, raw ) { + var i = 0, + length = elems.length, + bulk = key == null; + + // Sets many values + if ( jQuery.type( key ) === "object" ) { + chainable = true; + for ( i in key ) { + access( elems, fn, i, key[ i ], true, emptyGet, raw ); + } + + // Sets one value + } else if ( value !== undefined ) { + chainable = true; + + if ( !jQuery.isFunction( value ) ) { + raw = true; + } + + if ( bulk ) { + + // Bulk operations run against the entire set + if ( raw ) { + fn.call( elems, value ); + fn = null; + + // ...except when executing function values + } else { + bulk = fn; + fn = function( elem, key, value ) { + return bulk.call( jQuery( elem ), value ); + }; + } + } + + if ( fn ) { + for ( ; i < length; i++ ) { + fn( + elems[ i ], + key, + raw ? value : value.call( elems[ i ], i, fn( elems[ i ], key ) ) + ); + } + } + } + + return chainable ? + elems : + + // Gets + bulk ? + fn.call( elems ) : + length ? fn( elems[ 0 ], key ) : emptyGet; +}; +var rcheckableType = ( /^(?:checkbox|radio)$/i ); + +var rtagName = ( /<([\w:-]+)/ ); + +var rscriptType = ( /^$|\/(?:java|ecma)script/i ); + +var rleadingWhitespace = ( /^\s+/ ); + +var nodeNames = "abbr|article|aside|audio|bdi|canvas|data|datalist|" + + "details|dialog|figcaption|figure|footer|header|hgroup|main|" + + "mark|meter|nav|output|picture|progress|section|summary|template|time|video"; + + + +function createSafeFragment( document ) { + var list = nodeNames.split( "|" ), + safeFrag = document.createDocumentFragment(); + + if ( safeFrag.createElement ) { + while ( list.length ) { + safeFrag.createElement( + list.pop() + ); + } + } + return safeFrag; +} + + +( function() { + var div = document.createElement( "div" ), + fragment = document.createDocumentFragment(), + input = document.createElement( "input" ); + + // Setup + div.innerHTML = "
a"; + + // IE strips leading whitespace when .innerHTML is used + support.leadingWhitespace = div.firstChild.nodeType === 3; + + // Make sure that tbody elements aren't automatically inserted + // IE will insert them into empty tables + support.tbody = !div.getElementsByTagName( "tbody" ).length; + + // Make sure that link elements get serialized correctly by innerHTML + // This requires a wrapper element in IE + support.htmlSerialize = !!div.getElementsByTagName( "link" ).length; + + // Makes sure cloning an html5 element does not cause problems + // Where outerHTML is undefined, this still works + support.html5Clone = + document.createElement( "nav" ).cloneNode( true ).outerHTML !== "<:nav>"; + + // Check if a disconnected checkbox will retain its checked + // value of true after appended to the DOM (IE6/7) + input.type = "checkbox"; + input.checked = true; + fragment.appendChild( input ); + support.appendChecked = input.checked; + + // Make sure textarea (and checkbox) defaultValue is properly cloned + // Support: IE6-IE11+ + div.innerHTML = ""; + support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue; + + // #11217 - WebKit loses check when the name is after the checked attribute + fragment.appendChild( div ); + + // Support: Windows Web Apps (WWA) + // `name` and `type` must use .setAttribute for WWA (#14901) + input = document.createElement( "input" ); + input.setAttribute( "type", "radio" ); + input.setAttribute( "checked", "checked" ); + input.setAttribute( "name", "t" ); + + div.appendChild( input ); + + // Support: Safari 5.1, iOS 5.1, Android 4.x, Android 2.3 + // old WebKit doesn't clone checked state correctly in fragments + support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked; + + // Support: IE<9 + // Cloned elements keep attachEvent handlers, we use addEventListener on IE9+ + support.noCloneEvent = !!div.addEventListener; + + // Support: IE<9 + // Since attributes and properties are the same in IE, + // cleanData must set properties to undefined rather than use removeAttribute + div[ jQuery.expando ] = 1; + support.attributes = !div.getAttribute( jQuery.expando ); +} )(); + + +// We have to close these tags to support XHTML (#13200) +var wrapMap = { + option: [ 1, "" ], + legend: [ 1, "
", "
" ], + area: [ 1, "", "" ], + + // Support: IE8 + param: [ 1, "", "" ], + thead: [ 1, "", "
" ], + tr: [ 2, "", "
" ], + col: [ 2, "", "
" ], + td: [ 3, "", "
" ], + + // IE6-8 can't serialize link, script, style, or any html5 (NoScope) tags, + // unless wrapped in a div with non-breaking characters in front of it. + _default: support.htmlSerialize ? [ 0, "", "" ] : [ 1, "X
", "
" ] +}; + +// Support: IE8-IE9 +wrapMap.optgroup = wrapMap.option; + +wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; +wrapMap.th = wrapMap.td; + + +function getAll( context, tag ) { + var elems, elem, + i = 0, + found = typeof context.getElementsByTagName !== "undefined" ? + context.getElementsByTagName( tag || "*" ) : + typeof context.querySelectorAll !== "undefined" ? + context.querySelectorAll( tag || "*" ) : + undefined; + + if ( !found ) { + for ( found = [], elems = context.childNodes || context; + ( elem = elems[ i ] ) != null; + i++ + ) { + if ( !tag || jQuery.nodeName( elem, tag ) ) { + found.push( elem ); + } else { + jQuery.merge( found, getAll( elem, tag ) ); + } + } + } + + return tag === undefined || tag && jQuery.nodeName( context, tag ) ? + jQuery.merge( [ context ], found ) : + found; +} + + +// Mark scripts as having already been evaluated +function setGlobalEval( elems, refElements ) { + var elem, + i = 0; + for ( ; ( elem = elems[ i ] ) != null; i++ ) { + jQuery._data( + elem, + "globalEval", + !refElements || jQuery._data( refElements[ i ], "globalEval" ) + ); + } +} + + +var rhtml = /<|&#?\w+;/, + rtbody = / from table fragments + if ( !support.tbody ) { + + // String was a , *may* have spurious + elem = tag === "table" && !rtbody.test( elem ) ? + tmp.firstChild : + + // String was a bare or + wrap[ 1 ] === "
" && !rtbody.test( elem ) ? + tmp : + 0; + + j = elem && elem.childNodes.length; + while ( j-- ) { + if ( jQuery.nodeName( ( tbody = elem.childNodes[ j ] ), "tbody" ) && + !tbody.childNodes.length ) { + + elem.removeChild( tbody ); + } + } + } + + jQuery.merge( nodes, tmp.childNodes ); + + // Fix #12392 for WebKit and IE > 9 + tmp.textContent = ""; + + // Fix #12392 for oldIE + while ( tmp.firstChild ) { + tmp.removeChild( tmp.firstChild ); + } + + // Remember the top-level container for proper cleanup + tmp = safe.lastChild; + } + } + } + + // Fix #11356: Clear elements from fragment + if ( tmp ) { + safe.removeChild( tmp ); + } + + // Reset defaultChecked for any radios and checkboxes + // about to be appended to the DOM in IE 6/7 (#8060) + if ( !support.appendChecked ) { + jQuery.grep( getAll( nodes, "input" ), fixDefaultChecked ); + } + + i = 0; + while ( ( elem = nodes[ i++ ] ) ) { + + // Skip elements already in the context collection (trac-4087) + if ( selection && jQuery.inArray( elem, selection ) > -1 ) { + if ( ignored ) { + ignored.push( elem ); + } + + continue; + } + + contains = jQuery.contains( elem.ownerDocument, elem ); + + // Append to fragment + tmp = getAll( safe.appendChild( elem ), "script" ); + + // Preserve script evaluation history + if ( contains ) { + setGlobalEval( tmp ); + } + + // Capture executables + if ( scripts ) { + j = 0; + while ( ( elem = tmp[ j++ ] ) ) { + if ( rscriptType.test( elem.type || "" ) ) { + scripts.push( elem ); + } + } + } + } + + tmp = null; + + return safe; +} + + +( function() { + var i, eventName, + div = document.createElement( "div" ); + + // Support: IE<9 (lack submit/change bubble), Firefox (lack focus(in | out) events) + for ( i in { submit: true, change: true, focusin: true } ) { + eventName = "on" + i; + + if ( !( support[ i ] = eventName in window ) ) { + + // Beware of CSP restrictions (https://developer.mozilla.org/en/Security/CSP) + div.setAttribute( eventName, "t" ); + support[ i ] = div.attributes[ eventName ].expando === false; + } + } + + // Null elements to avoid leaks in IE. + div = null; +} )(); + + +var rformElems = /^(?:input|select|textarea)$/i, + rkeyEvent = /^key/, + rmouseEvent = /^(?:mouse|pointer|contextmenu|drag|drop)|click/, + rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, + rtypenamespace = /^([^.]*)(?:\.(.+)|)/; + +function returnTrue() { + return true; +} + +function returnFalse() { + return false; +} + +// Support: IE9 +// See #13393 for more info +function safeActiveElement() { + try { + return document.activeElement; + } catch ( err ) { } +} + +function on( elem, types, selector, data, fn, one ) { + var origFn, type; + + // Types can be a map of types/handlers + if ( typeof types === "object" ) { + + // ( types-Object, selector, data ) + if ( typeof selector !== "string" ) { + + // ( types-Object, data ) + data = data || selector; + selector = undefined; + } + for ( type in types ) { + on( elem, type, selector, data, types[ type ], one ); + } + return elem; + } + + if ( data == null && fn == null ) { + + // ( types, fn ) + fn = selector; + data = selector = undefined; + } else if ( fn == null ) { + if ( typeof selector === "string" ) { + + // ( types, selector, fn ) + fn = data; + data = undefined; + } else { + + // ( types, data, fn ) + fn = data; + data = selector; + selector = undefined; + } + } + if ( fn === false ) { + fn = returnFalse; + } else if ( !fn ) { + return elem; + } + + if ( one === 1 ) { + origFn = fn; + fn = function( event ) { + + // Can use an empty set, since event contains the info + jQuery().off( event ); + return origFn.apply( this, arguments ); + }; + + // Use same guid so caller can remove using origFn + fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); + } + return elem.each( function() { + jQuery.event.add( this, types, fn, data, selector ); + } ); +} + +/* + * Helper functions for managing events -- not part of the public interface. + * Props to Dean Edwards' addEvent library for many of the ideas. + */ +jQuery.event = { + + global: {}, + + add: function( elem, types, handler, data, selector ) { + var tmp, events, t, handleObjIn, + special, eventHandle, handleObj, + handlers, type, namespaces, origType, + elemData = jQuery._data( elem ); + + // Don't attach events to noData or text/comment nodes (but allow plain objects) + if ( !elemData ) { + return; + } + + // Caller can pass in an object of custom data in lieu of the handler + if ( handler.handler ) { + handleObjIn = handler; + handler = handleObjIn.handler; + selector = handleObjIn.selector; + } + + // Make sure that the handler has a unique ID, used to find/remove it later + if ( !handler.guid ) { + handler.guid = jQuery.guid++; + } + + // Init the element's event structure and main handler, if this is the first + if ( !( events = elemData.events ) ) { + events = elemData.events = {}; + } + if ( !( eventHandle = elemData.handle ) ) { + eventHandle = elemData.handle = function( e ) { + + // Discard the second event of a jQuery.event.trigger() and + // when an event is called after a page has unloaded + return typeof jQuery !== "undefined" && + ( !e || jQuery.event.triggered !== e.type ) ? + jQuery.event.dispatch.apply( eventHandle.elem, arguments ) : + undefined; + }; + + // Add elem as a property of the handle fn to prevent a memory leak + // with IE non-native events + eventHandle.elem = elem; + } + + // Handle multiple events separated by a space + types = ( types || "" ).match( rnotwhite ) || [ "" ]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[ t ] ) || []; + type = origType = tmp[ 1 ]; + namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); + + // There *must* be a type, no attaching namespace-only handlers + if ( !type ) { + continue; + } + + // If event changes its type, use the special event handlers for the changed type + special = jQuery.event.special[ type ] || {}; + + // If selector defined, determine special event api type, otherwise given type + type = ( selector ? special.delegateType : special.bindType ) || type; + + // Update special based on newly reset type + special = jQuery.event.special[ type ] || {}; + + // handleObj is passed to all event handlers + handleObj = jQuery.extend( { + type: type, + origType: origType, + data: data, + handler: handler, + guid: handler.guid, + selector: selector, + needsContext: selector && jQuery.expr.match.needsContext.test( selector ), + namespace: namespaces.join( "." ) + }, handleObjIn ); + + // Init the event handler queue if we're the first + if ( !( handlers = events[ type ] ) ) { + handlers = events[ type ] = []; + handlers.delegateCount = 0; + + // Only use addEventListener/attachEvent if the special events handler returns false + if ( !special.setup || + special.setup.call( elem, data, namespaces, eventHandle ) === false ) { + + // Bind the global event handler to the element + if ( elem.addEventListener ) { + elem.addEventListener( type, eventHandle, false ); + + } else if ( elem.attachEvent ) { + elem.attachEvent( "on" + type, eventHandle ); + } + } + } + + if ( special.add ) { + special.add.call( elem, handleObj ); + + if ( !handleObj.handler.guid ) { + handleObj.handler.guid = handler.guid; + } + } + + // Add to the element's handler list, delegates in front + if ( selector ) { + handlers.splice( handlers.delegateCount++, 0, handleObj ); + } else { + handlers.push( handleObj ); + } + + // Keep track of which events have ever been used, for event optimization + jQuery.event.global[ type ] = true; + } + + // Nullify elem to prevent memory leaks in IE + elem = null; + }, + + // Detach an event or set of events from an element + remove: function( elem, types, handler, selector, mappedTypes ) { + var j, handleObj, tmp, + origCount, t, events, + special, handlers, type, + namespaces, origType, + elemData = jQuery.hasData( elem ) && jQuery._data( elem ); + + if ( !elemData || !( events = elemData.events ) ) { + return; + } + + // Once for each type.namespace in types; type may be omitted + types = ( types || "" ).match( rnotwhite ) || [ "" ]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[ t ] ) || []; + type = origType = tmp[ 1 ]; + namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); + + // Unbind all events (on this namespace, if provided) for the element + if ( !type ) { + for ( type in events ) { + jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); + } + continue; + } + + special = jQuery.event.special[ type ] || {}; + type = ( selector ? special.delegateType : special.bindType ) || type; + handlers = events[ type ] || []; + tmp = tmp[ 2 ] && + new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ); + + // Remove matching events + origCount = j = handlers.length; + while ( j-- ) { + handleObj = handlers[ j ]; + + if ( ( mappedTypes || origType === handleObj.origType ) && + ( !handler || handler.guid === handleObj.guid ) && + ( !tmp || tmp.test( handleObj.namespace ) ) && + ( !selector || selector === handleObj.selector || + selector === "**" && handleObj.selector ) ) { + handlers.splice( j, 1 ); + + if ( handleObj.selector ) { + handlers.delegateCount--; + } + if ( special.remove ) { + special.remove.call( elem, handleObj ); + } + } + } + + // Remove generic event handler if we removed something and no more handlers exist + // (avoids potential for endless recursion during removal of special event handlers) + if ( origCount && !handlers.length ) { + if ( !special.teardown || + special.teardown.call( elem, namespaces, elemData.handle ) === false ) { + + jQuery.removeEvent( elem, type, elemData.handle ); + } + + delete events[ type ]; + } + } + + // Remove the expando if it's no longer used + if ( jQuery.isEmptyObject( events ) ) { + delete elemData.handle; + + // removeData also checks for emptiness and clears the expando if empty + // so use it instead of delete + jQuery._removeData( elem, "events" ); + } + }, + + trigger: function( event, data, elem, onlyHandlers ) { + var handle, ontype, cur, + bubbleType, special, tmp, i, + eventPath = [ elem || document ], + type = hasOwn.call( event, "type" ) ? event.type : event, + namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split( "." ) : []; + + cur = tmp = elem = elem || document; + + // Don't do events on text and comment nodes + if ( elem.nodeType === 3 || elem.nodeType === 8 ) { + return; + } + + // focus/blur morphs to focusin/out; ensure we're not firing them right now + if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { + return; + } + + if ( type.indexOf( "." ) > -1 ) { + + // Namespaced trigger; create a regexp to match event type in handle() + namespaces = type.split( "." ); + type = namespaces.shift(); + namespaces.sort(); + } + ontype = type.indexOf( ":" ) < 0 && "on" + type; + + // Caller can pass in a jQuery.Event object, Object, or just an event type string + event = event[ jQuery.expando ] ? + event : + new jQuery.Event( type, typeof event === "object" && event ); + + // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true) + event.isTrigger = onlyHandlers ? 2 : 3; + event.namespace = namespaces.join( "." ); + event.rnamespace = event.namespace ? + new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ) : + null; + + // Clean up the event in case it is being reused + event.result = undefined; + if ( !event.target ) { + event.target = elem; + } + + // Clone any incoming data and prepend the event, creating the handler arg list + data = data == null ? + [ event ] : + jQuery.makeArray( data, [ event ] ); + + // Allow special events to draw outside the lines + special = jQuery.event.special[ type ] || {}; + if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) { + return; + } + + // Determine event propagation path in advance, per W3C events spec (#9951) + // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) + if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) { + + bubbleType = special.delegateType || type; + if ( !rfocusMorph.test( bubbleType + type ) ) { + cur = cur.parentNode; + } + for ( ; cur; cur = cur.parentNode ) { + eventPath.push( cur ); + tmp = cur; + } + + // Only add window if we got to document (e.g., not plain obj or detached DOM) + if ( tmp === ( elem.ownerDocument || document ) ) { + eventPath.push( tmp.defaultView || tmp.parentWindow || window ); + } + } + + // Fire handlers on the event path + i = 0; + while ( ( cur = eventPath[ i++ ] ) && !event.isPropagationStopped() ) { + + event.type = i > 1 ? + bubbleType : + special.bindType || type; + + // jQuery handler + handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && + jQuery._data( cur, "handle" ); + + if ( handle ) { + handle.apply( cur, data ); + } + + // Native handler + handle = ontype && cur[ ontype ]; + if ( handle && handle.apply && acceptData( cur ) ) { + event.result = handle.apply( cur, data ); + if ( event.result === false ) { + event.preventDefault(); + } + } + } + event.type = type; + + // If nobody prevented the default action, do it now + if ( !onlyHandlers && !event.isDefaultPrevented() ) { + + if ( + ( !special._default || + special._default.apply( eventPath.pop(), data ) === false + ) && acceptData( elem ) + ) { + + // Call a native DOM method on the target with the same name name as the event. + // Can't use an .isFunction() check here because IE6/7 fails that test. + // Don't do default actions on window, that's where global variables be (#6170) + if ( ontype && elem[ type ] && !jQuery.isWindow( elem ) ) { + + // Don't re-trigger an onFOO event when we call its FOO() method + tmp = elem[ ontype ]; + + if ( tmp ) { + elem[ ontype ] = null; + } + + // Prevent re-triggering of the same event, since we already bubbled it above + jQuery.event.triggered = type; + try { + elem[ type ](); + } catch ( e ) { + + // IE<9 dies on focus/blur to hidden element (#1486,#12518) + // only reproducible on winXP IE8 native, not IE9 in IE8 mode + } + jQuery.event.triggered = undefined; + + if ( tmp ) { + elem[ ontype ] = tmp; + } + } + } + } + + return event.result; + }, + + dispatch: function( event ) { + + // Make a writable jQuery.Event from the native event object + event = jQuery.event.fix( event ); + + var i, j, ret, matched, handleObj, + handlerQueue = [], + args = slice.call( arguments ), + handlers = ( jQuery._data( this, "events" ) || {} )[ event.type ] || [], + special = jQuery.event.special[ event.type ] || {}; + + // Use the fix-ed jQuery.Event rather than the (read-only) native event + args[ 0 ] = event; + event.delegateTarget = this; + + // Call the preDispatch hook for the mapped type, and let it bail if desired + if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { + return; + } + + // Determine handlers + handlerQueue = jQuery.event.handlers.call( this, event, handlers ); + + // Run delegates first; they may want to stop propagation beneath us + i = 0; + while ( ( matched = handlerQueue[ i++ ] ) && !event.isPropagationStopped() ) { + event.currentTarget = matched.elem; + + j = 0; + while ( ( handleObj = matched.handlers[ j++ ] ) && + !event.isImmediatePropagationStopped() ) { + + // Triggered event must either 1) have no namespace, or 2) have namespace(s) + // a subset or equal to those in the bound event (both can have no namespace). + if ( !event.rnamespace || event.rnamespace.test( handleObj.namespace ) ) { + + event.handleObj = handleObj; + event.data = handleObj.data; + + ret = ( ( jQuery.event.special[ handleObj.origType ] || {} ).handle || + handleObj.handler ).apply( matched.elem, args ); + + if ( ret !== undefined ) { + if ( ( event.result = ret ) === false ) { + event.preventDefault(); + event.stopPropagation(); + } + } + } + } + } + + // Call the postDispatch hook for the mapped type + if ( special.postDispatch ) { + special.postDispatch.call( this, event ); + } + + return event.result; + }, + + handlers: function( event, handlers ) { + var i, matches, sel, handleObj, + handlerQueue = [], + delegateCount = handlers.delegateCount, + cur = event.target; + + // Support (at least): Chrome, IE9 + // Find delegate handlers + // Black-hole SVG instance trees (#13180) + // + // Support: Firefox<=42+ + // Avoid non-left-click in FF but don't block IE radio events (#3861, gh-2343) + if ( delegateCount && cur.nodeType && + ( event.type !== "click" || isNaN( event.button ) || event.button < 1 ) ) { + + /* jshint eqeqeq: false */ + for ( ; cur != this; cur = cur.parentNode || this ) { + /* jshint eqeqeq: true */ + + // Don't check non-elements (#13208) + // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764) + if ( cur.nodeType === 1 && ( cur.disabled !== true || event.type !== "click" ) ) { + matches = []; + for ( i = 0; i < delegateCount; i++ ) { + handleObj = handlers[ i ]; + + // Don't conflict with Object.prototype properties (#13203) + sel = handleObj.selector + " "; + + if ( matches[ sel ] === undefined ) { + matches[ sel ] = handleObj.needsContext ? + jQuery( sel, this ).index( cur ) > -1 : + jQuery.find( sel, this, null, [ cur ] ).length; + } + if ( matches[ sel ] ) { + matches.push( handleObj ); + } + } + if ( matches.length ) { + handlerQueue.push( { elem: cur, handlers: matches } ); + } + } + } + } + + // Add the remaining (directly-bound) handlers + if ( delegateCount < handlers.length ) { + handlerQueue.push( { elem: this, handlers: handlers.slice( delegateCount ) } ); + } + + return handlerQueue; + }, + + fix: function( event ) { + if ( event[ jQuery.expando ] ) { + return event; + } + + // Create a writable copy of the event object and normalize some properties + var i, prop, copy, + type = event.type, + originalEvent = event, + fixHook = this.fixHooks[ type ]; + + if ( !fixHook ) { + this.fixHooks[ type ] = fixHook = + rmouseEvent.test( type ) ? this.mouseHooks : + rkeyEvent.test( type ) ? this.keyHooks : + {}; + } + copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props; + + event = new jQuery.Event( originalEvent ); + + i = copy.length; + while ( i-- ) { + prop = copy[ i ]; + event[ prop ] = originalEvent[ prop ]; + } + + // Support: IE<9 + // Fix target property (#1925) + if ( !event.target ) { + event.target = originalEvent.srcElement || document; + } + + // Support: Safari 6-8+ + // Target should not be a text node (#504, #13143) + if ( event.target.nodeType === 3 ) { + event.target = event.target.parentNode; + } + + // Support: IE<9 + // For mouse/key events, metaKey==false if it's undefined (#3368, #11328) + event.metaKey = !!event.metaKey; + + return fixHook.filter ? fixHook.filter( event, originalEvent ) : event; + }, + + // Includes some event props shared by KeyEvent and MouseEvent + props: ( "altKey bubbles cancelable ctrlKey currentTarget detail eventPhase " + + "metaKey relatedTarget shiftKey target timeStamp view which" ).split( " " ), + + fixHooks: {}, + + keyHooks: { + props: "char charCode key keyCode".split( " " ), + filter: function( event, original ) { + + // Add which for key events + if ( event.which == null ) { + event.which = original.charCode != null ? original.charCode : original.keyCode; + } + + return event; + } + }, + + mouseHooks: { + props: ( "button buttons clientX clientY fromElement offsetX offsetY " + + "pageX pageY screenX screenY toElement" ).split( " " ), + filter: function( event, original ) { + var body, eventDoc, doc, + button = original.button, + fromElement = original.fromElement; + + // Calculate pageX/Y if missing and clientX/Y available + if ( event.pageX == null && original.clientX != null ) { + eventDoc = event.target.ownerDocument || document; + doc = eventDoc.documentElement; + body = eventDoc.body; + + event.pageX = original.clientX + + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - + ( doc && doc.clientLeft || body && body.clientLeft || 0 ); + event.pageY = original.clientY + + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - + ( doc && doc.clientTop || body && body.clientTop || 0 ); + } + + // Add relatedTarget, if necessary + if ( !event.relatedTarget && fromElement ) { + event.relatedTarget = fromElement === event.target ? + original.toElement : + fromElement; + } + + // Add which for click: 1 === left; 2 === middle; 3 === right + // Note: button is not normalized, so don't use it + if ( !event.which && button !== undefined ) { + event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) ); + } + + return event; + } + }, + + special: { + load: { + + // Prevent triggered image.load events from bubbling to window.load + noBubble: true + }, + focus: { + + // Fire native event if possible so blur/focus sequence is correct + trigger: function() { + if ( this !== safeActiveElement() && this.focus ) { + try { + this.focus(); + return false; + } catch ( e ) { + + // Support: IE<9 + // If we error on focus to hidden element (#1486, #12518), + // let .trigger() run the handlers + } + } + }, + delegateType: "focusin" + }, + blur: { + trigger: function() { + if ( this === safeActiveElement() && this.blur ) { + this.blur(); + return false; + } + }, + delegateType: "focusout" + }, + click: { + + // For checkbox, fire native event so checked state will be right + trigger: function() { + if ( jQuery.nodeName( this, "input" ) && this.type === "checkbox" && this.click ) { + this.click(); + return false; + } + }, + + // For cross-browser consistency, don't fire native .click() on links + _default: function( event ) { + return jQuery.nodeName( event.target, "a" ); + } + }, + + beforeunload: { + postDispatch: function( event ) { + + // Support: Firefox 20+ + // Firefox doesn't alert if the returnValue field is not set. + if ( event.result !== undefined && event.originalEvent ) { + event.originalEvent.returnValue = event.result; + } + } + } + }, + + // Piggyback on a donor event to simulate a different one + simulate: function( type, elem, event ) { + var e = jQuery.extend( + new jQuery.Event(), + event, + { + type: type, + isSimulated: true + + // Previously, `originalEvent: {}` was set here, so stopPropagation call + // would not be triggered on donor event, since in our own + // jQuery.event.stopPropagation function we had a check for existence of + // originalEvent.stopPropagation method, so, consequently it would be a noop. + // + // Guard for simulated events was moved to jQuery.event.stopPropagation function + // since `originalEvent` should point to the original event for the + // constancy with other events and for more focused logic + } + ); + + jQuery.event.trigger( e, null, elem ); + + if ( e.isDefaultPrevented() ) { + event.preventDefault(); + } + } +}; + +jQuery.removeEvent = document.removeEventListener ? + function( elem, type, handle ) { + + // This "if" is needed for plain objects + if ( elem.removeEventListener ) { + elem.removeEventListener( type, handle ); + } + } : + function( elem, type, handle ) { + var name = "on" + type; + + if ( elem.detachEvent ) { + + // #8545, #7054, preventing memory leaks for custom events in IE6-8 + // detachEvent needed property on element, by name of that event, + // to properly expose it to GC + if ( typeof elem[ name ] === "undefined" ) { + elem[ name ] = null; + } + + elem.detachEvent( name, handle ); + } + }; + +jQuery.Event = function( src, props ) { + + // Allow instantiation without the 'new' keyword + if ( !( this instanceof jQuery.Event ) ) { + return new jQuery.Event( src, props ); + } + + // Event object + if ( src && src.type ) { + this.originalEvent = src; + this.type = src.type; + + // Events bubbling up the document may have been marked as prevented + // by a handler lower down the tree; reflect the correct value. + this.isDefaultPrevented = src.defaultPrevented || + src.defaultPrevented === undefined && + + // Support: IE < 9, Android < 4.0 + src.returnValue === false ? + returnTrue : + returnFalse; + + // Event type + } else { + this.type = src; + } + + // Put explicitly provided properties onto the event object + if ( props ) { + jQuery.extend( this, props ); + } + + // Create a timestamp if incoming event doesn't have one + this.timeStamp = src && src.timeStamp || jQuery.now(); + + // Mark it as fixed + this[ jQuery.expando ] = true; +}; + +// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding +// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html +jQuery.Event.prototype = { + constructor: jQuery.Event, + isDefaultPrevented: returnFalse, + isPropagationStopped: returnFalse, + isImmediatePropagationStopped: returnFalse, + + preventDefault: function() { + var e = this.originalEvent; + + this.isDefaultPrevented = returnTrue; + if ( !e ) { + return; + } + + // If preventDefault exists, run it on the original event + if ( e.preventDefault ) { + e.preventDefault(); + + // Support: IE + // Otherwise set the returnValue property of the original event to false + } else { + e.returnValue = false; + } + }, + stopPropagation: function() { + var e = this.originalEvent; + + this.isPropagationStopped = returnTrue; + + if ( !e || this.isSimulated ) { + return; + } + + // If stopPropagation exists, run it on the original event + if ( e.stopPropagation ) { + e.stopPropagation(); + } + + // Support: IE + // Set the cancelBubble property of the original event to true + e.cancelBubble = true; + }, + stopImmediatePropagation: function() { + var e = this.originalEvent; + + this.isImmediatePropagationStopped = returnTrue; + + if ( e && e.stopImmediatePropagation ) { + e.stopImmediatePropagation(); + } + + this.stopPropagation(); + } +}; + +// Create mouseenter/leave events using mouseover/out and event-time checks +// so that event delegation works in jQuery. +// Do the same for pointerenter/pointerleave and pointerover/pointerout +// +// Support: Safari 7 only +// Safari sends mouseenter too often; see: +// https://code.google.com/p/chromium/issues/detail?id=470258 +// for the description of the bug (it existed in older Chrome versions as well). +jQuery.each( { + mouseenter: "mouseover", + mouseleave: "mouseout", + pointerenter: "pointerover", + pointerleave: "pointerout" +}, function( orig, fix ) { + jQuery.event.special[ orig ] = { + delegateType: fix, + bindType: fix, + + handle: function( event ) { + var ret, + target = this, + related = event.relatedTarget, + handleObj = event.handleObj; + + // For mouseenter/leave call the handler if related is outside the target. + // NB: No relatedTarget if the mouse left/entered the browser window + if ( !related || ( related !== target && !jQuery.contains( target, related ) ) ) { + event.type = handleObj.origType; + ret = handleObj.handler.apply( this, arguments ); + event.type = fix; + } + return ret; + } + }; +} ); + +// IE submit delegation +if ( !support.submit ) { + + jQuery.event.special.submit = { + setup: function() { + + // Only need this for delegated form submit events + if ( jQuery.nodeName( this, "form" ) ) { + return false; + } + + // Lazy-add a submit handler when a descendant form may potentially be submitted + jQuery.event.add( this, "click._submit keypress._submit", function( e ) { + + // Node name check avoids a VML-related crash in IE (#9807) + var elem = e.target, + form = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? + + // Support: IE <=8 + // We use jQuery.prop instead of elem.form + // to allow fixing the IE8 delegated submit issue (gh-2332) + // by 3rd party polyfills/workarounds. + jQuery.prop( elem, "form" ) : + undefined; + + if ( form && !jQuery._data( form, "submit" ) ) { + jQuery.event.add( form, "submit._submit", function( event ) { + event._submitBubble = true; + } ); + jQuery._data( form, "submit", true ); + } + } ); + + // return undefined since we don't need an event listener + }, + + postDispatch: function( event ) { + + // If form was submitted by the user, bubble the event up the tree + if ( event._submitBubble ) { + delete event._submitBubble; + if ( this.parentNode && !event.isTrigger ) { + jQuery.event.simulate( "submit", this.parentNode, event ); + } + } + }, + + teardown: function() { + + // Only need this for delegated form submit events + if ( jQuery.nodeName( this, "form" ) ) { + return false; + } + + // Remove delegated handlers; cleanData eventually reaps submit handlers attached above + jQuery.event.remove( this, "._submit" ); + } + }; +} + +// IE change delegation and checkbox/radio fix +if ( !support.change ) { + + jQuery.event.special.change = { + + setup: function() { + + if ( rformElems.test( this.nodeName ) ) { + + // IE doesn't fire change on a check/radio until blur; trigger it on click + // after a propertychange. Eat the blur-change in special.change.handle. + // This still fires onchange a second time for check/radio after blur. + if ( this.type === "checkbox" || this.type === "radio" ) { + jQuery.event.add( this, "propertychange._change", function( event ) { + if ( event.originalEvent.propertyName === "checked" ) { + this._justChanged = true; + } + } ); + jQuery.event.add( this, "click._change", function( event ) { + if ( this._justChanged && !event.isTrigger ) { + this._justChanged = false; + } + + // Allow triggered, simulated change events (#11500) + jQuery.event.simulate( "change", this, event ); + } ); + } + return false; + } + + // Delegated event; lazy-add a change handler on descendant inputs + jQuery.event.add( this, "beforeactivate._change", function( e ) { + var elem = e.target; + + if ( rformElems.test( elem.nodeName ) && !jQuery._data( elem, "change" ) ) { + jQuery.event.add( elem, "change._change", function( event ) { + if ( this.parentNode && !event.isSimulated && !event.isTrigger ) { + jQuery.event.simulate( "change", this.parentNode, event ); + } + } ); + jQuery._data( elem, "change", true ); + } + } ); + }, + + handle: function( event ) { + var elem = event.target; + + // Swallow native change events from checkbox/radio, we already triggered them above + if ( this !== elem || event.isSimulated || event.isTrigger || + ( elem.type !== "radio" && elem.type !== "checkbox" ) ) { + + return event.handleObj.handler.apply( this, arguments ); + } + }, + + teardown: function() { + jQuery.event.remove( this, "._change" ); + + return !rformElems.test( this.nodeName ); + } + }; +} + +// Support: Firefox +// Firefox doesn't have focus(in | out) events +// Related ticket - https://bugzilla.mozilla.org/show_bug.cgi?id=687787 +// +// Support: Chrome, Safari +// focus(in | out) events fire after focus & blur events, +// which is spec violation - http://www.w3.org/TR/DOM-Level-3-Events/#events-focusevent-event-order +// Related ticket - https://code.google.com/p/chromium/issues/detail?id=449857 +if ( !support.focusin ) { + jQuery.each( { focus: "focusin", blur: "focusout" }, function( orig, fix ) { + + // Attach a single capturing handler on the document while someone wants focusin/focusout + var handler = function( event ) { + jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ) ); + }; + + jQuery.event.special[ fix ] = { + setup: function() { + var doc = this.ownerDocument || this, + attaches = jQuery._data( doc, fix ); + + if ( !attaches ) { + doc.addEventListener( orig, handler, true ); + } + jQuery._data( doc, fix, ( attaches || 0 ) + 1 ); + }, + teardown: function() { + var doc = this.ownerDocument || this, + attaches = jQuery._data( doc, fix ) - 1; + + if ( !attaches ) { + doc.removeEventListener( orig, handler, true ); + jQuery._removeData( doc, fix ); + } else { + jQuery._data( doc, fix, attaches ); + } + } + }; + } ); +} + +jQuery.fn.extend( { + + on: function( types, selector, data, fn ) { + return on( this, types, selector, data, fn ); + }, + one: function( types, selector, data, fn ) { + return on( this, types, selector, data, fn, 1 ); + }, + off: function( types, selector, fn ) { + var handleObj, type; + if ( types && types.preventDefault && types.handleObj ) { + + // ( event ) dispatched jQuery.Event + handleObj = types.handleObj; + jQuery( types.delegateTarget ).off( + handleObj.namespace ? + handleObj.origType + "." + handleObj.namespace : + handleObj.origType, + handleObj.selector, + handleObj.handler + ); + return this; + } + if ( typeof types === "object" ) { + + // ( types-object [, selector] ) + for ( type in types ) { + this.off( type, selector, types[ type ] ); + } + return this; + } + if ( selector === false || typeof selector === "function" ) { + + // ( types [, fn] ) + fn = selector; + selector = undefined; + } + if ( fn === false ) { + fn = returnFalse; + } + return this.each( function() { + jQuery.event.remove( this, types, fn, selector ); + } ); + }, + + trigger: function( type, data ) { + return this.each( function() { + jQuery.event.trigger( type, data, this ); + } ); + }, + triggerHandler: function( type, data ) { + var elem = this[ 0 ]; + if ( elem ) { + return jQuery.event.trigger( type, data, elem, true ); + } + } +} ); + + +var rinlinejQuery = / jQuery\d+="(?:null|\d+)"/g, + rnoshimcache = new RegExp( "<(?:" + nodeNames + ")[\\s/>]", "i" ), + rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi, + + // Support: IE 10-11, Edge 10240+ + // In IE/Edge using regex groups here causes severe slowdowns. + // See https://connect.microsoft.com/IE/feedback/details/1736512/ + rnoInnerhtml = /\s*$/g, + safeFragment = createSafeFragment( document ), + fragmentDiv = safeFragment.appendChild( document.createElement( "div" ) ); + +// Support: IE<8 +// Manipulating tables requires a tbody +function manipulationTarget( elem, content ) { + return jQuery.nodeName( elem, "table" ) && + jQuery.nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ? + + elem.getElementsByTagName( "tbody" )[ 0 ] || + elem.appendChild( elem.ownerDocument.createElement( "tbody" ) ) : + elem; +} + +// Replace/restore the type attribute of script elements for safe DOM manipulation +function disableScript( elem ) { + elem.type = ( jQuery.find.attr( elem, "type" ) !== null ) + "/" + elem.type; + return elem; +} +function restoreScript( elem ) { + var match = rscriptTypeMasked.exec( elem.type ); + if ( match ) { + elem.type = match[ 1 ]; + } else { + elem.removeAttribute( "type" ); + } + return elem; +} + +function cloneCopyEvent( src, dest ) { + if ( dest.nodeType !== 1 || !jQuery.hasData( src ) ) { + return; + } + + var type, i, l, + oldData = jQuery._data( src ), + curData = jQuery._data( dest, oldData ), + events = oldData.events; + + if ( events ) { + delete curData.handle; + curData.events = {}; + + for ( type in events ) { + for ( i = 0, l = events[ type ].length; i < l; i++ ) { + jQuery.event.add( dest, type, events[ type ][ i ] ); + } + } + } + + // make the cloned public data object a copy from the original + if ( curData.data ) { + curData.data = jQuery.extend( {}, curData.data ); + } +} + +function fixCloneNodeIssues( src, dest ) { + var nodeName, e, data; + + // We do not need to do anything for non-Elements + if ( dest.nodeType !== 1 ) { + return; + } + + nodeName = dest.nodeName.toLowerCase(); + + // IE6-8 copies events bound via attachEvent when using cloneNode. + if ( !support.noCloneEvent && dest[ jQuery.expando ] ) { + data = jQuery._data( dest ); + + for ( e in data.events ) { + jQuery.removeEvent( dest, e, data.handle ); + } + + // Event data gets referenced instead of copied if the expando gets copied too + dest.removeAttribute( jQuery.expando ); + } + + // IE blanks contents when cloning scripts, and tries to evaluate newly-set text + if ( nodeName === "script" && dest.text !== src.text ) { + disableScript( dest ).text = src.text; + restoreScript( dest ); + + // IE6-10 improperly clones children of object elements using classid. + // IE10 throws NoModificationAllowedError if parent is null, #12132. + } else if ( nodeName === "object" ) { + if ( dest.parentNode ) { + dest.outerHTML = src.outerHTML; + } + + // This path appears unavoidable for IE9. When cloning an object + // element in IE9, the outerHTML strategy above is not sufficient. + // If the src has innerHTML and the destination does not, + // copy the src.innerHTML into the dest.innerHTML. #10324 + if ( support.html5Clone && ( src.innerHTML && !jQuery.trim( dest.innerHTML ) ) ) { + dest.innerHTML = src.innerHTML; + } + + } else if ( nodeName === "input" && rcheckableType.test( src.type ) ) { + + // IE6-8 fails to persist the checked state of a cloned checkbox + // or radio button. Worse, IE6-7 fail to give the cloned element + // a checked appearance if the defaultChecked value isn't also set + + dest.defaultChecked = dest.checked = src.checked; + + // IE6-7 get confused and end up setting the value of a cloned + // checkbox/radio button to an empty string instead of "on" + if ( dest.value !== src.value ) { + dest.value = src.value; + } + + // IE6-8 fails to return the selected option to the default selected + // state when cloning options + } else if ( nodeName === "option" ) { + dest.defaultSelected = dest.selected = src.defaultSelected; + + // IE6-8 fails to set the defaultValue to the correct value when + // cloning other types of input fields + } else if ( nodeName === "input" || nodeName === "textarea" ) { + dest.defaultValue = src.defaultValue; + } +} + +function domManip( collection, args, callback, ignored ) { + + // Flatten any nested arrays + args = concat.apply( [], args ); + + var first, node, hasScripts, + scripts, doc, fragment, + i = 0, + l = collection.length, + iNoClone = l - 1, + value = args[ 0 ], + isFunction = jQuery.isFunction( value ); + + // We can't cloneNode fragments that contain checked, in WebKit + if ( isFunction || + ( l > 1 && typeof value === "string" && + !support.checkClone && rchecked.test( value ) ) ) { + return collection.each( function( index ) { + var self = collection.eq( index ); + if ( isFunction ) { + args[ 0 ] = value.call( this, index, self.html() ); + } + domManip( self, args, callback, ignored ); + } ); + } + + if ( l ) { + fragment = buildFragment( args, collection[ 0 ].ownerDocument, false, collection, ignored ); + first = fragment.firstChild; + + if ( fragment.childNodes.length === 1 ) { + fragment = first; + } + + // Require either new content or an interest in ignored elements to invoke the callback + if ( first || ignored ) { + scripts = jQuery.map( getAll( fragment, "script" ), disableScript ); + hasScripts = scripts.length; + + // Use the original fragment for the last item + // instead of the first because it can end up + // being emptied incorrectly in certain situations (#8070). + for ( ; i < l; i++ ) { + node = fragment; + + if ( i !== iNoClone ) { + node = jQuery.clone( node, true, true ); + + // Keep references to cloned scripts for later restoration + if ( hasScripts ) { + + // Support: Android<4.1, PhantomJS<2 + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( scripts, getAll( node, "script" ) ); + } + } + + callback.call( collection[ i ], node, i ); + } + + if ( hasScripts ) { + doc = scripts[ scripts.length - 1 ].ownerDocument; + + // Reenable scripts + jQuery.map( scripts, restoreScript ); + + // Evaluate executable scripts on first document insertion + for ( i = 0; i < hasScripts; i++ ) { + node = scripts[ i ]; + if ( rscriptType.test( node.type || "" ) && + !jQuery._data( node, "globalEval" ) && + jQuery.contains( doc, node ) ) { + + if ( node.src ) { + + // Optional AJAX dependency, but won't run scripts if not present + if ( jQuery._evalUrl ) { + jQuery._evalUrl( node.src ); + } + } else { + jQuery.globalEval( + ( node.text || node.textContent || node.innerHTML || "" ) + .replace( rcleanScript, "" ) + ); + } + } + } + } + + // Fix #11809: Avoid leaking memory + fragment = first = null; + } + } + + return collection; +} + +function remove( elem, selector, keepData ) { + var node, + elems = selector ? jQuery.filter( selector, elem ) : elem, + i = 0; + + for ( ; ( node = elems[ i ] ) != null; i++ ) { + + if ( !keepData && node.nodeType === 1 ) { + jQuery.cleanData( getAll( node ) ); + } + + if ( node.parentNode ) { + if ( keepData && jQuery.contains( node.ownerDocument, node ) ) { + setGlobalEval( getAll( node, "script" ) ); + } + node.parentNode.removeChild( node ); + } + } + + return elem; +} + +jQuery.extend( { + htmlPrefilter: function( html ) { + return html.replace( rxhtmlTag, "<$1>" ); + }, + + clone: function( elem, dataAndEvents, deepDataAndEvents ) { + var destElements, node, clone, i, srcElements, + inPage = jQuery.contains( elem.ownerDocument, elem ); + + if ( support.html5Clone || jQuery.isXMLDoc( elem ) || + !rnoshimcache.test( "<" + elem.nodeName + ">" ) ) { + + clone = elem.cloneNode( true ); + + // IE<=8 does not properly clone detached, unknown element nodes + } else { + fragmentDiv.innerHTML = elem.outerHTML; + fragmentDiv.removeChild( clone = fragmentDiv.firstChild ); + } + + if ( ( !support.noCloneEvent || !support.noCloneChecked ) && + ( elem.nodeType === 1 || elem.nodeType === 11 ) && !jQuery.isXMLDoc( elem ) ) { + + // We eschew Sizzle here for performance reasons: http://jsperf.com/getall-vs-sizzle/2 + destElements = getAll( clone ); + srcElements = getAll( elem ); + + // Fix all IE cloning issues + for ( i = 0; ( node = srcElements[ i ] ) != null; ++i ) { + + // Ensure that the destination node is not null; Fixes #9587 + if ( destElements[ i ] ) { + fixCloneNodeIssues( node, destElements[ i ] ); + } + } + } + + // Copy the events from the original to the clone + if ( dataAndEvents ) { + if ( deepDataAndEvents ) { + srcElements = srcElements || getAll( elem ); + destElements = destElements || getAll( clone ); + + for ( i = 0; ( node = srcElements[ i ] ) != null; i++ ) { + cloneCopyEvent( node, destElements[ i ] ); + } + } else { + cloneCopyEvent( elem, clone ); + } + } + + // Preserve script evaluation history + destElements = getAll( clone, "script" ); + if ( destElements.length > 0 ) { + setGlobalEval( destElements, !inPage && getAll( elem, "script" ) ); + } + + destElements = srcElements = node = null; + + // Return the cloned set + return clone; + }, + + cleanData: function( elems, /* internal */ forceAcceptData ) { + var elem, type, id, data, + i = 0, + internalKey = jQuery.expando, + cache = jQuery.cache, + attributes = support.attributes, + special = jQuery.event.special; + + for ( ; ( elem = elems[ i ] ) != null; i++ ) { + if ( forceAcceptData || acceptData( elem ) ) { + + id = elem[ internalKey ]; + data = id && cache[ id ]; + + if ( data ) { + if ( data.events ) { + for ( type in data.events ) { + if ( special[ type ] ) { + jQuery.event.remove( elem, type ); + + // This is a shortcut to avoid jQuery.event.remove's overhead + } else { + jQuery.removeEvent( elem, type, data.handle ); + } + } + } + + // Remove cache only if it was not already removed by jQuery.event.remove + if ( cache[ id ] ) { + + delete cache[ id ]; + + // Support: IE<9 + // IE does not allow us to delete expando properties from nodes + // IE creates expando attributes along with the property + // IE does not have a removeAttribute function on Document nodes + if ( !attributes && typeof elem.removeAttribute !== "undefined" ) { + elem.removeAttribute( internalKey ); + + // Webkit & Blink performance suffers when deleting properties + // from DOM nodes, so set to undefined instead + // https://code.google.com/p/chromium/issues/detail?id=378607 + } else { + elem[ internalKey ] = undefined; + } + + deletedIds.push( id ); + } + } + } + } + } +} ); + +jQuery.fn.extend( { + + // Keep domManip exposed until 3.0 (gh-2225) + domManip: domManip, + + detach: function( selector ) { + return remove( this, selector, true ); + }, + + remove: function( selector ) { + return remove( this, selector ); + }, + + text: function( value ) { + return access( this, function( value ) { + return value === undefined ? + jQuery.text( this ) : + this.empty().append( + ( this[ 0 ] && this[ 0 ].ownerDocument || document ).createTextNode( value ) + ); + }, null, value, arguments.length ); + }, + + append: function() { + return domManip( this, arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.appendChild( elem ); + } + } ); + }, + + prepend: function() { + return domManip( this, arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.insertBefore( elem, target.firstChild ); + } + } ); + }, + + before: function() { + return domManip( this, arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this ); + } + } ); + }, + + after: function() { + return domManip( this, arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this.nextSibling ); + } + } ); + }, + + empty: function() { + var elem, + i = 0; + + for ( ; ( elem = this[ i ] ) != null; i++ ) { + + // Remove element nodes and prevent memory leaks + if ( elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem, false ) ); + } + + // Remove any remaining nodes + while ( elem.firstChild ) { + elem.removeChild( elem.firstChild ); + } + + // If this is a select, ensure that it displays empty (#12336) + // Support: IE<9 + if ( elem.options && jQuery.nodeName( elem, "select" ) ) { + elem.options.length = 0; + } + } + + return this; + }, + + clone: function( dataAndEvents, deepDataAndEvents ) { + dataAndEvents = dataAndEvents == null ? false : dataAndEvents; + deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; + + return this.map( function() { + return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); + } ); + }, + + html: function( value ) { + return access( this, function( value ) { + var elem = this[ 0 ] || {}, + i = 0, + l = this.length; + + if ( value === undefined ) { + return elem.nodeType === 1 ? + elem.innerHTML.replace( rinlinejQuery, "" ) : + undefined; + } + + // See if we can take a shortcut and just use innerHTML + if ( typeof value === "string" && !rnoInnerhtml.test( value ) && + ( support.htmlSerialize || !rnoshimcache.test( value ) ) && + ( support.leadingWhitespace || !rleadingWhitespace.test( value ) ) && + !wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) { + + value = jQuery.htmlPrefilter( value ); + + try { + for ( ; i < l; i++ ) { + + // Remove element nodes and prevent memory leaks + elem = this[ i ] || {}; + if ( elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem, false ) ); + elem.innerHTML = value; + } + } + + elem = 0; + + // If using innerHTML throws an exception, use the fallback method + } catch ( e ) {} + } + + if ( elem ) { + this.empty().append( value ); + } + }, null, value, arguments.length ); + }, + + replaceWith: function() { + var ignored = []; + + // Make the changes, replacing each non-ignored context element with the new content + return domManip( this, arguments, function( elem ) { + var parent = this.parentNode; + + if ( jQuery.inArray( this, ignored ) < 0 ) { + jQuery.cleanData( getAll( this ) ); + if ( parent ) { + parent.replaceChild( elem, this ); + } + } + + // Force callback invocation + }, ignored ); + } +} ); + +jQuery.each( { + appendTo: "append", + prependTo: "prepend", + insertBefore: "before", + insertAfter: "after", + replaceAll: "replaceWith" +}, function( name, original ) { + jQuery.fn[ name ] = function( selector ) { + var elems, + i = 0, + ret = [], + insert = jQuery( selector ), + last = insert.length - 1; + + for ( ; i <= last; i++ ) { + elems = i === last ? this : this.clone( true ); + jQuery( insert[ i ] )[ original ]( elems ); + + // Modern browsers can apply jQuery collections as arrays, but oldIE needs a .get() + push.apply( ret, elems.get() ); + } + + return this.pushStack( ret ); + }; +} ); + + +var iframe, + elemdisplay = { + + // Support: Firefox + // We have to pre-define these values for FF (#10227) + HTML: "block", + BODY: "block" + }; + +/** + * Retrieve the actual display of a element + * @param {String} name nodeName of the element + * @param {Object} doc Document object + */ + +// Called only from within defaultDisplay +function actualDisplay( name, doc ) { + var elem = jQuery( doc.createElement( name ) ).appendTo( doc.body ), + + display = jQuery.css( elem[ 0 ], "display" ); + + // We don't have any data stored on the element, + // so use "detach" method as fast way to get rid of the element + elem.detach(); + + return display; +} + +/** + * Try to determine the default display value of an element + * @param {String} nodeName + */ +function defaultDisplay( nodeName ) { + var doc = document, + display = elemdisplay[ nodeName ]; + + if ( !display ) { + display = actualDisplay( nodeName, doc ); + + // If the simple way fails, read from inside an iframe + if ( display === "none" || !display ) { + + // Use the already-created iframe if possible + iframe = ( iframe || jQuery( "' + ,id: id + }) + } + ,tabDelete: function(othis){ + //删除指定Tab项 + element.tabDelete('xbs_tab', '44'); //删除:“商品管理” + + + othis.addClass('layui-btn-disabled'); + } + ,tabChange: function(id){ + //切换到指定Tab项 + element.tabChange('xbs_tab', id); //切换到:用户管理 + } + }; + + + tableCheck = { + init:function () { + $(".layui-form-checkbox").click(function(event) { + if($(this).hasClass('layui-form-checked')){ + $(this).removeClass('layui-form-checked'); + if($(this).hasClass('header')){ + $(".layui-form-checkbox").removeClass('layui-form-checked'); + } + }else{ + $(this).addClass('layui-form-checked'); + if($(this).hasClass('header')){ + $(".layui-form-checkbox").addClass('layui-form-checked'); + } + } + + }); + }, + getData:function () { + var obj = $(".layui-form-checked").not('.header'); + var arr=[]; + obj.each(function(index, el) { + arr.push(obj.eq(index).attr('data-id')); + }); + return arr; + } + } + + //开启表格多选 + tableCheck.init(); + + + $('.container .left_open i').click(function(event) { + if($('.left-nav').css('left')=='0px'){ + $('.left-nav').animate({left: '-221px'}, 100); + $('.page-content').animate({left: '0px'}, 100); + $('.page-content-bg').hide(); + }else{ + $('.left-nav').animate({left: '0px'}, 100); + $('.page-content').animate({left: '221px'}, 100); + if($(window).width()<768){ + $('.page-content-bg').show(); + } + } + + }); + + $('.page-content-bg').click(function(event) { + $('.left-nav').animate({left: '-221px'}, 100); + $('.page-content').animate({left: '0px'}, 100); + $(this).hide(); + }); + + $('.layui-tab-close').click(function(event) { + $('.layui-tab-title li').eq(0).find('i').remove(); + }); + + $("tbody.x-cate tr[fid!='0']").hide(); + // 栏目多级显示效果 + $('.x-show').click(function () { + if($(this).attr('status')=='true'){ + $(this).html(''); + $(this).attr('status','false'); + cateId = $(this).parents('tr').attr('cate-id'); + $("tbody tr[fid="+cateId+"]").show(); + }else{ + cateIds = []; + $(this).html(''); + $(this).attr('status','true'); + cateId = $(this).parents('tr').attr('cate-id'); + getCateId(cateId); + for (var i in cateIds) { + $("tbody tr[cate-id="+cateIds[i]+"]").hide().find('.x-show').html('').attr('status','true'); + } + } + }) + + //左侧菜单效果 + // $('#content').bind("click",function(event){ + $('.left-nav #nav li').click(function (event) { + + if($(this).children('.sub-menu').length){ + if($(this).hasClass('open')){ + $(this).removeClass('open'); + $(this).find('.nav_right').html(''); + $(this).children('.sub-menu').stop().slideUp(); + $(this).siblings().children('.sub-menu').slideUp(); + }else{ + $(this).addClass('open'); + $(this).children('a').find('.nav_right').html(''); + $(this).children('.sub-menu').stop().slideDown(); + $(this).siblings().children('.sub-menu').stop().slideUp(); + $(this).siblings().find('.nav_right').html(''); + $(this).siblings().removeClass('open'); + } + }else{ + + var url = $(this).children('a').attr('_href'); + var title = $(this).find('cite').html(); + var index = $('.left-nav #nav li').index($(this)); + + for (var i = 0; i <$('.x-iframe').length; i++) { + if($('.x-iframe').eq(i).attr('tab-id')==index+1){ + tab.tabChange(index+1); + event.stopPropagation(); + return; + } + }; + + tab.tabAdd(title,url,index+1); + tab.tabChange(index+1); + } + + event.stopPropagation(); + + }) + +}) +var cateIds = []; +function getCateId(cateId) { + + $("tbody tr[fid="+cateId+"]").each(function(index, el) { + id = $(el).attr('cate-id'); + cateIds.push(id); + getCateId(id); + }); +} + +/*弹出层*/ +/* + 参数解释: + title 标题 + url 请求的url + id 需要操作的数据id + w 弹出层宽度(缺省调默认值) + h 弹出层高度(缺省调默认值) +*/ +function x_admin_show(title,url,w,h){ + if (title == null || title == '') { + title=false; + }; + if (url == null || url == '') { + url="404.html"; + }; + if (w == null || w == '') { + w=($(window).width()*0.9); + }; + if (h == null || h == '') { + h=($(window).height() - 50); + }; + layer.open({ + type: 2, + area: [w+'px', h +'px'], + fix: false, //不固定 + maxmin: true, + shadeClose: true, + shade:0.4, + title: title, + content: url + }); +} + +/*关闭弹出框口*/ +function x_admin_close(){ + var index = parent.layer.getFrameIndex(window.name); + parent.layer.close(index); +} + + diff --git a/main/webapp/static/js/xcity.js b/main/webapp/static/js/xcity.js new file mode 100644 index 0000000..e1fb2cf --- /dev/null +++ b/main/webapp/static/js/xcity.js @@ -0,0 +1,511 @@ +$.fn.xcity = function(pName,cName,aName){ + + var p = $(this).find('select[lay-filter=province]'); + + var c = $(this).find('select[lay-filter=city]'); + + var a = $(this).find('select[lay-filter=area]'); + + var cityList = []; + + var areaList = []; + + showP(provinceList); + + showC(cityList); + + showA(areaList); + + function showP(provinceList) { + p.html(''); + + is_pName = false; + + for (var i in provinceList) { + + if(pName==provinceList[i].name){ + is_pName = true; + cityList = provinceList[i].cityList; + p.append("") + }else{ + p.append("") + } + } + if(!is_pName){ + cityList = provinceList[0].cityList; + } + + } + + function showC(cityList) { + + c.html(''); + + is_cName = false; + + for (var i in cityList) { + if(cName==cityList[i].name){ + is_cName = true; + areaList = cityList[i].areaList; + c.append("") + }else{ + c.append("") + } + } + + if(!is_cName){ + areaList = cityList[0].areaList; + } + } + + function showA(areaList) { + a.html(''); + + for (var i in areaList) { + + if(aName==areaList[i]){ + a.append("") + }else{ + a.append("") + } + } + } + + form.render('select'); + + form.on('select(province)', function(data){ + pName = data.value; + showP(provinceList); + showC(cityList); + showA(areaList); + form.render('select'); + }); + + form.on('select(city)', function(data){ + cName = data.value; + showC(cityList); + showA(areaList); + form.render('select'); + }); + + + +} +var provinceList = [ +{name:'北京', cityList:[ +{name:'市辖区', areaList:['东城区','西城区','崇文区','宣武区','朝阳区','丰台区','石景山区','海淀区','门头沟区','房山区','通州区','顺义区','昌平区','大兴区','怀柔区','平谷区']}, +{name:'县', areaList:['密云县','延庆县']} +]}, +{name:'上海', cityList:[ +{name:'市辖区', areaList:['黄浦区','卢湾区','徐汇区','长宁区','静安区','普陀区','闸北区','虹口区','杨浦区','闵行区','宝山区','嘉定区','浦东新区','金山区','松江区','青浦区','南汇区','奉贤区']}, +{name:'县', areaList:['崇明县']} +]}, +{name:'天津', cityList:[ +{name:'市辖区', areaList:['和平区','河东区','河西区','南开区','河北区','红桥区','塘沽区','汉沽区','大港区','东丽区','西青区','津南区','北辰区','武清区','宝坻区']}, +{name:'县', areaList:['宁河县','静海县','蓟 县']} +]}, +{name:'重庆', cityList:[ +{name:'市辖区', areaList:['万州区','涪陵区','渝中区','大渡口区','江北区','沙坪坝区','九龙坡区','南岸区','北碚区','万盛区','双桥区','渝北区','巴南区','黔江区','长寿区']}, +{name:'县', areaList:['綦江县','潼南县','铜梁县','大足县','荣昌县','璧山县','梁平县','城口县','丰都县','垫江县','武隆县','忠 县','开 县','云阳县','奉节县','巫山县','巫溪县','石柱土家族自治县','秀山土家族苗族自治县','酉阳土家族苗族自治县','彭水苗族土家族自治县']}, +{name:'市', areaList:['江津市','合川市','永川市','南川市']} +]}, +{name:'四川', cityList:[ +{name:'成都市', areaList:['市辖区','锦江区','青羊区','金牛区','武侯区','成华区','龙泉驿区','青白江区','新都区','温江县','金堂县','双流县','郫 县','大邑县','蒲江县','新津县','都江堰市','彭州市','邛崃市','崇州市']}, +{name:'自贡市', areaList:['市辖区','自流井区','贡井区','大安区','沿滩区','荣 县','富顺县']}, +{name:'攀枝花市', areaList:['市辖区','东 区','西 区','仁和区','米易县','盐边县']}, +{name:'泸州市', areaList:['市辖区','江阳区','纳溪区','龙马潭区','泸 县','合江县','叙永县','古蔺县']}, +{name:'德阳市', areaList:['市辖区','旌阳区','中江县','罗江县','广汉市','什邡市','绵竹市']}, +{name:'绵阳市', areaList:['市辖区','涪城区','游仙区','三台县','盐亭县','安 县','梓潼县','北川羌族自治县','平武县','江油市']}, +{name:'广元市', areaList:['市辖区','市中区','元坝区','朝天区','旺苍县','青川县','剑阁县','苍溪县']}, +{name:'遂宁市', areaList:['市辖区','船山区','安居区','蓬溪县','射洪县','大英县']}, +{name:'内江市', areaList:['市辖区','市中区','东兴区','威远县','资中县','隆昌县']}, +{name:'乐山市', areaList:['市辖区','市中区','沙湾区','五通桥区','金口河区','犍为县','井研县','夹江县','沐川县','峨边彝族自治县','马边彝族自治县','峨眉山市']}, +{name:'南充市', areaList:['市辖区','顺庆区','高坪区','嘉陵区','南部县','营山县','蓬安县','仪陇县','西充县','阆中市']}, +{name:'眉山市', areaList:['市辖区','东坡区','仁寿县','彭山县','洪雅县','丹棱县','青神县']}, +{name:'宜宾市', areaList:['市辖区','翠屏区','宜宾县','南溪县','江安县','长宁县','高 县','珙 县','筠连县','兴文县','屏山县']}, +{name:'广安市', areaList:['市辖区','广安区','岳池县','武胜县','邻水县','华莹市']}, +{name:'达州市', areaList:['市辖区','通川区','达 县','宣汉县','开江县','大竹县','渠 县','万源市']}, +{name:'雅安市', areaList:['市辖区','雨城区','名山县','荥经县','汉源县','石棉县','天全县','芦山县','宝兴县']}, +{name:'巴中市', areaList:['市辖区','巴州区','通江县','南江县','平昌县']}, +{name:'资阳市', areaList:['市辖区','雁江区','安岳县','乐至县','简阳市']}, +{name:'阿坝藏族羌族自治州', areaList:['汶川县','理 县','茂 县','松潘县','九寨沟县','金川县','小金县','黑水县','马尔康县','壤塘县','阿坝县','若尔盖县','红原县']}, +{name:'甘孜藏族自治州', areaList:['康定县','泸定县','丹巴县','九龙县','雅江县','道孚县','炉霍县','甘孜县','新龙县','德格县','白玉县','石渠县','色达县','理塘县','巴塘县','乡城县','稻城县','得荣县']}, +{name:'凉山彝族自治州', areaList:['西昌市','木里藏族自治县','盐源县','德昌县','会理县','会东县','宁南县','普格县','布拖县','金阳县','昭觉县','喜德县','冕宁县','越西县','甘洛县','美姑县','雷波县']} +]}, +{name:'贵州', cityList:[ +{name:'贵阳市', areaList:['市辖区','南明区','云岩区','花溪区','乌当区','白云区','小河区','开阳县','息烽县','修文县','清镇市']}, +{name:'六盘水市', areaList:['钟山区','六枝特区','水城县','盘 县']}, +{name:'遵义市', areaList:['市辖区','红花岗区','汇川区','遵义县','桐梓县','绥阳县','正安县','道真仡佬族苗族自治县','务川仡佬族苗族自治县','凤冈县','湄潭县','余庆县','习水县','赤水市','仁怀市']}, +{name:'安顺市', areaList:['市辖区','西秀区','平坝县','普定县','镇宁布依族苗族自治县','关岭布依族苗族自治县','紫云苗族布依族自治县']}, +{name:'铜仁地区', areaList:['铜仁市','江口县','玉屏侗族自治县','石阡县','思南县','印江土家族苗族自治县','德江县','沿河土家族自治县','松桃苗族自治县','万山特区']}, +{name:'黔西南布依族苗族自治州', areaList:['兴义市','兴仁县','普安县','晴隆县','贞丰县','望谟县','册亨县','安龙县']}, +{name:'毕节地区', areaList:['毕节市','大方县','黔西县','金沙县','织金县','纳雍县','威宁彝族回族苗族自治县','赫章县']}, +{name:'黔东南苗族侗族自治州', areaList:['凯里市','黄平县','施秉县','三穗县','镇远县','岑巩县','天柱县','锦屏县','剑河县','台江县','黎平县','榕江县','从江县','雷山县','麻江县','丹寨县']}, +{name:'黔南布依族苗族自治州', areaList:['都匀市','福泉市','荔波县','贵定县','瓮安县','独山县','平塘县','罗甸县','长顺县','龙里县','惠水县','三都水族自治县']} +]}, +{name:'云南', cityList:[ +{name:'昆明市', areaList:['市辖区','五华区','盘龙区','官渡区','西山区','东川区','呈贡县','晋宁县','富民县','宜良县','石林彝族自治县','嵩明县','禄劝彝族苗族自治县','寻甸回族彝族自治县','安宁市']}, +{name:'曲靖市', areaList:['市辖区','麒麟区','马龙县','陆良县','师宗县','罗平县','富源县','会泽县','沾益县','宣威市']}, +{name:'玉溪市', areaList:['市辖区','红塔区','江川县','澄江县','通海县','华宁县','易门县','峨山彝族自治县','新平彝族傣族自治县','元江哈尼族彝族傣族自治县']}, +{name:'保山市', areaList:['市辖区','隆阳区','施甸县','腾冲县','龙陵县','昌宁县']}, +{name:'昭通市', areaList:['市辖区','昭阳区','鲁甸县','巧家县','盐津县','大关县','永善县','绥江县','镇雄县','彝良县','威信县','水富县']}, +{name:'丽江市', areaList:['市辖区','古城区','玉龙纳西族自治县','永胜县','华坪县','宁蒗彝族自治县']}, +{name:'思茅市', areaList:['市辖区','翠云区','普洱哈尼族彝族自治县','墨江哈尼族自治县','景东彝族自治县','景谷傣族彝族自治县','镇沅彝族哈尼族拉祜族自治县','江城哈尼族彝族自治县','孟连傣族拉祜族佤族自治县','澜沧拉祜族自治县','西盟佤族自治县']}, +{name:'临沧市', areaList:['市辖区','临翔区','凤庆县','云 县','永德县','镇康县','双江拉祜族佤族布朗族傣族自治县','耿马傣族佤族自治县','沧源佤族自治县']}, +{name:'楚雄彝族自治州', areaList:['楚雄市','双柏县','牟定县','南华县','姚安县','大姚县','永仁县','元谋县','武定县','禄丰县']}, +{name:'红河哈尼族彝族自治州', areaList:['个旧市','开远市','蒙自县','屏边苗族自治县','建水县','石屏县','弥勒县','泸西县','元阳县','红河县','金平苗族瑶族傣族自治县','绿春县','河口瑶族自治县']}, +{name:'文山壮族苗族自治州', areaList:['文山县','砚山县','西畴县','麻栗坡县','马关县','丘北县','广南县','富宁县']}, +{name:'西双版纳傣族自治州', areaList:['景洪市','勐海县','勐腊县']}, +{name:'大理白族自治州', areaList:['大理市','漾濞彝族自治县','祥云县','宾川县','弥渡县','南涧彝族自治县','巍山彝族回族自治县','永平县','云龙县','洱源县','剑川县','鹤庆县']}, +{name:'德宏傣族景颇族自治州', areaList:['瑞丽市','潞西市','梁河县','盈江县','陇川县']}, +{name:'怒江傈僳族自治州', areaList:['泸水县','福贡县','贡山独龙族怒族自治县','兰坪白族普米族自治县']}, +{name:'迪庆藏族自治州', areaList:['香格里拉县','德钦县','维西傈僳族自治县']} +]}, +{name:'西藏', cityList:[ +{name:'拉萨市', areaList:['市辖区','城关区','林周县','当雄县','尼木县','曲水县','堆龙德庆县','达孜县','墨竹工卡县']}, +{name:'昌都地区', areaList:['昌都县','江达县','贡觉县','类乌齐县','丁青县','察雅县','八宿县','左贡县','芒康县','洛隆县','边坝县']}, +{name:'山南地区', areaList:['乃东县','扎囊县','贡嘎县','桑日县','琼结县','曲松县','措美县','洛扎县','加查县','隆子县','错那县','浪卡子县']}, +{name:'日喀则地区', areaList:['日喀则市','南木林县','江孜县','定日县','萨迦县','拉孜县','昂仁县','谢通门县','白朗县','仁布县','康马县','定结县','仲巴县','亚东县','吉隆县','聂拉木县','萨嘎县','岗巴县']}, +{name:'那曲地区', areaList:['那曲县','嘉黎县','比如县','聂荣县','安多县','申扎县','索 县','班戈县','巴青县','尼玛县']}, +{name:'阿里地区', areaList:['普兰县','札达县','噶尔县','日土县','革吉县','改则县','措勤县']}, +{name:'林芝地区', areaList:['林芝县','工布江达县','米林县','墨脱县','波密县','察隅县','朗 县']} +]}, +{name:'河南', cityList:[ +{name:'郑州市', areaList:['市辖区','中原区','二七区','管城回族区','金水区','上街区','邙山区','中牟县','巩义市','荥阳市','新密市','新郑市','登封市']}, +{name:'开封市', areaList:['市辖区','龙亭区','顺河回族区','鼓楼区','南关区','郊 区','杞 县','通许县','尉氏县','开封县','兰考县']}, +{name:'洛阳市', areaList:['市辖区','老城区','西工区','廛河回族区','涧西区','吉利区','洛龙区','孟津县','新安县','栾川县','嵩 县','汝阳县','宜阳县','洛宁县','伊川县','偃师市']}, +{name:'平顶山市', areaList:['市辖区','新华区','卫东区','石龙区','湛河区','宝丰县','叶 县','鲁山县','郏 县','舞钢市','汝州市']}, +{name:'安阳市', areaList:['市辖区','文峰区','北关区','殷都区','龙安区','安阳县','汤阴县','滑 县','内黄县','林州市']}, +{name:'鹤壁市', areaList:['市辖区','鹤山区','山城区','淇滨区','浚 县','淇 县']}, +{name:'新乡市', areaList:['市辖区','红旗区','卫滨区','凤泉区','牧野区','新乡县','获嘉县','原阳县','延津县','封丘县','长垣县','卫辉市','辉县市']}, +{name:'焦作市', areaList:['市辖区','解放区','中站区','马村区','山阳区','修武县','博爱县','武陟县','温 县','济源市','沁阳市','孟州市']}, +{name:'濮阳市', areaList:['市辖区','华龙区','清丰县','南乐县','范 县','台前县','濮阳县']}, +{name:'许昌市', areaList:['市辖区','魏都区','许昌县','鄢陵县','襄城县','禹州市','长葛市']}, +{name:'漯河市', areaList:['市辖区','源汇区','郾城区','召陵区','舞阳县','临颍县']}, +{name:'三门峡市', areaList:['市辖区','湖滨区','渑池县','陕 县','卢氏县','义马市','灵宝市']}, +{name:'南阳市', areaList:['市辖区','宛城区','卧龙区','南召县','方城县','西峡县','镇平县','内乡县','淅川县','社旗县','唐河县','新野县','桐柏县','邓州市']}, +{name:'商丘市', areaList:['市辖区','梁园区','睢阳区','民权县','睢 县','宁陵县','柘城县','虞城县','夏邑县','永城市']}, +{name:'信阳市', areaList:['市辖区','师河区','平桥区','罗山县','光山县','新 县','商城县','固始县','潢川县','淮滨县','息 县']}, +{name:'周口市', areaList:['市辖区','川汇区','扶沟县','西华县','商水县','沈丘县','郸城县','淮阳县','太康县','鹿邑县','项城市']}, +{name:'驻马店市', areaList:['市辖区','驿城区','西平县','上蔡县','平舆县','正阳县','确山县','泌阳县','汝南县','遂平县','新蔡县']} +]}, +{name:'湖北', cityList:[ +{name:'武汉市', areaList:['市辖区','江岸区','江汉区','乔口区','汉阳区','武昌区','青山区','洪山区','东西湖区','汉南区','蔡甸区','江夏区','黄陂区','新洲区']}, +{name:'黄石市', areaList:['市辖区','黄石港区','西塞山区','下陆区','铁山区','阳新县','大冶市']}, +{name:'十堰市', areaList:['市辖区','茅箭区','张湾区','郧 县','郧西县','竹山县','竹溪县','房 县','丹江口市']}, +{name:'宜昌市', areaList:['市辖区','西陵区','伍家岗区','点军区','猇亭区','夷陵区','远安县','兴山县','秭归县','长阳土家族自治县','五峰土家族自治县','宜都市','当阳市','枝江市']}, +{name:'襄樊市', areaList:['市辖区','襄城区','樊城区','襄阳区','南漳县','谷城县','保康县','老河口市','枣阳市','宜城市']}, +{name:'鄂州市', areaList:['市辖区','梁子湖区','华容区','鄂城区']}, +{name:'荆门市', areaList:['市辖区','东宝区','掇刀区','京山县','沙洋县','钟祥市']}, +{name:'孝感市', areaList:['市辖区','孝南区','孝昌县','大悟县','云梦县','应城市','安陆市','汉川市']}, +{name:'荆州市', areaList:['市辖区','沙市区','荆州区','公安县','监利县','江陵县','石首市','洪湖市','松滋市']}, +{name:'黄冈市', areaList:['市辖区','黄州区','团风县','红安县','罗田县','英山县','浠水县','蕲春县','黄梅县','麻城市','武穴市']}, +{name:'咸宁市', areaList:['市辖区','咸安区','嘉鱼县','通城县','崇阳县','通山县','赤壁市']}, +{name:'随州市', areaList:['市辖区','曾都区','广水市']}, +{name:'恩施土家族苗族自治州', areaList:['恩施市','利川市','建始县','巴东县','宣恩县','咸丰县','来凤县','鹤峰县']}, +{name:'省直辖行政单位', areaList:['仙桃市','潜江市','天门市','神农架林区']} +]}, +{name:'湖南', cityList:[ +{name:'长沙市', areaList:['市辖区','芙蓉区','天心区','岳麓区','开福区','雨花区','长沙县','望城县','宁乡县','浏阳市']}, +{name:'株洲市', areaList:['市辖区','荷塘区','芦淞区','石峰区','天元区','株洲县','攸 县','茶陵县','炎陵县','醴陵市']}, +{name:'湘潭市', areaList:['市辖区','雨湖区','岳塘区','湘潭县','湘乡市','韶山市']}, +{name:'衡阳市', areaList:['市辖区','珠晖区','雁峰区','石鼓区','蒸湘区','南岳区','衡阳县','衡南县','衡山县','衡东县','祁东县','耒阳市','常宁市']}, +{name:'邵阳市', areaList:['市辖区','双清区','大祥区','北塔区','邵东县','新邵县','邵阳县','隆回县','洞口县','绥宁县','新宁县','城步苗族自治县','武冈市']}, +{name:'岳阳市', areaList:['市辖区','岳阳楼区','云溪区','君山区','岳阳县','华容县','湘阴县','平江县','汨罗市','临湘市']}, +{name:'常德市', areaList:['市辖区','武陵区','鼎城区','安乡县','汉寿县','澧 县','临澧县','桃源县','石门县','津市市']}, +{name:'张家界市', areaList:['市辖区','永定区','武陵源区','慈利县','桑植县']}, +{name:'益阳市', areaList:['市辖区','资阳区','赫山区','南 县','桃江县','安化县','沅江市']}, +{name:'郴州市', areaList:['市辖区','北湖区','苏仙区','桂阳县','宜章县','永兴县','嘉禾县','临武县','汝城县','桂东县','安仁县','资兴市']}, +{name:'永州市', areaList:['市辖区','芝山区','冷水滩区','祁阳县','东安县','双牌县','道 县','江永县','宁远县','蓝山县','新田县','江华瑶族自治县']}, +{name:'怀化市', areaList:['市辖区','鹤城区','中方县','沅陵县','辰溪县','溆浦县','会同县','麻阳苗族自治县','新晃侗族自治县','芷江侗族自治县','靖州苗族侗族自治县','通道侗族自治县','洪江市']}, +{name:'娄底市', areaList:['市辖区','娄星区','双峰县','新化县','冷水江市','涟源市']}, +{name:'湘西土家族苗族自治州', areaList:['吉首市','泸溪县','凤凰县','花垣县','保靖县','古丈县','永顺县','龙山县']} +]}, +{name:'广东', cityList:[ +{name:'广州市', areaList:['市辖区','东山区','荔湾区','越秀区','海珠区','天河区','芳村区','白云区','黄埔区','番禺区','花都区','增城市','从化市']}, +{name:'韶关市', areaList:['市辖区','武江区','浈江区','曲江区','始兴县','仁化县','翁源县','乳源瑶族自治县','新丰县','乐昌市','南雄市']}, +{name:'深圳市', areaList:['市辖区','罗湖区','福田区','南山区','宝安区','龙岗区','盐田区']}, +{name:'珠海市', areaList:['市辖区','香洲区','斗门区','金湾区']}, +{name:'汕头市', areaList:['市辖区','龙湖区','金平区','濠江区','潮阳区','潮南区','澄海区','南澳县']}, +{name:'佛山市', areaList:['市辖区','禅城区','南海区','顺德区','三水区','高明区']}, +{name:'江门市', areaList:['市辖区','蓬江区','江海区','新会区','台山市','开平市','鹤山市','恩平市']}, +{name:'湛江市', areaList:['市辖区','赤坎区','霞山区','坡头区','麻章区','遂溪县','徐闻县','廉江市','雷州市','吴川市']}, +{name:'茂名市', areaList:['市辖区','茂南区','茂港区','电白县','高州市','化州市','信宜市']}, +{name:'肇庆市', areaList:['市辖区','端州区','鼎湖区','广宁县','怀集县','封开县','德庆县','高要市','四会市']}, +{name:'惠州市', areaList:['市辖区','惠城区','惠阳区','博罗县','惠东县','龙门县']}, +{name:'梅州市', areaList:['市辖区','梅江区','梅 县','大埔县','丰顺县','五华县','平远县','蕉岭县','兴宁市']}, +{name:'汕尾市', areaList:['市辖区','城 区','海丰县','陆河县','陆丰市']}, +{name:'河源市', areaList:['市辖区','源城区','紫金县','龙川县','连平县','和平县','东源县']}, +{name:'阳江市', areaList:['市辖区','江城区','阳西县','阳东县','阳春市']}, +{name:'清远市', areaList:['市辖区','清城区','佛冈县','阳山县','连山壮族瑶族自治县','连南瑶族自治县','清新县','英德市','连州市']}, +{name:'东莞市', areaList:['东莞市']}, +{name:'中山市', areaList:['中山市']}, +{name:'潮州市', areaList:['市辖区','湘桥区','潮安县','饶平县']}, +{name:'揭阳市', areaList:['市辖区','榕城区','揭东县','揭西县','惠来县','普宁市']}, +{name:'云浮市', areaList:['市辖区','云城区','新兴县','郁南县','云安县','罗定市']} +]}, +{name:'广西', cityList:[ +{name:'南宁市', areaList:['市辖区','兴宁区','青秀区','江南区','西乡塘区','良庆区','邕宁区','武鸣县','隆安县','马山县','上林县','宾阳县','横 县']}, +{name:'柳州市', areaList:['市辖区','城中区','鱼峰区','柳南区','柳北区','柳江县','柳城县','鹿寨县','融安县','融水苗族自治县','三江侗族自治县']}, +{name:'桂林市', areaList:['市辖区','秀峰区','叠彩区','象山区','七星区','雁山区','阳朔县','临桂县','灵川县','全州县','兴安县','永福县','灌阳县','龙胜各族自治县','资源县','平乐县','荔蒲县','恭城瑶族自治县']}, +{name:'梧州市', areaList:['市辖区','万秀区','蝶山区','长洲区','苍梧县','藤 县','蒙山县','岑溪市']}, +{name:'北海市', areaList:['市辖区','海城区','银海区','铁山港区','合浦县']}, +{name:'防城港市', areaList:['市辖区','港口区','防城区','上思县','东兴市']}, +{name:'钦州市', areaList:['市辖区','钦南区','钦北区','灵山县','浦北县']}, +{name:'贵港市', areaList:['市辖区','港北区','港南区','覃塘区','平南县','桂平市']}, +{name:'玉林市', areaList:['市辖区','玉州区','容 县','陆川县','博白县','兴业县','北流市']}, +{name:'百色市', areaList:['市辖区','右江区','田阳县','田东县','平果县','德保县','靖西县','那坡县','凌云县','乐业县','田林县','西林县','隆林各族自治县']}, +{name:'贺州市', areaList:['市辖区','八步区','昭平县','钟山县','富川瑶族自治县']}, +{name:'河池市', areaList:['市辖区','金城江区','南丹县','天峨县','凤山县','东兰县','罗城仫佬族自治县','环江毛南族自治县','巴马瑶族自治县','都安瑶族自治县','大化瑶族自治县','宜州市']}, +{name:'来宾市', areaList:['市辖区','兴宾区','忻城县','象州县','武宣县','金秀瑶族自治县','合山市']}, +{name:'崇左市', areaList:['市辖区','江洲区','扶绥县','宁明县','龙州县','大新县','天等县','凭祥市']} +]}, +{name:'陕西', cityList:[ +{name:'西安市', areaList:['市辖区','新城区','碑林区','莲湖区','灞桥区','未央区','雁塔区','阎良区','临潼区','长安区','蓝田县','周至县','户 县','高陵县']}, +{name:'铜川市', areaList:['市辖区','王益区','印台区','耀州区','宜君县']}, +{name:'宝鸡市', areaList:['市辖区','渭滨区','金台区','陈仓区','凤翔县','岐山县','扶风县','眉 县','陇 县','千阳县','麟游县','凤 县','太白县']}, +{name:'咸阳市', areaList:['市辖区','秦都区','杨凌区','渭城区','三原县','泾阳县','乾 县','礼泉县','永寿县','彬 县','长武县','旬邑县','淳化县','武功县','兴平市']}, +{name:'渭南市', areaList:['市辖区','临渭区','华 县','潼关县','大荔县','合阳县','澄城县','蒲城县','白水县','富平县','韩城市','华阴市']}, +{name:'延安市', areaList:['市辖区','宝塔区','延长县','延川县','子长县','安塞县','志丹县','吴旗县','甘泉县','富 县','洛川县','宜川县','黄龙县','黄陵县']}, +{name:'汉中市', areaList:['市辖区','汉台区','南郑县','城固县','洋 县','西乡县','勉 县','宁强县','略阳县','镇巴县','留坝县','佛坪县']}, +{name:'榆林市', areaList:['市辖区','榆阳区','神木县','府谷县','横山县','靖边县','定边县','绥德县','米脂县','佳 县','吴堡县','清涧县','子洲县']}, +{name:'安康市', areaList:['市辖区','汉滨区','汉阴县','石泉县','宁陕县','紫阳县','岚皋县','平利县','镇坪县','旬阳县','白河县']}, +{name:'商洛市', areaList:['市辖区','商州区','洛南县','丹凤县','商南县','山阳县','镇安县','柞水县']} +]}, +{name:'甘肃', cityList:[ +{name:'兰州市', areaList:['市辖区','城关区','七里河区','西固区','安宁区','红古区','永登县','皋兰县','榆中县']}, +{name:'嘉峪关市', areaList:['市辖区']}, +{name:'金昌市', areaList:['市辖区','金川区','永昌县']}, +{name:'白银市', areaList:['市辖区','白银区','平川区','靖远县','会宁县','景泰县']}, +{name:'天水市', areaList:['市辖区','秦城区','北道区','清水县','秦安县','甘谷县','武山县','张家川回族自治县']}, +{name:'武威市', areaList:['市辖区','凉州区','民勤县','古浪县','天祝藏族自治县']}, +{name:'张掖市', areaList:['市辖区','甘州区','肃南裕固族自治县','民乐县','临泽县','高台县','山丹县']}, +{name:'平凉市', areaList:['市辖区','崆峒区','泾川县','灵台县','崇信县','华亭县','庄浪县','静宁县']}, +{name:'酒泉市', areaList:['市辖区','肃州区','金塔县','安西县','肃北蒙古族自治县','阿克塞哈萨克族自治县','玉门市','敦煌市']}, +{name:'庆阳市', areaList:['市辖区','西峰区','庆城县','环 县','华池县','合水县','正宁县','宁 县','镇原县']}, +{name:'定西市', areaList:['市辖区','安定区','通渭县','陇西县','渭源县','临洮县','漳 县','岷 县']}, +{name:'陇南市', areaList:['市辖区','武都区','成 县','文 县','宕昌县','康 县','西和县','礼 县','徽 县','两当县']}, +{name:'临夏回族自治州', areaList:['临夏市','临夏县','康乐县','永靖县','广河县','和政县','东乡族自治县','积石山保安族东乡族撒拉族自治县']}, +{name:'甘南藏族自治州', areaList:['合作市','临潭县','卓尼县','舟曲县','迭部县','玛曲县','碌曲县','夏河县']} +]}, +{name:'青海', cityList:[ +{name:'西宁市', areaList:['市辖区','城东区','城中区','城西区','城北区','大通回族土族自治县','湟中县','湟源县']}, +{name:'海东地区', areaList:['平安县','民和回族土族自治县','乐都县','互助土族自治县','化隆回族自治县','循化撒拉族自治县']}, +{name:'海北藏族自治州', areaList:['门源回族自治县','祁连县','海晏县','刚察县']}, +{name:'黄南藏族自治州', areaList:['同仁县','尖扎县','泽库县','河南蒙古族自治县']}, +{name:'海南藏族自治州', areaList:['共和县','同德县','贵德县','兴海县','贵南县']}, +{name:'果洛藏族自治州', areaList:['玛沁县','班玛县','甘德县','达日县','久治县','玛多县']}, +{name:'玉树藏族自治州', areaList:['玉树县','杂多县','称多县','治多县','囊谦县','曲麻莱县']}, +{name:'海西蒙古族藏族自治州', areaList:['格尔木市','德令哈市','乌兰县','都兰县','天峻县']} +]}, +{name:'宁夏', cityList:[ +{name:'银川市', areaList:['市辖区','兴庆区','西夏区','金凤区','永宁县','贺兰县','灵武市']}, +{name:'石嘴山市', areaList:['市辖区','大武口区','惠农区','平罗县']}, +{name:'吴忠市', areaList:['市辖区','利通区','盐池县','同心县','青铜峡市']}, +{name:'固原市', areaList:['市辖区','原州区','西吉县','隆德县','泾源县','彭阳县','海原县']}, +{name:'中卫市', areaList:['市辖区','沙坡头区','中宁县']} +]}, +{name:'新疆', cityList:[ +{name:'乌鲁木齐市', areaList:['市辖区','天山区','沙依巴克区','新市区','水磨沟区','头屯河区','达坂城区','东山区','乌鲁木齐县']}, +{name:'克拉玛依市', areaList:['市辖区','独山子区','克拉玛依区','白碱滩区','乌尔禾区']}, +{name:'吐鲁番地区', areaList:['吐鲁番市','鄯善县','托克逊县']}, +{name:'哈密地区', areaList:['哈密市','巴里坤哈萨克自治县','伊吾县']}, +{name:'昌吉回族自治州', areaList:['昌吉市','阜康市','米泉市','呼图壁县','玛纳斯县','奇台县','吉木萨尔县','木垒哈萨克自治县']}, +{name:'博尔塔拉蒙古自治州', areaList:['博乐市','精河县','温泉县']}, +{name:'巴音郭楞蒙古自治州', areaList:['库尔勒市','轮台县','尉犁县','若羌县','且末县','焉耆回族自治县','和静县','和硕县','博湖县']}, +{name:'阿克苏地区', areaList:['阿克苏市','温宿县','库车县','沙雅县','新和县','拜城县','乌什县','阿瓦提县','柯坪县']}, +{name:'克孜勒苏柯尔克孜自治州', areaList:['阿图什市','阿克陶县','阿合奇县','乌恰县']}, +{name:'喀什地区', areaList:['喀什市','疏附县','疏勒县','英吉沙县','泽普县','莎车县','叶城县','麦盖提县','岳普湖县','伽师县','巴楚县','塔什库尔干塔吉克自治县']}, +{name:'和田地区', areaList:['和田市','和田县','墨玉县','皮山县','洛浦县','策勒县','于田县','民丰县']}, +{name:'伊犁哈萨克自治州', areaList:['伊宁市','奎屯市','伊宁县','察布查尔锡伯自治县','霍城县','巩留县','新源县','昭苏县','特克斯县','尼勒克县']}, +{name:'塔城地区', areaList:['塔城市','乌苏市','额敏县','沙湾县','托里县','裕民县','和布克赛尔蒙古自治县']}, +{name:'阿勒泰地区', areaList:['阿勒泰市','布尔津县','富蕴县','福海县','哈巴河县','青河县','吉木乃县']}, +{name:'省直辖行政单位', areaList:['石河子市','阿拉尔市','图木舒克市','五家渠市']} +]}, +{name:'河北', cityList:[ +{name:'石家庄市', areaList:['市辖区','长安区','桥东区','桥西区','新华区','井陉矿区','裕华区','井陉县','正定县','栾城县','行唐县','灵寿县','高邑县','深泽县','赞皇县','无极县','平山县','元氏县','赵 县','辛集市','藁城市','晋州市','新乐市','鹿泉市']}, +{name:'唐山市', areaList:['市辖区','路南区','路北区','古冶区','开平区','丰南区','丰润区','滦 县','滦南县','乐亭县','迁西县','玉田县','唐海县','遵化市','迁安市']}, +{name:'秦皇岛市', areaList:['市辖区','海港区','山海关区','北戴河区','青龙满族自治县','昌黎县','抚宁县','卢龙县']}, +{name:'邯郸市', areaList:['市辖区','邯山区','丛台区','复兴区','峰峰矿区','邯郸县','临漳县','成安县','大名县','涉 县','磁 县','肥乡县','永年县','邱 县','鸡泽县','广平县','馆陶县','魏 县','曲周县','武安市']}, +{name:'邢台市', areaList:['市辖区','桥东区','桥西区','邢台县','临城县','内丘县','柏乡县','隆尧县','任 县','南和县','宁晋县','巨鹿县','新河县','广宗县','平乡县','威 县','清河县','临西县','南宫市','沙河市']}, +{name:'保定市', areaList:['市辖区','新市区','北市区','南市区','满城县','清苑县','涞水县','阜平县','徐水县','定兴县','唐 县','高阳县','容城县','涞源县','望都县','安新县','易 县','曲阳县','蠡 县','顺平县','博野县','雄 县','涿州市','定州市','安国市','高碑店市']}, +{name:'张家口市', areaList:['市辖区','桥东区','桥西区','宣化区','下花园区','宣化县','张北县','康保县','沽源县','尚义县','蔚 县','阳原县','怀安县','万全县','怀来县','涿鹿县','赤城县','崇礼县']}, +{name:'承德市', areaList:['市辖区','双桥区','双滦区','鹰手营子矿区','承德县','兴隆县','平泉县','滦平县','隆化县','丰宁满族自治县','宽城满族自治县','围场满族蒙古族自治县']}, +{name:'沧州市', areaList:['市辖区','新华区','运河区','沧 县','青 县','东光县','海兴县','盐山县','肃宁县','南皮县','吴桥县','献 县','孟村回族自治县','泊头市','任丘市','黄骅市','河间市']}, +{name:'廊坊市', areaList:['市辖区','安次区','广阳区','固安县','永清县','香河县','大城县','文安县','大厂回族自治县','霸州市','三河市']}, +{name:'衡水市', areaList:['市辖区','桃城区','枣强县','武邑县','武强县','饶阳县','安平县','故城县','景 县','阜城县','冀州市','深州市']} +]}, +{name:'山西', cityList:[ +{name:'太原市', areaList:['市辖区','小店区','迎泽区','杏花岭区','尖草坪区','万柏林区','晋源区','清徐县','阳曲县','娄烦县','古交市']}, +{name:'大同市', areaList:['市辖区','城 区','矿 区','南郊区','新荣区','阳高县','天镇县','广灵县','灵丘县','浑源县','左云县','大同县']}, +{name:'阳泉市', areaList:['市辖区','城 区','矿 区','郊 区','平定县','盂 县']}, +{name:'长治市', areaList:['市辖区','城 区','郊 区','长治县','襄垣县','屯留县','平顺县','黎城县','壶关县','长子县','武乡县','沁 县','沁源县','潞城市']}, +{name:'晋城市', areaList:['市辖区','城 区','沁水县','阳城县','陵川县','泽州县','高平市']}, +{name:'朔州市', areaList:['市辖区','朔城区','平鲁区','山阴县','应 县','右玉县','怀仁县']}, +{name:'晋中市', areaList:['市辖区','榆次区','榆社县','左权县','和顺县','昔阳县','寿阳县','太谷县','祁 县','平遥县','灵石县','介休市']}, +{name:'运城市', areaList:['市辖区','盐湖区','临猗县','万荣县','闻喜县','稷山县','新绛县','绛 县','垣曲县','夏 县','平陆县','芮城县','永济市','河津市']}, +{name:'忻州市', areaList:['市辖区','忻府区','定襄县','五台县','代 县','繁峙县','宁武县','静乐县','神池县','五寨县','岢岚县','河曲县','保德县','偏关县','原平市']}, +{name:'临汾市', areaList:['市辖区','尧都区','曲沃县','翼城县','襄汾县','洪洞县','古 县','安泽县','浮山县','吉 县','乡宁县','大宁县','隰 县','永和县','蒲 县','汾西县','侯马市','霍州市']}, +{name:'吕梁市', areaList:['市辖区','离石区','文水县','交城县','兴 县','临 县','柳林县','石楼县','岚 县','方山县','中阳县','交口县','孝义市','汾阳市']} +]}, +{name:'内蒙古', cityList:[ +{name:'呼和浩特市', areaList:['市辖区','新城区','回民区','玉泉区','赛罕区','土默特左旗','托克托县','和林格尔县','清水河县','武川县']}, +{name:'包头市', areaList:['市辖区','东河区','昆都仑区','青山区','石拐区','白云矿区','九原区','土默特右旗','固阳县','达尔罕茂明安联合旗']}, +{name:'乌海市', areaList:['市辖区','海勃湾区','海南区','乌达区']}, +{name:'赤峰市', areaList:['市辖区','红山区','元宝山区','松山区','阿鲁科尔沁旗','巴林左旗','巴林右旗','林西县','克什克腾旗','翁牛特旗','喀喇沁旗','宁城县','敖汉旗']}, +{name:'通辽市', areaList:['市辖区','科尔沁区','科尔沁左翼中旗','科尔沁左翼后旗','开鲁县','库伦旗','奈曼旗','扎鲁特旗','霍林郭勒市']}, +{name:'鄂尔多斯市', areaList:['东胜区','达拉特旗','准格尔旗','鄂托克前旗','鄂托克旗','杭锦旗','乌审旗','伊金霍洛旗']}, +{name:'呼伦贝尔市', areaList:['市辖区','海拉尔区','阿荣旗','莫力达瓦达斡尔族自治旗','鄂伦春自治旗','鄂温克族自治旗','陈巴尔虎旗','新巴尔虎左旗','新巴尔虎右旗','满洲里市','牙克石市','扎兰屯市','额尔古纳市','根河市']}, +{name:'巴彦淖尔市', areaList:['市辖区','临河区','五原县','磴口县','乌拉特前旗','乌拉特中旗','乌拉特后旗','杭锦后旗']}, +{name:'乌兰察布市', areaList:['市辖区','集宁区','卓资县','化德县','商都县','兴和县','凉城县','察哈尔右翼前旗','察哈尔右翼中旗','察哈尔右翼后旗','四子王旗','丰镇市']}, +{name:'兴安盟', areaList:['乌兰浩特市','阿尔山市','科尔沁右翼前旗','科尔沁右翼中旗','扎赉特旗','突泉县']}, +{name:'锡林郭勒盟', areaList:['二连浩特市','锡林浩特市','阿巴嘎旗','苏尼特左旗','苏尼特右旗','东乌珠穆沁旗','西乌珠穆沁旗','太仆寺旗','镶黄旗','正镶白旗','正蓝旗','多伦县']}, +{name:'阿拉善盟', areaList:['阿拉善左旗','阿拉善右旗','额济纳旗']} +]}, +{name:'江苏', cityList:[ +{name:'南京市', areaList:['市辖区','玄武区','白下区','秦淮区','建邺区','鼓楼区','下关区','浦口区','栖霞区','雨花台区','江宁区','六合区','溧水县','高淳县']}, +{name:'无锡市', areaList:['市辖区','崇安区','南长区','北塘区','锡山区','惠山区','滨湖区','江阴市','宜兴市']}, +{name:'徐州市', areaList:['市辖区','鼓楼区','云龙区','九里区','贾汪区','泉山区','丰 县','沛 县','铜山县','睢宁县','新沂市','邳州市']}, +{name:'常州市', areaList:['市辖区','天宁区','钟楼区','戚墅堰区','新北区','武进区','溧阳市','金坛市']}, +{name:'苏州市', areaList:['市辖区','沧浪区','平江区','金阊区','虎丘区','吴中区','相城区','常熟市','张家港市','昆山市','吴江市','太仓市']}, +{name:'南通市', areaList:['市辖区','崇川区','港闸区','海安县','如东县','启东市','如皋市','通州市','海门市']}, +{name:'连云港市', areaList:['市辖区','连云区','新浦区','海州区','赣榆县','东海县','灌云县','灌南县']}, +{name:'淮安市', areaList:['市辖区','清河区','楚州区','淮阴区','清浦区','涟水县','洪泽县','盱眙县','金湖县']}, +{name:'盐城市', areaList:['市辖区','亭湖区','盐都区','响水县','滨海县','阜宁县','射阳县','建湖县','东台市','大丰市']}, +{name:'扬州市', areaList:['市辖区','广陵区','邗江区','郊 区','宝应县','仪征市','高邮市','江都市']}, +{name:'镇江市', areaList:['市辖区','京口区','润州区','丹徒区','丹阳市','扬中市','句容市']}, +{name:'泰州市', areaList:['市辖区','海陵区','高港区','兴化市','靖江市','泰兴市','姜堰市']}, +{name:'宿迁市', areaList:['市辖区','宿城区','宿豫区','沭阳县','泗阳县','泗洪县']} +]}, +{name:'浙江', cityList:[ +{name:'杭州市', areaList:['市辖区','上城区','下城区','江干区','拱墅区','西湖区','滨江区','萧山区','余杭区','桐庐县','淳安县','建德市','富阳市','临安市']}, +{name:'宁波市', areaList:['市辖区','海曙区','江东区','江北区','北仑区','镇海区','鄞州区','象山县','宁海县','余姚市','慈溪市','奉化市']}, +{name:'温州市', areaList:['市辖区','鹿城区','龙湾区','瓯海区','洞头县','永嘉县','平阳县','苍南县','文成县','泰顺县','瑞安市','乐清市']}, +{name:'嘉兴市', areaList:['市辖区','秀城区','秀洲区','嘉善县','海盐县','海宁市','平湖市','桐乡市']}, +{name:'湖州市', areaList:['市辖区','吴兴区','南浔区','德清县','长兴县','安吉县']}, +{name:'绍兴市', areaList:['市辖区','越城区','绍兴县','新昌县','诸暨市','上虞市','嵊州市']}, +{name:'金华市', areaList:['市辖区','婺城区','金东区','武义县','浦江县','磐安县','兰溪市','义乌市','东阳市','永康市']}, +{name:'衢州市', areaList:['市辖区','柯城区','衢江区','常山县','开化县','龙游县','江山市']}, +{name:'舟山市', areaList:['市辖区','定海区','普陀区','岱山县','嵊泗县']}, +{name:'台州市', areaList:['市辖区','椒江区','黄岩区','路桥区','玉环县','三门县','天台县','仙居县','温岭市','临海市']}, +{name:'丽水市', areaList:['市辖区','莲都区','青田县','缙云县','遂昌县','松阳县','云和县','庆元县','景宁畲族自治县','龙泉市']} +]}, +{name:'安徽', cityList:[ +{name:'合肥市', areaList:['市辖区','瑶海区','庐阳区','蜀山区','包河区','长丰县','肥东县','肥西县']}, +{name:'芜湖市', areaList:['市辖区','镜湖区','马塘区','新芜区','鸠江区','芜湖县','繁昌县','南陵县']}, +{name:'蚌埠市', areaList:['市辖区','龙子湖区','蚌山区','禹会区','淮上区','怀远县','五河县','固镇县']}, +{name:'淮南市', areaList:['市辖区','大通区','田家庵区','谢家集区','八公山区','潘集区','凤台县']}, +{name:'马鞍山市', areaList:['市辖区','金家庄区','花山区','雨山区','当涂县']}, +{name:'淮北市', areaList:['市辖区','杜集区','相山区','烈山区','濉溪县']}, +{name:'铜陵市', areaList:['市辖区','铜官山区','狮子山区','郊 区','铜陵县']}, +{name:'安庆市', areaList:['市辖区','迎江区','大观区','郊 区','怀宁县','枞阳县','潜山县','太湖县','宿松县','望江县','岳西县','桐城市']}, +{name:'黄山市', areaList:['市辖区','屯溪区','黄山区','徽州区','歙 县','休宁县','黟 县','祁门县']}, +{name:'滁州市', areaList:['市辖区','琅琊区','南谯区','来安县','全椒县','定远县','凤阳县','天长市','明光市']}, +{name:'阜阳市', areaList:['市辖区','颍州区','颍东区','颍泉区','临泉县','太和县','阜南县','颍上县','界首市']}, +{name:'宿州市', areaList:['市辖区','墉桥区','砀山县','萧 县','灵璧县','泗 县']}, +{name:'巢湖市', areaList:['市辖区','居巢区','庐江县','无为县','含山县','和 县']}, +{name:'六安市', areaList:['市辖区','金安区','裕安区','寿 县','霍邱县','舒城县','金寨县','霍山县']}, +{name:'亳州市', areaList:['市辖区','谯城区','涡阳县','蒙城县','利辛县']}, +{name:'池州市', areaList:['市辖区','贵池区','东至县','石台县','青阳县']}, +{name:'宣城市', areaList:['市辖区','宣州区','郎溪县','广德县','泾 县','绩溪县','旌德县','宁国市']} +]}, +{name:'福建', cityList:[ +{name:'福州市', areaList:['市辖区','鼓楼区','台江区','仓山区','马尾区','晋安区','闽侯县','连江县','罗源县','闽清县','永泰县','平潭县','福清市','长乐市']}, +{name:'厦门市', areaList:['市辖区','思明区','海沧区','湖里区','集美区','同安区','翔安区']}, +{name:'莆田市', areaList:['市辖区','城厢区','涵江区','荔城区','秀屿区','仙游县']}, +{name:'三明市', areaList:['市辖区','梅列区','三元区','明溪县','清流县','宁化县','大田县','尤溪县','沙 县','将乐县','泰宁县','建宁县','永安市']}, +{name:'泉州市', areaList:['市辖区','鲤城区','丰泽区','洛江区','泉港区','惠安县','安溪县','永春县','德化县','金门县','石狮市','晋江市','南安市']}, +{name:'漳州市', areaList:['市辖区','芗城区','龙文区','云霄县','漳浦县','诏安县','长泰县','东山县','南靖县','平和县','华安县','龙海市']}, +{name:'南平市', areaList:['市辖区','延平区','顺昌县','浦城县','光泽县','松溪县','政和县','邵武市','武夷山市','建瓯市','建阳市']}, +{name:'龙岩市', areaList:['市辖区','新罗区','长汀县','永定县','上杭县','武平县','连城县','漳平市']}, +{name:'宁德市', areaList:['市辖区','蕉城区','霞浦县','古田县','屏南县','寿宁县','周宁县','柘荣县','福安市','福鼎市']} +]}, +{name:'江西', cityList:[ +{name:'南昌市', areaList:['市辖区','东湖区','西湖区','青云谱区','湾里区','青山湖区','南昌县','新建县','安义县','进贤县']}, +{name:'景德镇市', areaList:['市辖区','昌江区','珠山区','浮梁县','乐平市']}, +{name:'萍乡市', areaList:['市辖区','安源区','湘东区','莲花县','上栗县','芦溪县']}, +{name:'九江市', areaList:['市辖区','庐山区','浔阳区','九江县','武宁县','修水县','永修县','德安县','星子县','都昌县','湖口县','彭泽县','瑞昌市']}, +{name:'新余市', areaList:['市辖区','渝水区','分宜县']}, +{name:'鹰潭市', areaList:['市辖区','月湖区','余江县','贵溪市']}, +{name:'赣州市', areaList:['市辖区','章贡区','赣 县','信丰县','大余县','上犹县','崇义县','安远县','龙南县','定南县','全南县','宁都县','于都县','兴国县','会昌县','寻乌县','石城县','瑞金市','南康市']}, +{name:'吉安市', areaList:['市辖区','吉州区','青原区','吉安县','吉水县','峡江县','新干县','永丰县','泰和县','遂川县','万安县','安福县','永新县','井冈山市']}, +{name:'宜春市', areaList:['市辖区','袁州区','奉新县','万载县','上高县','宜丰县','靖安县','铜鼓县','丰城市','樟树市','高安市']}, +{name:'抚州市', areaList:['市辖区','临川区','南城县','黎川县','南丰县','崇仁县','乐安县','宜黄县','金溪县','资溪县','东乡县','广昌县']}, +{name:'上饶市', areaList:['市辖区','信州区','上饶县','广丰县','玉山县','铅山县','横峰县','弋阳县','余干县','鄱阳县','万年县','婺源县','德兴市']} +]}, +{name:'山东', cityList:[ +{name:'济南市', areaList:['市辖区','历下区','市中区','槐荫区','天桥区','历城区','长清区','平阴县','济阳县','商河县','章丘市']}, +{name:'青岛市', areaList:['市辖区','市南区','市北区','四方区','黄岛区','崂山区','李沧区','城阳区','胶州市','即墨市','平度市','胶南市','莱西市']}, +{name:'淄博市', areaList:['市辖区','淄川区','张店区','博山区','临淄区','周村区','桓台县','高青县','沂源县']}, +{name:'枣庄市', areaList:['市辖区','市中区','薛城区','峄城区','台儿庄区','山亭区','滕州市']}, +{name:'东营市', areaList:['市辖区','东营区','河口区','垦利县','利津县','广饶县']}, +{name:'烟台市', areaList:['市辖区','芝罘区','福山区','牟平区','莱山区','长岛县','龙口市','莱阳市','莱州市','蓬莱市','招远市','栖霞市','海阳市']}, +{name:'潍坊市', areaList:['市辖区','潍城区','寒亭区','坊子区','奎文区','临朐县','昌乐县','青州市','诸城市','寿光市','安丘市','高密市','昌邑市']}, +{name:'济宁市', areaList:['市辖区','市中区','任城区','微山县','鱼台县','金乡县','嘉祥县','汶上县','泗水县','梁山县','曲阜市','兖州市','邹城市']}, +{name:'泰安市', areaList:['市辖区','泰山区','岱岳区','宁阳县','东平县','新泰市','肥城市']}, +{name:'威海市', areaList:['市辖区','环翠区','文登市','荣成市','乳山市']}, +{name:'日照市', areaList:['市辖区','东港区','岚山区','五莲县','莒 县']}, +{name:'莱芜市', areaList:['市辖区','莱城区','钢城区']}, +{name:'临沂市', areaList:['市辖区','兰山区','罗庄区','河东区','沂南县','郯城县','沂水县','苍山县','费 县','平邑县','莒南县','蒙阴县','临沭县']}, +{name:'德州市', areaList:['市辖区','德城区','陵 县','宁津县','庆云县','临邑县','齐河县','平原县','夏津县','武城县','乐陵市','禹城市']}, +{name:'聊城市', areaList:['市辖区','东昌府区','阳谷县','莘 县','茌平县','东阿县','冠 县','高唐县','临清市']}, +{name:'滨州市', areaList:['市辖区','滨城区','惠民县','阳信县','无棣县','沾化县','博兴县','邹平县']}, +{name:'荷泽市', areaList:['市辖区','牡丹区','曹 县','单 县','成武县','巨野县','郓城县','鄄城县','定陶县','东明县']} +]}, +{name:'辽宁', cityList:[ +{name:'沈阳市', areaList:['市辖区','和平区','沈河区','大东区','皇姑区','铁西区','苏家屯区','东陵区','新城子区','于洪区','辽中县','康平县','法库县','新民市']}, +{name:'大连市', areaList:['市辖区','中山区','西岗区','沙河口区','甘井子区','旅顺口区','金州区','长海县','瓦房店市','普兰店市','庄河市']}, +{name:'鞍山市', areaList:['市辖区','铁东区','铁西区','立山区','千山区','台安县','岫岩满族自治县','海城市']}, +{name:'抚顺市', areaList:['市辖区','新抚区','东洲区','望花区','顺城区','抚顺县','新宾满族自治县','清原满族自治县']}, +{name:'本溪市', areaList:['市辖区','平山区','溪湖区','明山区','南芬区','本溪满族自治县','桓仁满族自治县']}, +{name:'丹东市', areaList:['市辖区','元宝区','振兴区','振安区','宽甸满族自治县','东港市','凤城市']}, +{name:'锦州市', areaList:['市辖区','古塔区','凌河区','太和区','黑山县','义 县','凌海市','北宁市']}, +{name:'营口市', areaList:['市辖区','站前区','西市区','鲅鱼圈区','老边区','盖州市','大石桥市']}, +{name:'阜新市', areaList:['市辖区','海州区','新邱区','太平区','清河门区','细河区','阜新蒙古族自治县','彰武县']}, +{name:'辽阳市', areaList:['市辖区','白塔区','文圣区','宏伟区','弓长岭区','太子河区','辽阳县','灯塔市']}, +{name:'盘锦市', areaList:['市辖区','双台子区','兴隆台区','大洼县','盘山县']}, +{name:'铁岭市', areaList:['市辖区','银州区','清河区','铁岭县','西丰县','昌图县','调兵山市','开原市']}, +{name:'朝阳市', areaList:['市辖区','双塔区','龙城区','朝阳县','建平县','喀喇沁左翼蒙古族自治县','北票市','凌源市']}, +{name:'葫芦岛市', areaList:['市辖区','连山区','龙港区','南票区','绥中县','建昌县','兴城市']} +]}, +{name:'吉林', cityList:[ +{name:'长春市', areaList:['市辖区','南关区','宽城区','朝阳区','二道区','绿园区','双阳区','农安县','九台市','榆树市','德惠市']}, +{name:'吉林市', areaList:['市辖区','昌邑区','龙潭区','船营区','丰满区','永吉县','蛟河市','桦甸市','舒兰市','磐石市']}, +{name:'四平市', areaList:['市辖区','铁西区','铁东区','梨树县','伊通满族自治县','公主岭市','双辽市']}, +{name:'辽源市', areaList:['市辖区','龙山区','西安区','东丰县','东辽县']}, +{name:'通化市', areaList:['市辖区','东昌区','二道江区','通化县','辉南县','柳河县','梅河口市','集安市']}, +{name:'白山市', areaList:['市辖区','八道江区','抚松县','靖宇县','长白朝鲜族自治县','江源县','临江市']}, +{name:'松原市', areaList:['市辖区','宁江区','前郭尔罗斯蒙古族自治县','长岭县','乾安县','扶余县']}, +{name:'白城市', areaList:['市辖区','洮北区','镇赉县','通榆县','洮南市','大安市']}, +{name:'延边朝鲜族自治州', areaList:['延吉市','图们市','敦化市','珲春市','龙井市','和龙市','汪清县','安图县']} +]}, +{name:'黑龙江', cityList:[ +{name:'哈尔滨市', areaList:['市辖区','道里区','南岗区','道外区','香坊区','动力区','平房区','松北区','呼兰区','依兰县','方正县','宾 县','巴彦县','木兰县','通河县','延寿县','阿城市','双城市','尚志市','五常市']}, +{name:'齐齐哈尔市', areaList:['市辖区','龙沙区','建华区','铁锋区','昂昂溪区','富拉尔基区','碾子山区','梅里斯达斡尔族区','龙江县','依安县','泰来县','甘南县','富裕县','克山县','克东县','拜泉县','讷河市']}, +{name:'鸡西市', areaList:['市辖区','鸡冠区','恒山区','滴道区','梨树区','城子河区','麻山区','鸡东县','虎林市','密山市']}, +{name:'鹤岗市', areaList:['市辖区','向阳区','工农区','南山区','兴安区','东山区','兴山区','萝北县','绥滨县']}, +{name:'双鸭山市', areaList:['市辖区','尖山区','岭东区','四方台区','宝山区','集贤县','友谊县','宝清县','饶河县']}, +{name:'大庆市', areaList:['市辖区','萨尔图区','龙凤区','让胡路区','红岗区','大同区','肇州县','肇源县','林甸县','杜尔伯特蒙古族自治县']}, +{name:'伊春市', areaList:['市辖区','伊春区','南岔区','友好区','西林区','翠峦区','新青区','美溪区','金山屯区','五营区','乌马河区','汤旺河区','带岭区','乌伊岭区','红星区','上甘岭区','嘉荫县','铁力市']}, +{name:'佳木斯市', areaList:['市辖区','永红区','向阳区','前进区','东风区','郊 区','桦南县','桦川县','汤原县','抚远县','同江市','富锦市']}, +{name:'七台河市', areaList:['市辖区','新兴区','桃山区','茄子河区','勃利县']}, +{name:'牡丹江市', areaList:['市辖区','东安区','阳明区','爱民区','西安区','东宁县','林口县','绥芬河市','海林市','宁安市','穆棱市']}, +{name:'黑河市', areaList:['市辖区','爱辉区','嫩江县','逊克县','孙吴县','北安市','五大连池市']}, +{name:'绥化市', areaList:['市辖区','北林区','望奎县','兰西县','青冈县','庆安县','明水县','绥棱县','安达市','肇东市','海伦市']}, +{name:'大兴安岭地区', areaList:['呼玛县','塔河县','漠河县']} +]}, +{name:'海南', cityList:[ +{name:'海口市', areaList:['市辖区','秀英区','龙华区','琼山区','美兰区']}, +{name:'三亚市', areaList:['市辖区']}, +{name:'省直辖县级行政单位', areaList:['五指山市','琼海市','儋州市','文昌市','万宁市','东方市','定安县','屯昌县','澄迈县','临高县','白沙黎族自治县','昌江黎族自治县','乐东黎族自治县','陵水黎族自治县','保亭黎族苗族自治县','琼中黎族苗族自治县','西沙群岛','南沙群岛','中沙群岛的岛礁及其海域']} +]}, +{name:'台湾', cityList:[ +{name:'台湾', areaList:['台湾']} +]}, +{name:'香港', cityList:[ +{name:'香港', areaList:['香港']} +]}, +{name:'澳门', cityList:[ +{name:'澳门', areaList:['澳门']} +]} +]; diff --git a/main/webapp/static/lib/layui/css/layui.css b/main/webapp/static/lib/layui/css/layui.css new file mode 100644 index 0000000..0213787 --- /dev/null +++ b/main/webapp/static/lib/layui/css/layui.css @@ -0,0 +1,2 @@ +/** layui-v2.2.6 MIT License By https://www.layui.com */ + .layui-inline,img{display:inline-block;vertical-align:middle}h1,h2,h3,h4,h5,h6{font-weight:400}.layui-edge,.layui-header,.layui-inline,.layui-main{position:relative}.layui-btn,.layui-edge,.layui-inline,img{vertical-align:middle}.layui-btn,.layui-disabled,.layui-icon,.layui-unselect{-webkit-user-select:none;-ms-user-select:none;-moz-user-select:none}blockquote,body,button,dd,div,dl,dt,form,h1,h2,h3,h4,h5,h6,input,li,ol,p,pre,td,textarea,th,ul{margin:0;padding:0;-webkit-tap-highlight-color:rgba(0,0,0,0)}a:active,a:hover{outline:0}img{border:none}li{list-style:none}table{border-collapse:collapse;border-spacing:0}h4,h5,h6{font-size:100%}button,input,optgroup,option,select,textarea{font-family:inherit;font-size:inherit;font-style:inherit;font-weight:inherit;outline:0}pre{white-space:pre-wrap;white-space:-moz-pre-wrap;white-space:-pre-wrap;white-space:-o-pre-wrap;word-wrap:break-word}body{line-height:24px;font:14px Helvetica Neue,Helvetica,PingFang SC,\5FAE\8F6F\96C5\9ED1,Tahoma,Arial,sans-serif}hr{height:1px;margin:10px 0;border:0;clear:both}a{color:#333;text-decoration:none}a:hover{color:#777}a cite{font-style:normal;*cursor:pointer}.layui-border-box,.layui-border-box *{box-sizing:border-box}.layui-box,.layui-box *{box-sizing:content-box}.layui-clear{clear:both;*zoom:1}.layui-clear:after{content:'\20';clear:both;*zoom:1;display:block;height:0}.layui-inline{*display:inline;*zoom:1}.layui-edge{display:inline-block;width:0;height:0;border-width:6px;border-style:dashed;border-color:transparent;overflow:hidden}.layui-edge-top{top:-4px;border-bottom-color:#999;border-bottom-style:solid}.layui-edge-right{border-left-color:#999;border-left-style:solid}.layui-edge-bottom{top:2px;border-top-color:#999;border-top-style:solid}.layui-edge-left{border-right-color:#999;border-right-style:solid}.layui-elip{text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.layui-disabled,.layui-disabled:hover{color:#d2d2d2!important;cursor:not-allowed!important}.layui-circle{border-radius:100%}.layui-show{display:block!important}.layui-hide{display:none!important}@font-face{font-family:layui-icon;src:url(../font/iconfont.eot?v=226_rc2);src:url(../font/iconfont.eot?v=226_rc2#iefix) format('embedded-opentype'),url(../font/iconfont.svg?v=226_rc2#iconfont) format('svg'),url(../font/iconfont.woff?v=226_rc2) format('woff'),url(../font/iconfont.ttf?v=226_rc2) format('truetype')}.layui-icon{font-family:layui-icon!important;font-size:16px;font-style:normal;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.layui-icon-duihua:before{content:"\e611"}.layui-icon-shezhi:before{content:"\e614"}.layui-icon-yinshenim:before{content:"\e60f"}.layui-icon-search:before{content:"\e615"}.layui-icon-fenxiang1:before{content:"\e641"}.layui-icon-shezhi1:before{content:"\e620"}.layui-icon-yinqing:before{content:"\e628"}.layui-icon-close:before{content:"\1006"}.layui-icon-close-fill:before{content:"\1007"}.layui-icon-baobiao:before{content:"\e629"}.layui-icon-star:before{content:"\e600"}.layui-icon-yuandian:before{content:"\e617"}.layui-icon-chat:before{content:"\e606"}.layui-icon-logo:before{content:"\e609"}.layui-icon-list:before{content:"\e60a"}.layui-icon-tubiao:before{content:"\e62c"}.layui-icon-ok-circle:before{content:"\1005"}.layui-icon-huanfu2:before{content:"\e61b"}.layui-icon-On-line:before{content:"\e610"}.layui-icon-biaoge:before{content:"\e62d"}.layui-icon-right:before{content:"\e602"}.layui-icon-left:before{content:"\e603"}.layui-icon-cart-simple:before{content:"\e698"}.layui-icon-cry:before{content:"\e69c"}.layui-icon-smile:before{content:"\e6af"}.layui-icon-survey:before{content:"\e6b2"}.layui-icon-tree:before{content:"\e62e"}.layui-icon-iconfont17:before{content:"\e62f"}.layui-icon-tianjia:before{content:"\e61f"}.layui-icon-download-circle:before{content:"\e601"}.layui-icon-xuanzemoban48:before{content:"\e630"}.layui-icon-gongju:before{content:"\e631"}.layui-icon-face-surprised:before{content:"\e664"}.layui-icon-bianji:before{content:"\e642"}.layui-icon-speaker:before{content:"\e645"}.layui-icon-down:before{content:"\e61a"}.layui-icon-wenjian:before{content:"\e621"}.layui-icon-layouts:before{content:"\e632"}.layui-icon-duigou:before{content:"\e618"}.layui-icon-tianjia1:before{content:"\e608"}.layui-icon-yaoyaozhibofanye:before{content:"\e633"}.layui-icon-read:before{content:"\e705"}.layui-icon-404:before{content:"\e61c"}.layui-icon-lunbozutu:before{content:"\e634"}.layui-icon-help:before{content:"\e607"}.layui-icon-daima1:before{content:"\e635"}.layui-icon-jinshui:before{content:"\e636"}.layui-icon-username:before{content:"\e66f"}.layui-icon-find-fill:before{content:"\e670"}.layui-icon-about:before{content:"\e60b"}.layui-icon-location:before{content:"\e715"}.layui-icon-up:before{content:"\e619"}.layui-icon-pause:before{content:"\e651"}.layui-icon-riqi:before{content:"\e637"}.layui-icon-uploadfile:before{content:"\e61d"}.layui-icon-delete:before{content:"\e640"}.layui-icon-play:before{content:"\e652"}.layui-icon-top:before{content:"\e604"}.layui-icon-haoyouqingqiu:before{content:"\e612"}.layui-icon-refresh-3:before{content:"\e9aa"}.layui-icon-weibiaoti1:before{content:"\e605"}.layui-icon-chuangkou:before{content:"\e638"}.layui-icon-comiisbiaoqing:before{content:"\e60c"}.layui-icon-zhengque:before{content:"\e616"}.layui-icon-dollar:before{content:"\e659"}.layui-icon-iconfontwodehaoyou:before{content:"\e613"}.layui-icon-wenjianxiazai:before{content:"\e61e"}.layui-icon-tupian:before{content:"\e60d"}.layui-icon-lianjie:before{content:"\e64c"}.layui-icon-diamond:before{content:"\e735"}.layui-icon-jilu:before{content:"\e60e"}.layui-icon-liucheng:before{content:"\e622"}.layui-icon-fontstrikethrough:before{content:"\e64f"}.layui-icon-unlink:before{content:"\e64d"}.layui-icon-bianjiwenzi:before{content:"\e639"}.layui-icon-sanjiao:before{content:"\e623"}.layui-icon-danxuankuanghouxuan:before{content:"\e63f"}.layui-icon-danxuankuangxuanzhong:before{content:"\e643"}.layui-icon-juzhongduiqi:before{content:"\e647"}.layui-icon-youduiqi:before{content:"\e648"}.layui-icon-zuoduiqi:before{content:"\e649"}.layui-icon-gongsisvgtubiaozongji22:before{content:"\e626"}.layui-icon-gongsisvgtubiaozongji23:before{content:"\e627"}.layui-icon-refresh-2:before{content:"\1002"}.layui-icon-loading-1:before{content:"\e63e"}.layui-icon-return:before{content:"\e65c"}.layui-icon-jiacu:before{content:"\e62b"}.layui-icon-uploading:before{content:"\e67c"}.layui-icon-liaotianduihuaimgoutong:before{content:"\e63a"}.layui-icon-video:before{content:"\e6ed"}.layui-icon-headset:before{content:"\e6fc"}.layui-icon-wenjianjiafan:before{content:"\e624"}.layui-icon-shouji:before{content:"\e63b"}.layui-icon-tianjia2:before{content:"\e654"}.layui-icon-wenjianjia:before{content:"\e7a0"}.layui-icon-biaoqing:before{content:"\e650"}.layui-icon-html:before{content:"\e64b"}.layui-icon-biaodan:before{content:"\e63c"}.layui-icon-cart:before{content:"\e657"}.layui-icon-camera-fill:before{content:"\e65d"}.layui-icon-25:before{content:"\e62a"}.layui-icon-emwdaima:before{content:"\e64e"}.layui-icon-fire:before{content:"\e756"}.layui-icon-set:before{content:"\e716"}.layui-icon-zitixiahuaxian:before{content:"\e646"}.layui-icon-sanjiao1:before{content:"\e625"}.layui-icon-tips:before{content:"\e702"}.layui-icon-tupian-copy-copy:before{content:"\e64a"}.layui-icon-more-vertical:before{content:"\e671"}.layui-icon-zhuti2:before{content:"\e66c"}.layui-icon-loading:before{content:"\e63d"}.layui-icon-xieti:before{content:"\e644"}.layui-icon-refresh-1:before{content:"\e666"}.layui-icon-rmb:before{content:"\e65e"}.layui-icon-home:before{content:"\e68e"}.layui-icon-user:before{content:"\e770"}.layui-icon-notice:before{content:"\e667"}.layui-icon-login-weibo:before{content:"\e675"}.layui-icon-voice:before{content:"\e688"}.layui-icon-download:before{content:"\e681"}.layui-icon-login-qq:before{content:"\e676"}.layui-icon-snowflake:before{content:"\e6b1"}.layui-icon-yemian1:before{content:"\e655"}.layui-icon-template:before{content:"\e663"}.layui-icon-auz:before{content:"\e672"}.layui-icon-console:before{content:"\e665"}.layui-icon-app:before{content:"\e653"}.layui-icon-prev:before{content:"\e65a"}.layui-icon-website:before{content:"\e7ae"}.layui-icon-next:before{content:"\e65b"}.layui-icon-component:before{content:"\e857"}.layui-icon-more:before{content:"\e65f"}.layui-icon-login-wechat:before{content:"\e677"}.layui-icon-shrink-right:before{content:"\e668"}.layui-icon-spread-left:before{content:"\e66b"}.layui-icon-camera:before{content:"\e660"}.layui-icon-note:before{content:"\e66e"}.layui-icon-refresh:before{content:"\e669"}.layui-icon-nv:before{content:"\e661"}.layui-icon-nan:before{content:"\e662"}.layui-icon-password:before{content:"\e673"}.layui-icon-senior:before{content:"\e674"}.layui-icon-theme:before{content:"\e66a"}.layui-icon-tread:before{content:"\e6c5"}.layui-icon-praise:before{content:"\e6c6"}.layui-icon-star-fill:before{content:"\e658"}.layui-icon-template-1:before{content:"\e656"}.layui-icon-loading-2:before{content:"\e66d"}.layui-icon-vercode:before{content:"\e679"}.layui-icon-cellphone:before{content:"\e678"}.layui-main{width:1140px;margin:0 auto}.layui-header{z-index:1000;height:60px}.layui-header a:hover{transition:all .5s;-webkit-transition:all .5s}.layui-side{position:fixed;left:0;top:0;bottom:0;z-index:999;width:200px;overflow-x:hidden}.layui-side-scroll{position:relative;width:220px;height:100%;overflow-x:hidden}.layui-body{position:absolute;left:200px;right:0;top:0;bottom:0;z-index:998;width:auto;overflow:hidden;overflow-y:auto;box-sizing:border-box}.layui-layout-body{overflow:hidden}.layui-layout-admin .layui-header{background-color:#23262E}.layui-layout-admin .layui-side{top:60px;width:200px;overflow-x:hidden}.layui-layout-admin .layui-body{top:60px;bottom:44px}.layui-layout-admin .layui-main{width:auto;margin:0 15px}.layui-layout-admin .layui-footer{position:fixed;left:200px;right:0;bottom:0;height:44px;line-height:44px;padding:0 15px;background-color:#eee}.layui-layout-admin .layui-logo{position:absolute;left:0;top:0;width:200px;height:100%;line-height:60px;text-align:center;color:#009688;font-size:16px}.layui-layout-admin .layui-header .layui-nav{background:0 0}.layui-layout-left{position:absolute!important;left:200px;top:0}.layui-layout-right{position:absolute!important;right:0;top:0}.layui-container{position:relative;margin:0 auto;padding:0 15px;box-sizing:border-box}.layui-fluid{position:relative;margin:0 auto;padding:0 15px}.layui-row:after,.layui-row:before{content:'';display:block;clear:both}.layui-col-lg1,.layui-col-lg10,.layui-col-lg11,.layui-col-lg12,.layui-col-lg2,.layui-col-lg3,.layui-col-lg4,.layui-col-lg5,.layui-col-lg6,.layui-col-lg7,.layui-col-lg8,.layui-col-lg9,.layui-col-md1,.layui-col-md10,.layui-col-md11,.layui-col-md12,.layui-col-md2,.layui-col-md3,.layui-col-md4,.layui-col-md5,.layui-col-md6,.layui-col-md7,.layui-col-md8,.layui-col-md9,.layui-col-sm1,.layui-col-sm10,.layui-col-sm11,.layui-col-sm12,.layui-col-sm2,.layui-col-sm3,.layui-col-sm4,.layui-col-sm5,.layui-col-sm6,.layui-col-sm7,.layui-col-sm8,.layui-col-sm9,.layui-col-xs1,.layui-col-xs10,.layui-col-xs11,.layui-col-xs12,.layui-col-xs2,.layui-col-xs3,.layui-col-xs4,.layui-col-xs5,.layui-col-xs6,.layui-col-xs7,.layui-col-xs8,.layui-col-xs9{position:relative;display:block;box-sizing:border-box}.layui-col-xs1,.layui-col-xs10,.layui-col-xs11,.layui-col-xs12,.layui-col-xs2,.layui-col-xs3,.layui-col-xs4,.layui-col-xs5,.layui-col-xs6,.layui-col-xs7,.layui-col-xs8,.layui-col-xs9{float:left}.layui-col-xs1{width:8.33333333%}.layui-col-xs2{width:16.66666667%}.layui-col-xs3{width:25%}.layui-col-xs4{width:33.33333333%}.layui-col-xs5{width:41.66666667%}.layui-col-xs6{width:50%}.layui-col-xs7{width:58.33333333%}.layui-col-xs8{width:66.66666667%}.layui-col-xs9{width:75%}.layui-col-xs10{width:83.33333333%}.layui-col-xs11{width:91.66666667%}.layui-col-xs12{width:100%}.layui-col-xs-offset1{margin-left:8.33333333%}.layui-col-xs-offset2{margin-left:16.66666667%}.layui-col-xs-offset3{margin-left:25%}.layui-col-xs-offset4{margin-left:33.33333333%}.layui-col-xs-offset5{margin-left:41.66666667%}.layui-col-xs-offset6{margin-left:50%}.layui-col-xs-offset7{margin-left:58.33333333%}.layui-col-xs-offset8{margin-left:66.66666667%}.layui-col-xs-offset9{margin-left:75%}.layui-col-xs-offset10{margin-left:83.33333333%}.layui-col-xs-offset11{margin-left:91.66666667%}.layui-col-xs-offset12{margin-left:100%}@media screen and (max-width:768px){.layui-hide-xs{display:none!important}.layui-show-xs-block{display:block!important}.layui-show-xs-inline{display:inline!important}.layui-show-xs-inline-block{display:inline-block!important}}@media screen and (min-width:768px){.layui-container{width:750px}.layui-hide-sm{display:none!important}.layui-show-sm-block{display:block!important}.layui-show-sm-inline{display:inline!important}.layui-show-sm-inline-block{display:inline-block!important}.layui-col-sm1,.layui-col-sm10,.layui-col-sm11,.layui-col-sm12,.layui-col-sm2,.layui-col-sm3,.layui-col-sm4,.layui-col-sm5,.layui-col-sm6,.layui-col-sm7,.layui-col-sm8,.layui-col-sm9{float:left}.layui-col-sm1{width:8.33333333%}.layui-col-sm2{width:16.66666667%}.layui-col-sm3{width:25%}.layui-col-sm4{width:33.33333333%}.layui-col-sm5{width:41.66666667%}.layui-col-sm6{width:50%}.layui-col-sm7{width:58.33333333%}.layui-col-sm8{width:66.66666667%}.layui-col-sm9{width:75%}.layui-col-sm10{width:83.33333333%}.layui-col-sm11{width:91.66666667%}.layui-col-sm12{width:100%}.layui-col-sm-offset1{margin-left:8.33333333%}.layui-col-sm-offset2{margin-left:16.66666667%}.layui-col-sm-offset3{margin-left:25%}.layui-col-sm-offset4{margin-left:33.33333333%}.layui-col-sm-offset5{margin-left:41.66666667%}.layui-col-sm-offset6{margin-left:50%}.layui-col-sm-offset7{margin-left:58.33333333%}.layui-col-sm-offset8{margin-left:66.66666667%}.layui-col-sm-offset9{margin-left:75%}.layui-col-sm-offset10{margin-left:83.33333333%}.layui-col-sm-offset11{margin-left:91.66666667%}.layui-col-sm-offset12{margin-left:100%}}@media screen and (min-width:992px){.layui-container{width:970px}.layui-hide-md{display:none!important}.layui-show-md-block{display:block!important}.layui-show-md-inline{display:inline!important}.layui-show-md-inline-block{display:inline-block!important}.layui-col-md1,.layui-col-md10,.layui-col-md11,.layui-col-md12,.layui-col-md2,.layui-col-md3,.layui-col-md4,.layui-col-md5,.layui-col-md6,.layui-col-md7,.layui-col-md8,.layui-col-md9{float:left}.layui-col-md1{width:8.33333333%}.layui-col-md2{width:16.66666667%}.layui-col-md3{width:25%}.layui-col-md4{width:33.33333333%}.layui-col-md5{width:41.66666667%}.layui-col-md6{width:50%}.layui-col-md7{width:58.33333333%}.layui-col-md8{width:66.66666667%}.layui-col-md9{width:75%}.layui-col-md10{width:83.33333333%}.layui-col-md11{width:91.66666667%}.layui-col-md12{width:100%}.layui-col-md-offset1{margin-left:8.33333333%}.layui-col-md-offset2{margin-left:16.66666667%}.layui-col-md-offset3{margin-left:25%}.layui-col-md-offset4{margin-left:33.33333333%}.layui-col-md-offset5{margin-left:41.66666667%}.layui-col-md-offset6{margin-left:50%}.layui-col-md-offset7{margin-left:58.33333333%}.layui-col-md-offset8{margin-left:66.66666667%}.layui-col-md-offset9{margin-left:75%}.layui-col-md-offset10{margin-left:83.33333333%}.layui-col-md-offset11{margin-left:91.66666667%}.layui-col-md-offset12{margin-left:100%}}@media screen and (min-width:1200px){.layui-container{width:1170px}.layui-hide-lg{display:none!important}.layui-show-lg-block{display:block!important}.layui-show-lg-inline{display:inline!important}.layui-show-lg-inline-block{display:inline-block!important}.layui-col-lg1,.layui-col-lg10,.layui-col-lg11,.layui-col-lg12,.layui-col-lg2,.layui-col-lg3,.layui-col-lg4,.layui-col-lg5,.layui-col-lg6,.layui-col-lg7,.layui-col-lg8,.layui-col-lg9{float:left}.layui-col-lg1{width:8.33333333%}.layui-col-lg2{width:16.66666667%}.layui-col-lg3{width:25%}.layui-col-lg4{width:33.33333333%}.layui-col-lg5{width:41.66666667%}.layui-col-lg6{width:50%}.layui-col-lg7{width:58.33333333%}.layui-col-lg8{width:66.66666667%}.layui-col-lg9{width:75%}.layui-col-lg10{width:83.33333333%}.layui-col-lg11{width:91.66666667%}.layui-col-lg12{width:100%}.layui-col-lg-offset1{margin-left:8.33333333%}.layui-col-lg-offset2{margin-left:16.66666667%}.layui-col-lg-offset3{margin-left:25%}.layui-col-lg-offset4{margin-left:33.33333333%}.layui-col-lg-offset5{margin-left:41.66666667%}.layui-col-lg-offset6{margin-left:50%}.layui-col-lg-offset7{margin-left:58.33333333%}.layui-col-lg-offset8{margin-left:66.66666667%}.layui-col-lg-offset9{margin-left:75%}.layui-col-lg-offset10{margin-left:83.33333333%}.layui-col-lg-offset11{margin-left:91.66666667%}.layui-col-lg-offset12{margin-left:100%}}.layui-col-space1{margin:-.5px}.layui-col-space1>*{padding:.5px}.layui-col-space3{margin:-1.5px}.layui-col-space3>*{padding:1.5px}.layui-col-space5{margin:-2.5px}.layui-col-space5>*{padding:2.5px}.layui-col-space8{margin:-3.5px}.layui-col-space8>*{padding:3.5px}.layui-col-space10{margin:-5px}.layui-col-space10>*{padding:5px}.layui-col-space12{margin:-6px}.layui-col-space12>*{padding:6px}.layui-col-space15{margin:-7.5px}.layui-col-space15>*{padding:7.5px}.layui-col-space18{margin:-9px}.layui-col-space18>*{padding:9px}.layui-col-space20{margin:-10px}.layui-col-space20>*{padding:10px}.layui-col-space22{margin:-11px}.layui-col-space22>*{padding:11px}.layui-col-space25{margin:-12.5px}.layui-col-space25>*{padding:12.5px}.layui-col-space30{margin:-15px}.layui-col-space30>*{padding:15px}.layui-btn,.layui-input,.layui-select,.layui-textarea,.layui-upload-button{outline:0;-webkit-appearance:none;transition:all .3s;-webkit-transition:all .3s;box-sizing:border-box}.layui-elem-quote{margin-bottom:10px;padding:15px;line-height:22px;border-left:5px solid #009688;border-radius:0 2px 2px 0;background-color:#f2f2f2}.layui-quote-nm{border-style:solid;border-width:1px 1px 1px 5px;background:0 0}.layui-elem-field{margin-bottom:10px;padding:0;border-width:1px;border-style:solid}.layui-elem-field legend{margin-left:20px;padding:0 10px;font-size:20px;font-weight:300}.layui-field-title{margin:10px 0 20px;border-width:1px 0 0}.layui-field-box{padding:10px 15px}.layui-field-title .layui-field-box{padding:10px 0}.layui-progress{position:relative;height:6px;border-radius:20px;background-color:#e2e2e2}.layui-progress-bar{position:absolute;left:0;top:0;width:0;max-width:100%;height:6px;border-radius:20px;text-align:right;background-color:#5FB878;transition:all .3s;-webkit-transition:all .3s}.layui-progress-big,.layui-progress-big .layui-progress-bar{height:18px;line-height:18px}.layui-progress-text{position:relative;top:-20px;line-height:18px;font-size:12px;color:#666}.layui-progress-big .layui-progress-text{position:static;padding:0 10px;color:#fff}.layui-collapse{border-width:1px;border-style:solid;border-radius:2px}.layui-colla-content,.layui-colla-item{border-top-width:1px;border-top-style:solid}.layui-colla-item:first-child{border-top:none}.layui-colla-title{position:relative;height:42px;line-height:42px;padding:0 15px 0 35px;color:#333;background-color:#f2f2f2;cursor:pointer;font-size:14px;overflow:hidden}.layui-colla-content{display:none;padding:10px 15px;line-height:22px;color:#666}.layui-colla-icon{position:absolute;left:15px;top:0;font-size:14px}.layui-card-body,.layui-card-header,.layui-form-label,.layui-form-mid,.layui-form-select,.layui-input-block,.layui-input-inline,.layui-textarea{position:relative}.layui-card{margin-bottom:15px;border-radius:2px;background-color:#fff;box-shadow:0 1px 2px 0 rgba(0,0,0,.05)}.layui-card:last-child{margin-bottom:0}.layui-card-header{height:42px;line-height:42px;padding:0 15px;border-bottom:1px solid #f6f6f6;color:#333;border-radius:2px 2px 0 0;font-size:14px}.layui-bg-black,.layui-bg-blue,.layui-bg-cyan,.layui-bg-green,.layui-bg-orange,.layui-bg-red{color:#fff!important}.layui-card-body{padding:10px 15px;line-height:24px}.layui-card-body[pad15]{padding:15px}.layui-card-body[pad20]{padding:20px}.layui-card-body .layui-table{margin:5px 0}.layui-card .layui-tab{margin:0}.layui-panel-window{position:relative;padding:15px;border-radius:0;border-top:5px solid #E6E6E6;background-color:#fff}.layui-bg-red{background-color:#FF5722!important}.layui-bg-orange{background-color:#FFB800!important}.layui-bg-green{background-color:#009688!important}.layui-bg-cyan{background-color:#2F4056!important}.layui-bg-blue{background-color:#1E9FFF!important}.layui-bg-black{background-color:#393D49!important}.layui-bg-gray{background-color:#eee!important;color:#666!important}.layui-badge-rim,.layui-colla-content,.layui-colla-item,.layui-collapse,.layui-elem-field,.layui-form-pane .layui-form-item[pane],.layui-form-pane .layui-form-label,.layui-input,.layui-layedit,.layui-layedit-tool,.layui-quote-nm,.layui-select,.layui-tab-bar,.layui-tab-card,.layui-tab-title,.layui-tab-title .layui-this:after,.layui-textarea{border-color:#e6e6e6}.layui-timeline-item:before,hr{background-color:#e6e6e6}.layui-text{line-height:22px;font-size:14px;color:#666}.layui-text h1,.layui-text h2,.layui-text h3{font-weight:500;color:#333}.layui-text h1{font-size:30px}.layui-text h2{font-size:24px}.layui-text h3{font-size:18px}.layui-text a:not(.layui-btn){color:#01AAED}.layui-text a:not(.layui-btn):hover{text-decoration:underline}.layui-text ul{padding:5px 0 5px 15px}.layui-text ul li{margin-top:5px;list-style-type:disc}.layui-text em,.layui-word-aux{color:#999!important;padding:0 5px!important}.layui-btn{display:inline-block;height:38px;line-height:38px;padding:0 18px;background-color:#009688;color:#fff;white-space:nowrap;text-align:center;font-size:14px;border:none;border-radius:2px;cursor:pointer}.layui-btn:hover{opacity:.8;filter:alpha(opacity=80);color:#fff}.layui-btn:active{opacity:1;filter:alpha(opacity=100)}.layui-btn+.layui-btn{margin-left:10px}.layui-btn-container{font-size:0}.layui-btn-container .layui-btn{margin-right:10px;margin-bottom:10px}.layui-btn-container .layui-btn+.layui-btn{margin-left:0}.layui-table .layui-btn-container .layui-btn{margin-bottom:9px}.layui-btn-radius{border-radius:100px}.layui-btn .layui-icon{margin-right:3px;font-size:18px;vertical-align:bottom;vertical-align:middle\9}.layui-btn-primary{border:1px solid #C9C9C9;background-color:#fff;color:#555}.layui-btn-primary:hover{border-color:#009688;color:#333}.layui-btn-normal{background-color:#1E9FFF}.layui-btn-warm{background-color:#FFB800}.layui-btn-danger{background-color:#FF5722}.layui-btn-disabled,.layui-btn-disabled:active,.layui-btn-disabled:hover{border:1px solid #e6e6e6;background-color:#FBFBFB;color:#C9C9C9;cursor:not-allowed;opacity:1}.layui-btn-lg{height:44px;line-height:44px;padding:0 25px;font-size:16px}.layui-btn-sm{height:30px;line-height:30px;padding:0 10px;font-size:12px}.layui-btn-sm i{font-size:16px!important}.layui-btn-xs{height:22px;line-height:22px;padding:0 5px;font-size:12px}.layui-btn-xs i{font-size:14px!important}.layui-btn-group{display:inline-block;vertical-align:middle;font-size:0}.layui-btn-group .layui-btn{margin-left:0!important;margin-right:0!important;border-left:1px solid rgba(255,255,255,.5);border-radius:0}.layui-btn-group .layui-btn-primary{border-left:none}.layui-btn-group .layui-btn-primary:hover{border-color:#C9C9C9;color:#009688}.layui-btn-group .layui-btn:first-child{border-left:none;border-radius:2px 0 0 2px}.layui-btn-group .layui-btn-primary:first-child{border-left:1px solid #c9c9c9}.layui-btn-group .layui-btn:last-child{border-radius:0 2px 2px 0}.layui-btn-group .layui-btn+.layui-btn{margin-left:0}.layui-btn-group+.layui-btn-group{margin-left:10px}.layui-btn-fluid{width:100%}.layui-input,.layui-select,.layui-textarea{height:38px;line-height:1.3;line-height:38px\9;border-width:1px;border-style:solid;background-color:#fff;border-radius:2px}.layui-input::-webkit-input-placeholder,.layui-select::-webkit-input-placeholder,.layui-textarea::-webkit-input-placeholder{line-height:1.3}.layui-input,.layui-textarea{display:block;width:100%;padding-left:10px}.layui-input:hover,.layui-textarea:hover{border-color:#D2D2D2!important}.layui-input:focus,.layui-textarea:focus{border-color:#C9C9C9!important}.layui-textarea{min-height:100px;height:auto;line-height:20px;padding:6px 10px;resize:vertical}.layui-select{padding:0 10px}.layui-form input[type=checkbox],.layui-form input[type=radio],.layui-form select{display:none}.layui-form [lay-ignore]{display:initial}.layui-form-item{margin-bottom:15px;clear:both;*zoom:1}.layui-form-item:after{content:'\20';clear:both;*zoom:1;display:block;height:0}.layui-form-label{float:left;display:block;padding:9px 15px;width:80px;font-weight:400;line-height:20px;text-align:right}.layui-form-label-col{display:block;float:none;padding:9px 0;line-height:20px;text-align:left}.layui-form-item .layui-inline{margin-bottom:5px;margin-right:10px}.layui-input-block{margin-left:110px;min-height:36px}.layui-input-inline{display:inline-block;vertical-align:middle}.layui-form-item .layui-input-inline{float:left;width:190px;margin-right:10px}.layui-form-text .layui-input-inline{width:auto}.layui-form-mid{float:left;display:block;padding:9px 0!important;line-height:20px;margin-right:10px}.layui-form-danger+.layui-form-select .layui-input,.layui-form-danger:focus{border-color:#FF5722!important}.layui-form-select .layui-input{padding-right:30px;cursor:pointer}.layui-form-select .layui-edge{position:absolute;right:10px;top:50%;margin-top:-3px;cursor:pointer;border-width:6px;border-top-color:#c2c2c2;border-top-style:solid;transition:all .3s;-webkit-transition:all .3s}.layui-form-select dl{display:none;position:absolute;left:0;top:42px;padding:5px 0;z-index:999;min-width:100%;border:1px solid #d2d2d2;max-height:300px;overflow-y:auto;background-color:#fff;border-radius:2px;box-shadow:0 2px 4px rgba(0,0,0,.12);box-sizing:border-box}.layui-form-select dl dd,.layui-form-select dl dt{padding:0 10px;line-height:36px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.layui-form-select dl dt{font-size:12px;color:#999}.layui-form-select dl dd{cursor:pointer}.layui-form-select dl dd:hover{background-color:#f2f2f2}.layui-form-select .layui-select-group dd{padding-left:20px}.layui-form-select dl dd.layui-select-tips{padding-left:10px!important;color:#999}.layui-form-select dl dd.layui-this{background-color:#5FB878;color:#fff}.layui-form-checkbox,.layui-form-select dl dd.layui-disabled{background-color:#fff}.layui-form-selected dl{display:block}.layui-form-checkbox,.layui-form-checkbox *,.layui-form-switch{display:inline-block;vertical-align:middle}.layui-form-selected .layui-edge{margin-top:-9px;-webkit-transform:rotate(180deg);transform:rotate(180deg);margin-top:-3px\9}:root .layui-form-selected .layui-edge{margin-top:-9px\0/IE9}.layui-form-selectup dl{top:auto;bottom:42px}.layui-select-none{margin:5px 0;text-align:center;color:#999}.layui-select-disabled .layui-disabled{border-color:#eee!important}.layui-select-disabled .layui-edge{border-top-color:#d2d2d2}.layui-form-checkbox{position:relative;height:30px;line-height:28px;margin-right:10px;padding-right:30px;border:1px solid #d2d2d2;cursor:pointer;font-size:0;border-radius:2px;-webkit-transition:.1s linear;transition:.1s linear;box-sizing:border-box}.layui-form-checkbox:hover{border:1px solid #c2c2c2}.layui-form-checkbox span{padding:0 10px;height:100%;font-size:14px;background-color:#d2d2d2;color:#fff;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.layui-form-checkbox:hover span{background-color:#c2c2c2}.layui-form-checkbox i{position:absolute;right:0;width:30px;color:#fff;font-size:20px;text-align:center}.layui-form-checkbox:hover i{color:#c2c2c2}.layui-form-checked,.layui-form-checked:hover{border-color:#5FB878}.layui-form-checked span,.layui-form-checked:hover span{background-color:#5FB878}.layui-form-checked i,.layui-form-checked:hover i{color:#5FB878}.layui-form-item .layui-form-checkbox{margin-top:4px}.layui-form-checkbox[lay-skin=primary]{height:auto!important;line-height:normal!important;border:none!important;margin-right:0;padding-right:0;background:0 0}.layui-form-checkbox[lay-skin=primary] span{float:right;padding-right:15px;line-height:18px;background:0 0;color:#666}.layui-form-checkbox[lay-skin=primary] i{position:relative;top:0;width:16px;height:16px;line-height:16px;border:1px solid #d2d2d2;font-size:12px;border-radius:2px;background-color:#fff;-webkit-transition:.1s linear;transition:.1s linear}.layui-form-checkbox[lay-skin=primary]:hover i{border-color:#5FB878;color:#fff}.layui-form-checked[lay-skin=primary] i{border-color:#5FB878;background-color:#5FB878;color:#fff}.layui-checkbox-disbaled[lay-skin=primary] span{background:0 0!important;color:#c2c2c2}.layui-checkbox-disbaled[lay-skin=primary]:hover i{border-color:#d2d2d2}.layui-form-item .layui-form-checkbox[lay-skin=primary]{margin-top:10px}.layui-form-switch{position:relative;height:22px;line-height:22px;width:42px;padding:0 5px;margin-top:8px;border:1px solid #d2d2d2;border-radius:20px;cursor:pointer;background-color:#fff;-webkit-transition:.1s linear;transition:.1s linear}.layui-form-switch i{position:absolute;left:5px;top:3px;width:16px;height:16px;border-radius:20px;background-color:#d2d2d2;-webkit-transition:.1s linear;transition:.1s linear}.layui-form-switch em{position:absolute;right:5px;top:0;width:25px;padding:0!important;text-align:center!important;color:#999!important;font-style:normal!important;font-size:12px}.layui-form-onswitch{border-color:#5FB878;background-color:#5FB878}.layui-form-onswitch i{left:32px;background-color:#fff}.layui-form-onswitch em{left:5px;right:auto;color:#fff!important}.layui-checkbox-disbaled{border-color:#e2e2e2!important}.layui-checkbox-disbaled span{background-color:#e2e2e2!important}.layui-checkbox-disbaled:hover i{color:#fff!important}[lay-radio]{display:none}.layui-form-radio,.layui-form-radio *{display:inline-block;vertical-align:middle}.layui-form-radio{line-height:28px;margin:6px 10px 0 0;padding-right:10px;cursor:pointer;font-size:0}.layui-form-radio *{font-size:14px}.layui-form-radio>i{margin-right:8px;font-size:22px;color:#c2c2c2}.layui-form-radio>i:hover,.layui-form-radioed>i{color:#5FB878}.layui-radio-disbaled>i{color:#e2e2e2!important}.layui-form-pane .layui-form-label{width:110px;padding:8px 15px;height:38px;line-height:20px;border-width:1px;border-style:solid;border-radius:2px 0 0 2px;text-align:center;background-color:#FBFBFB;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;box-sizing:border-box}.layui-form-pane .layui-input-inline{margin-left:-1px}.layui-form-pane .layui-input-block{margin-left:110px;left:-1px}.layui-form-pane .layui-input{border-radius:0 2px 2px 0}.layui-form-pane .layui-form-text .layui-form-label{float:none;width:100%;border-radius:2px;box-sizing:border-box;text-align:left}.layui-form-pane .layui-form-text .layui-input-inline{display:block;margin:0;top:-1px;clear:both}.layui-form-pane .layui-form-text .layui-input-block{margin:0;left:0;top:-1px}.layui-form-pane .layui-form-text .layui-textarea{min-height:100px;border-radius:0 0 2px 2px}.layui-form-pane .layui-form-checkbox{margin:4px 0 4px 10px}.layui-form-pane .layui-form-radio,.layui-form-pane .layui-form-switch{margin-top:6px;margin-left:10px}.layui-form-pane .layui-form-item[pane]{position:relative;border-width:1px;border-style:solid}.layui-form-pane .layui-form-item[pane] .layui-form-label{position:absolute;left:0;top:0;height:100%;border-width:0 1px 0 0}.layui-form-pane .layui-form-item[pane] .layui-input-inline{margin-left:110px}@media screen and (max-width:450px){.layui-form-item .layui-form-label{text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.layui-form-item .layui-inline{display:block;margin-right:0;margin-bottom:20px;clear:both}.layui-form-item .layui-inline:after{content:'\20';clear:both;display:block;height:0}.layui-form-item .layui-input-inline{display:block;float:none;left:-3px;width:auto;margin:0 0 10px 112px}.layui-form-item .layui-input-inline+.layui-form-mid{margin-left:110px;top:-5px;padding:0}.layui-form-item .layui-form-checkbox{margin-right:5px;margin-bottom:5px}}.layui-layedit{border-width:1px;border-style:solid;border-radius:2px}.layui-layedit-tool{padding:3px 5px;border-bottom-width:1px;border-bottom-style:solid;font-size:0}.layedit-tool-fixed{position:fixed;top:0;border-top:1px solid #e2e2e2}.layui-layedit-tool .layedit-tool-mid,.layui-layedit-tool .layui-icon{display:inline-block;vertical-align:middle;text-align:center;font-size:14px}.layui-layedit-tool .layui-icon{position:relative;width:32px;height:30px;line-height:30px;margin:3px 5px;color:#777;cursor:pointer;border-radius:2px}.layui-layedit-tool .layui-icon:hover{color:#393D49}.layui-layedit-tool .layui-icon:active{color:#000}.layui-layedit-tool .layedit-tool-active{background-color:#e2e2e2;color:#000}.layui-layedit-tool .layui-disabled,.layui-layedit-tool .layui-disabled:hover{color:#d2d2d2;cursor:not-allowed}.layui-layedit-tool .layedit-tool-mid{width:1px;height:18px;margin:0 10px;background-color:#d2d2d2}.layedit-tool-html{width:50px!important;font-size:30px!important}.layedit-tool-b,.layedit-tool-code,.layedit-tool-help{font-size:16px!important}.layedit-tool-d,.layedit-tool-face,.layedit-tool-image,.layedit-tool-unlink{font-size:18px!important}.layedit-tool-image input{position:absolute;font-size:0;left:0;top:0;width:100%;height:100%;opacity:.01;filter:Alpha(opacity=1);cursor:pointer}.layui-layedit-iframe iframe{display:block;width:100%}#LAY_layedit_code{overflow:hidden}.layui-laypage{display:inline-block;*display:inline;*zoom:1;vertical-align:middle;margin:10px 0;font-size:0}.layui-laypage>a:first-child,.layui-laypage>a:first-child em{border-radius:2px 0 0 2px}.layui-laypage>a:last-child,.layui-laypage>a:last-child em{border-radius:0 2px 2px 0}.layui-laypage>:first-child{margin-left:0!important}.layui-laypage>:last-child{margin-right:0!important}.layui-laypage a,.layui-laypage button,.layui-laypage input,.layui-laypage select,.layui-laypage span{border:1px solid #e2e2e2}.layui-laypage a,.layui-laypage span{display:inline-block;*display:inline;*zoom:1;vertical-align:middle;padding:0 15px;height:28px;line-height:28px;margin:0 -1px 5px 0;background-color:#fff;color:#333;font-size:12px}.layui-laypage a:hover{color:#009688}.layui-laypage em{font-style:normal}.layui-laypage .layui-laypage-spr{color:#999;font-weight:700}.layui-laypage a{text-decoration:none}.layui-laypage .layui-laypage-curr{position:relative}.layui-laypage .layui-laypage-curr em{position:relative;color:#fff}.layui-laypage .layui-laypage-curr .layui-laypage-em{position:absolute;left:-1px;top:-1px;padding:1px;width:100%;height:100%;background-color:#009688}.layui-laypage-em{border-radius:2px}.layui-laypage-next em,.layui-laypage-prev em{font-family:Sim sun;font-size:16px}.layui-laypage .layui-laypage-count,.layui-laypage .layui-laypage-limits,.layui-laypage .layui-laypage-skip{margin-left:10px;margin-right:10px;padding:0;border:none}.layui-laypage .layui-laypage-limits{vertical-align:top}.layui-laypage select{height:22px;padding:3px;border-radius:2px;cursor:pointer}.layui-laypage .layui-laypage-skip{height:30px;line-height:30px;color:#999}.layui-laypage button,.layui-laypage input{height:30px;line-height:30px;border-radius:2px;vertical-align:top;background-color:#fff;box-sizing:border-box}.layui-laypage input{display:inline-block;width:40px;margin:0 10px;padding:0 3px;text-align:center}.layui-laypage input:focus,.layui-laypage select:focus{border-color:#009688!important}.layui-laypage button{margin-left:10px;padding:0 10px;cursor:pointer}.layui-table,.layui-table-view{margin:10px 0}.layui-flow-more{margin:10px 0;text-align:center;color:#999;font-size:14px}.layui-flow-more a{height:32px;line-height:32px}.layui-flow-more a *{display:inline-block;vertical-align:top}.layui-flow-more a cite{padding:0 20px;border-radius:3px;background-color:#eee;color:#333;font-style:normal}.layui-flow-more a cite:hover{opacity:.8}.layui-flow-more a i{font-size:30px;color:#737383}.layui-table{width:100%;background-color:#fff;color:#666}.layui-table tr{transition:all .3s;-webkit-transition:all .3s}.layui-table th{text-align:left;font-weight:400}.layui-table tbody tr:hover,.layui-table thead tr,.layui-table-click,.layui-table-header,.layui-table-hover,.layui-table-mend,.layui-table-patch,.layui-table-tool,.layui-table[lay-even] tr:nth-child(even){background-color:#f2f2f2}.layui-table td,.layui-table th,.layui-table-fixed-r,.layui-table-header,.layui-table-page,.layui-table-tips-main,.layui-table-tool,.layui-table-view,.layui-table[lay-skin=line],.layui-table[lay-skin=row]{border-width:1px;border-style:solid;border-color:#e6e6e6}.layui-table td,.layui-table th{position:relative;padding:9px 15px;min-height:20px;line-height:20px;font-size:14px}.layui-table[lay-skin=line] td,.layui-table[lay-skin=line] th{border-width:0 0 1px}.layui-table[lay-skin=row] td,.layui-table[lay-skin=row] th{border-width:0 1px 0 0}.layui-table[lay-skin=nob] td,.layui-table[lay-skin=nob] th{border:none}.layui-table img{max-width:100px}.layui-table[lay-size=lg] td,.layui-table[lay-size=lg] th{padding:15px 30px}.layui-table-view .layui-table[lay-size=lg] .layui-table-cell{height:40px;line-height:40px}.layui-table[lay-size=sm] td,.layui-table[lay-size=sm] th{font-size:12px;padding:5px 10px}.layui-table-view .layui-table[lay-size=sm] .layui-table-cell{height:20px;line-height:20px}.layui-table[lay-data]{display:none}.layui-table-box,.layui-table-view{position:relative;overflow:hidden}.layui-table-view .layui-table{position:relative;width:auto;margin:0}.layui-table-body,.layui-table-header .layui-table,.layui-table-page{margin-bottom:-1px}.layui-table-view .layui-table[lay-skin=line]{border-width:0 1px 0 0}.layui-table-view .layui-table[lay-skin=row]{border-width:0 0 1px}.layui-table-view .layui-table td,.layui-table-view .layui-table th{padding:5px 0;border-top:none;border-left:none}.layui-table-view .layui-table td{cursor:default}.layui-table-view .layui-form-checkbox[lay-skin=primary] i{width:18px;height:18px}.layui-table-header{border-width:0 0 1px;overflow:hidden}.layui-table-sort{width:10px;height:20px;margin-left:5px;cursor:pointer!important}.layui-table-sort .layui-edge{position:absolute;left:5px;border-width:5px}.layui-table-sort .layui-table-sort-asc{top:4px;border-top:none;border-bottom-style:solid;border-bottom-color:#b2b2b2}.layui-table-sort .layui-table-sort-asc:hover{border-bottom-color:#666}.layui-table-sort .layui-table-sort-desc{bottom:4px;border-bottom:none;border-top-style:solid;border-top-color:#b2b2b2}.layui-table-sort .layui-table-sort-desc:hover{border-top-color:#666}.layui-table-sort[lay-sort=asc] .layui-table-sort-asc{border-bottom-color:#000}.layui-table-sort[lay-sort=desc] .layui-table-sort-desc{border-top-color:#000}.layui-table-cell{height:28px;line-height:28px;padding:0 15px;position:relative;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;box-sizing:border-box}.layui-table-cell .layui-form-checkbox[lay-skin=primary]{top:-1px;vertical-align:middle}.layui-table-cell .layui-table-link{color:#01AAED}.laytable-cell-checkbox,.laytable-cell-numbers,.laytable-cell-space{padding:0;text-align:center}.layui-table-body{position:relative;overflow:auto;margin-right:-1px}.layui-table-body .layui-none{line-height:40px;text-align:center;color:#999}.layui-table-fixed{position:absolute;left:0;top:0}.layui-table-fixed .layui-table-body{overflow:hidden}.layui-table-fixed-l{box-shadow:0 -1px 8px rgba(0,0,0,.08)}.layui-table-fixed-r{left:auto;right:-1px;border-width:0 0 0 1px;box-shadow:-1px 0 8px rgba(0,0,0,.08)}.layui-table-fixed-r .layui-table-header{position:relative;overflow:visible}.layui-table-mend{position:absolute;right:-49px;top:0;height:100%;width:50px}.layui-table-tool{position:relative;width:100%;height:50px;line-height:30px;padding:10px 15px;border-width:0 0 1px}.layui-table-page{position:relative;width:100%;padding:7px 7px 0;border-width:1px 0 0;height:41px;font-size:12px}.layui-table-page>div{height:26px}.layui-table-page .layui-laypage{margin:0}.layui-table-page .layui-laypage a,.layui-table-page .layui-laypage span{height:26px;line-height:26px;margin-bottom:10px;border:none;background:0 0}.layui-table-page .layui-laypage a,.layui-table-page .layui-laypage span.layui-laypage-curr{padding:0 12px}.layui-table-page .layui-laypage span{margin-left:0;padding:0}.layui-table-page .layui-laypage .layui-laypage-prev{margin-left:-7px!important}.layui-table-page .layui-laypage .layui-laypage-curr .layui-laypage-em{left:0;top:0;padding:0}.layui-table-page .layui-laypage button,.layui-table-page .layui-laypage input{height:26px;line-height:26px}.layui-table-page .layui-laypage input{width:40px}.layui-table-page .layui-laypage button{padding:0 10px}.layui-table-page select{height:18px}.layui-table-view select[lay-ignore]{display:inline-block}.layui-table-patch .layui-table-cell{padding:0;width:30px}.layui-table-edit{position:absolute;left:0;top:0;width:100%;height:100%;padding:0 14px 1px;border-radius:0;box-shadow:1px 1px 20px rgba(0,0,0,.15)}.layui-table-edit:focus{border-color:#5FB878!important}select.layui-table-edit{padding:0 0 0 10px;border-color:#C9C9C9}.layui-table-view .layui-form-checkbox,.layui-table-view .layui-form-radio,.layui-table-view .layui-form-switch{top:0;margin:0;box-sizing:content-box}.layui-table-view .layui-form-checkbox{top:-1px;height:26px;line-height:26px}body .layui-table-tips .layui-layer-content{background:0 0;padding:0;box-shadow:0 1px 6px rgba(0,0,0,.1)}.layui-table-tips-main{margin:-44px 0 0 -1px;max-height:150px;padding:8px 15px;font-size:14px;overflow-y:scroll;background-color:#fff;color:#333}.layui-code,.layui-upload-list{margin:10px 0}.layui-table-tips-c{position:absolute;right:-3px;top:-12px;width:18px;height:18px;padding:3px;text-align:center;font-weight:700;border-radius:100%;font-size:14px;cursor:pointer;background-color:#666}.layui-table-tips-c:hover{background-color:#999}.layui-upload-file{display:none!important;opacity:.01;filter:Alpha(opacity=1)}.layui-upload-drag,.layui-upload-form,.layui-upload-wrap{display:inline-block}.layui-upload-choose{padding:0 10px;color:#999}.layui-upload-drag{position:relative;padding:30px;border:1px dashed #e2e2e2;background-color:#fff;text-align:center;cursor:pointer;color:#999}.layui-upload-drag .layui-icon{font-size:50px;color:#009688}.layui-upload-drag[lay-over]{border-color:#009688}.layui-upload-iframe{position:absolute;width:0;height:0;border:0;visibility:hidden}.layui-upload-wrap{position:relative;vertical-align:middle}.layui-upload-wrap .layui-upload-file{display:block!important;position:absolute;left:0;top:0;z-index:10;font-size:100px;width:100%;height:100%;opacity:.01;filter:Alpha(opacity=1);cursor:pointer}.layui-code{position:relative;padding:15px;line-height:20px;border:1px solid #ddd;border-left-width:6px;background-color:#F2F2F2;color:#333;font-family:Courier New;font-size:12px}.layui-tree{line-height:26px}.layui-tree li{text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.layui-tree li .layui-tree-spread,.layui-tree li a{display:inline-block;vertical-align:top;height:26px;*display:inline;*zoom:1;cursor:pointer}.layui-tree li a{font-size:0}.layui-tree li a i{font-size:16px}.layui-tree li a cite{padding:0 6px;font-size:14px;font-style:normal}.layui-tree li i{padding-left:6px;color:#333;-moz-user-select:none}.layui-tree li .layui-tree-check{font-size:13px}.layui-tree li .layui-tree-check:hover{color:#009E94}.layui-tree li ul{display:none;margin-left:20px}.layui-tree li .layui-tree-enter{line-height:24px;border:1px dotted #000}.layui-tree-drag{display:none;position:absolute;left:-666px;top:-666px;background-color:#f2f2f2;padding:5px 10px;border:1px dotted #000;white-space:nowrap}.layui-tree-drag i{padding-right:5px}.layui-nav{position:relative;padding:0 20px;background-color:#393D49;color:#fff;border-radius:2px;font-size:0;box-sizing:border-box}.layui-nav *{font-size:14px}.layui-nav .layui-nav-item{position:relative;display:inline-block;*display:inline;*zoom:1;vertical-align:middle;line-height:60px}.layui-nav .layui-nav-item a{display:block;padding:0 20px;color:#fff;color:rgba(255,255,255,.7);transition:all .3s;-webkit-transition:all .3s}.layui-nav .layui-this:after,.layui-nav-bar,.layui-nav-tree .layui-nav-itemed:after{position:absolute;left:0;top:0;width:0;height:5px;background-color:#5FB878;transition:all .2s;-webkit-transition:all .2s}.layui-nav-bar{z-index:1000}.layui-nav .layui-nav-item a:hover,.layui-nav .layui-this a{color:#fff}.layui-nav .layui-this:after{content:'';top:auto;bottom:0;width:100%}.layui-nav-img{width:30px;height:30px;margin-right:10px;border-radius:50%}.layui-nav .layui-nav-more{content:'';width:0;height:0;border-style:solid dashed dashed;border-color:#fff transparent transparent;overflow:hidden;cursor:pointer;transition:all .2s;-webkit-transition:all .2s;position:absolute;top:50%;right:3px;margin-top:-3px;border-width:6px;border-top-color:rgba(255,255,255,.7)}.layui-nav .layui-nav-mored,.layui-nav-itemed>a .layui-nav-more{margin-top:-9px;border-style:dashed dashed solid;border-color:transparent transparent #fff}.layui-nav-child{display:none;position:absolute;left:0;top:65px;min-width:100%;line-height:36px;padding:5px 0;box-shadow:0 2px 4px rgba(0,0,0,.12);border:1px solid #d2d2d2;background-color:#fff;z-index:100;border-radius:2px;white-space:nowrap}.layui-nav .layui-nav-child a{color:#333}.layui-nav .layui-nav-child a:hover{background-color:#f2f2f2;color:#000}.layui-nav-child dd{position:relative}.layui-nav .layui-nav-child dd.layui-this a,.layui-nav-child dd.layui-this{background-color:#5FB878;color:#fff}.layui-nav-child dd.layui-this:after{display:none}.layui-nav-tree{width:200px;padding:0}.layui-nav-tree .layui-nav-item{display:block;width:100%;line-height:45px}.layui-nav-tree .layui-nav-item a{position:relative;height:45px;line-height:45px;text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.layui-nav-tree .layui-nav-item a:hover{background-color:#4E5465}.layui-nav-tree .layui-nav-bar{width:5px;height:0;background-color:#009688}.layui-nav-tree .layui-nav-child dd.layui-this,.layui-nav-tree .layui-nav-child dd.layui-this a,.layui-nav-tree .layui-this,.layui-nav-tree .layui-this>a,.layui-nav-tree .layui-this>a:hover{background-color:#009688;color:#fff}.layui-nav-tree .layui-this:after{display:none}.layui-nav-itemed>a,.layui-nav-tree .layui-nav-title a,.layui-nav-tree .layui-nav-title a:hover{color:#fff!important}.layui-nav-tree .layui-nav-child{position:relative;z-index:0;top:0;border:none;box-shadow:none}.layui-nav-tree .layui-nav-child a{height:40px;line-height:40px;color:#fff;color:rgba(255,255,255,.7)}.layui-nav-tree .layui-nav-child,.layui-nav-tree .layui-nav-child a:hover{background:0 0;color:#fff}.layui-nav-tree .layui-nav-more{right:10px}.layui-nav-itemed>.layui-nav-child{display:block;padding:0;background-color:rgba(0,0,0,.3)!important}.layui-nav-itemed>.layui-nav-child>.layui-this>.layui-nav-child{display:block}.layui-nav-side{position:fixed;top:0;bottom:0;left:0;overflow-x:hidden;z-index:999}.layui-bg-blue .layui-nav-bar,.layui-bg-blue .layui-nav-itemed:after,.layui-bg-blue .layui-this:after{background-color:#93D1FF}.layui-bg-blue .layui-nav-child dd.layui-this{background-color:#1E9FFF}.layui-bg-blue .layui-nav-itemed>a,.layui-nav-tree.layui-bg-blue .layui-nav-title a,.layui-nav-tree.layui-bg-blue .layui-nav-title a:hover{background-color:#007DDB!important}.layui-breadcrumb{visibility:hidden;font-size:0}.layui-breadcrumb>*{font-size:14px}.layui-breadcrumb a{color:#999!important}.layui-breadcrumb a:hover{color:#5FB878!important}.layui-breadcrumb a cite{color:#666;font-style:normal}.layui-breadcrumb span[lay-separator]{margin:0 10px;color:#999}.layui-tab{margin:10px 0;text-align:left!important}.layui-tab[overflow]>.layui-tab-title{overflow:hidden}.layui-tab-title{position:relative;left:0;height:40px;white-space:nowrap;font-size:0;border-bottom-width:1px;border-bottom-style:solid;transition:all .2s;-webkit-transition:all .2s}.layui-tab-title li{display:inline-block;*display:inline;*zoom:1;vertical-align:middle;font-size:14px;transition:all .2s;-webkit-transition:all .2s;position:relative;line-height:40px;min-width:65px;padding:0 15px;text-align:center;cursor:pointer}.layui-tab-title li a{display:block}.layui-tab-title .layui-this{color:#000}.layui-tab-title .layui-this:after{position:absolute;left:0;top:0;content:'';width:100%;height:41px;border-width:1px;border-style:solid;border-bottom-color:#fff;border-radius:2px 2px 0 0;box-sizing:border-box;pointer-events:none}.layui-tab-bar{position:absolute;right:0;top:0;z-index:10;width:30px;height:39px;line-height:39px;border-width:1px;border-style:solid;border-radius:2px;text-align:center;background-color:#fff;cursor:pointer}.layui-tab-bar .layui-icon{position:relative;display:inline-block;top:3px;transition:all .3s;-webkit-transition:all .3s}.layui-tab-item{display:none}.layui-tab-more{padding-right:30px;height:auto!important;white-space:normal!important}.layui-tab-more li.layui-this:after{border-bottom-color:#e2e2e2;border-radius:2px}.layui-tab-more .layui-tab-bar .layui-icon{top:-2px;top:3px\9;-webkit-transform:rotate(180deg);transform:rotate(180deg)}:root .layui-tab-more .layui-tab-bar .layui-icon{top:-2px\0/IE9}.layui-tab-content{padding:10px}.layui-tab-title li .layui-tab-close{position:relative;display:inline-block;width:18px;height:18px;line-height:20px;margin-left:8px;top:1px;text-align:center;font-size:14px;color:#c2c2c2;transition:all .2s;-webkit-transition:all .2s}.layui-tab-title li .layui-tab-close:hover{border-radius:2px;background-color:#FF5722;color:#fff}.layui-tab-brief>.layui-tab-title .layui-this{color:#009688}.layui-tab-brief>.layui-tab-more li.layui-this:after,.layui-tab-brief>.layui-tab-title .layui-this:after{border:none;border-radius:0;border-bottom:2px solid #5FB878}.layui-tab-brief[overflow]>.layui-tab-title .layui-this:after{top:-1px}.layui-tab-card{border-width:1px;border-style:solid;border-radius:2px;box-shadow:0 2px 5px 0 rgba(0,0,0,.1)}.layui-tab-card>.layui-tab-title{background-color:#f2f2f2}.layui-tab-card>.layui-tab-title li{margin-right:-1px;margin-left:-1px}.layui-tab-card>.layui-tab-title .layui-this{background-color:#fff}.layui-tab-card>.layui-tab-title .layui-this:after{border-top:none;border-width:1px;border-bottom-color:#fff}.layui-tab-card>.layui-tab-title .layui-tab-bar{height:40px;line-height:40px;border-radius:0;border-top:none;border-right:none}.layui-tab-card>.layui-tab-more .layui-this{background:0 0;color:#5FB878}.layui-tab-card>.layui-tab-more .layui-this:after{border:none}.layui-timeline{padding-left:5px}.layui-timeline-item{position:relative;padding-bottom:20px}.layui-timeline-axis{position:absolute;left:-5px;top:0;z-index:10;width:20px;height:20px;line-height:20px;background-color:#fff;color:#5FB878;border-radius:50%;text-align:center;cursor:pointer}.layui-timeline-axis:hover{color:#FF5722}.layui-timeline-item:before{content:'';position:absolute;left:5px;top:0;z-index:0;width:1px;height:100%}.layui-timeline-item:last-child:before{display:none}.layui-timeline-item:first-child:before{display:block}.layui-timeline-content{padding-left:25px}.layui-timeline-title{position:relative;margin-bottom:10px}.layui-badge,.layui-badge-dot,.layui-badge-rim{position:relative;display:inline-block;padding:0 6px;font-size:12px;text-align:center;background-color:#FF5722;color:#fff;border-radius:2px}.layui-badge{height:18px;line-height:18px}.layui-badge-dot{width:8px;height:8px;padding:0;border-radius:50%}.layui-badge-rim{height:18px;line-height:18px;border-width:1px;border-style:solid;background-color:#fff;color:#666}.layui-btn .layui-badge,.layui-btn .layui-badge-dot{margin-left:5px}.layui-nav .layui-badge,.layui-nav .layui-badge-dot{position:absolute;top:50%;margin:-8px 6px 0}.layui-tab-title .layui-badge,.layui-tab-title .layui-badge-dot{left:5px;top:-2px}.layui-carousel{position:relative;left:0;top:0;background-color:#f8f8f8}.layui-carousel>[carousel-item]{position:relative;width:100%;height:100%;overflow:hidden}.layui-carousel>[carousel-item]:before{position:absolute;content:'\e63d';left:50%;top:50%;width:100px;line-height:20px;margin:-10px 0 0 -50px;text-align:center;color:#c2c2c2;font-family:layui-icon!important;font-size:30px;font-style:normal;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.layui-carousel>[carousel-item]>*{display:none;position:absolute;left:0;top:0;width:100%;height:100%;background-color:#f8f8f8;transition-duration:.3s;-webkit-transition-duration:.3s}.layui-carousel-updown>*{-webkit-transition:.3s ease-in-out up;transition:.3s ease-in-out up}.layui-carousel-arrow{display:none\9;opacity:0;position:absolute;left:10px;top:50%;margin-top:-18px;width:36px;height:36px;line-height:36px;text-align:center;font-size:20px;border:0;border-radius:50%;background-color:rgba(0,0,0,.2);color:#fff;-webkit-transition-duration:.3s;transition-duration:.3s;cursor:pointer}.layui-carousel-arrow[lay-type=add]{left:auto!important;right:10px}.layui-carousel:hover .layui-carousel-arrow[lay-type=add],.layui-carousel[lay-arrow=always] .layui-carousel-arrow[lay-type=add]{right:20px}.layui-carousel[lay-arrow=always] .layui-carousel-arrow{opacity:1;left:20px}.layui-carousel[lay-arrow=none] .layui-carousel-arrow{display:none}.layui-carousel-arrow:hover,.layui-carousel-ind ul:hover{background-color:rgba(0,0,0,.35)}.layui-carousel:hover .layui-carousel-arrow{display:block\9;opacity:1;left:20px}.layui-carousel-ind{position:relative;top:-35px;width:100%;line-height:0!important;text-align:center;font-size:0}.layui-carousel[lay-indicator=outside]{margin-bottom:30px}.layui-carousel[lay-indicator=outside] .layui-carousel-ind{top:10px}.layui-carousel[lay-indicator=outside] .layui-carousel-ind ul{background-color:rgba(0,0,0,.5)}.layui-carousel[lay-indicator=none] .layui-carousel-ind{display:none}.layui-carousel-ind ul{display:inline-block;padding:5px;background-color:rgba(0,0,0,.2);border-radius:10px;-webkit-transition-duration:.3s;transition-duration:.3s}.layui-carousel-ind li{display:inline-block;width:10px;height:10px;margin:0 3px;font-size:14px;background-color:#e2e2e2;background-color:rgba(255,255,255,.5);border-radius:50%;cursor:pointer;-webkit-transition-duration:.3s;transition-duration:.3s}.layui-carousel-ind li:hover{background-color:rgba(255,255,255,.7)}.layui-carousel-ind li.layui-this{background-color:#fff}.layui-carousel>[carousel-item]>.layui-carousel-next,.layui-carousel>[carousel-item]>.layui-carousel-prev,.layui-carousel>[carousel-item]>.layui-this{display:block}.layui-carousel>[carousel-item]>.layui-this{left:0}.layui-carousel>[carousel-item]>.layui-carousel-prev{left:-100%}.layui-carousel>[carousel-item]>.layui-carousel-next{left:100%}.layui-carousel>[carousel-item]>.layui-carousel-next.layui-carousel-left,.layui-carousel>[carousel-item]>.layui-carousel-prev.layui-carousel-right{left:0}.layui-carousel>[carousel-item]>.layui-this.layui-carousel-left{left:-100%}.layui-carousel>[carousel-item]>.layui-this.layui-carousel-right{left:100%}.layui-carousel[lay-anim=updown] .layui-carousel-arrow{left:50%!important;top:20px;margin:0 0 0 -18px}.layui-carousel[lay-anim=updown]>[carousel-item]>*,.layui-carousel[lay-anim=fade]>[carousel-item]>*{left:0!important}.layui-carousel[lay-anim=updown] .layui-carousel-arrow[lay-type=add]{top:auto!important;bottom:20px}.layui-carousel[lay-anim=updown] .layui-carousel-ind{position:absolute;top:50%;right:20px;width:auto;height:auto}.layui-carousel[lay-anim=updown] .layui-carousel-ind ul{padding:3px 5px}.layui-carousel[lay-anim=updown] .layui-carousel-ind li{display:block;margin:6px 0}.layui-carousel[lay-anim=updown]>[carousel-item]>.layui-this{top:0}.layui-carousel[lay-anim=updown]>[carousel-item]>.layui-carousel-prev{top:-100%}.layui-carousel[lay-anim=updown]>[carousel-item]>.layui-carousel-next{top:100%}.layui-carousel[lay-anim=updown]>[carousel-item]>.layui-carousel-next.layui-carousel-left,.layui-carousel[lay-anim=updown]>[carousel-item]>.layui-carousel-prev.layui-carousel-right{top:0}.layui-carousel[lay-anim=updown]>[carousel-item]>.layui-this.layui-carousel-left{top:-100%}.layui-carousel[lay-anim=updown]>[carousel-item]>.layui-this.layui-carousel-right{top:100%}.layui-carousel[lay-anim=fade]>[carousel-item]>.layui-carousel-next,.layui-carousel[lay-anim=fade]>[carousel-item]>.layui-carousel-prev{opacity:0}.layui-carousel[lay-anim=fade]>[carousel-item]>.layui-carousel-next.layui-carousel-left,.layui-carousel[lay-anim=fade]>[carousel-item]>.layui-carousel-prev.layui-carousel-right{opacity:1}.layui-carousel[lay-anim=fade]>[carousel-item]>.layui-this.layui-carousel-left,.layui-carousel[lay-anim=fade]>[carousel-item]>.layui-this.layui-carousel-right{opacity:0}.layui-fixbar{position:fixed;right:15px;bottom:15px;z-index:9999}.layui-fixbar li{width:50px;height:50px;line-height:50px;margin-bottom:1px;text-align:center;cursor:pointer;font-size:30px;background-color:#9F9F9F;color:#fff;border-radius:2px;opacity:.95}.layui-fixbar li:hover{opacity:.85}.layui-fixbar li:active{opacity:1}.layui-fixbar .layui-fixbar-top{display:none;font-size:40px}body .layui-util-face{border:none;background:0 0}body .layui-util-face .layui-layer-content{padding:0;background-color:#fff;color:#666;box-shadow:none}.layui-util-face .layui-layer-TipsG{display:none}.layui-util-face ul{position:relative;width:372px;padding:10px;border:1px solid #D9D9D9;background-color:#fff;box-shadow:0 0 20px rgba(0,0,0,.2)}.layui-util-face ul li{cursor:pointer;float:left;border:1px solid #e8e8e8;height:22px;width:26px;overflow:hidden;margin:-1px 0 0 -1px;padding:4px 2px;text-align:center}.layui-util-face ul li:hover{position:relative;z-index:2;border:1px solid #eb7350;background:#fff9ec}.layui-anim{-webkit-animation-duration:.3s;animation-duration:.3s;-webkit-animation-fill-mode:both;animation-fill-mode:both}.layui-anim.layui-icon{display:inline-block}.layui-anim-loop{-webkit-animation-iteration-count:infinite;animation-iteration-count:infinite}.layui-trans,.layui-trans a{transition:all .3s;-webkit-transition:all .3s}@-webkit-keyframes layui-rotate{from{-webkit-transform:rotate(0)}to{-webkit-transform:rotate(360deg)}}@keyframes layui-rotate{from{transform:rotate(0)}to{transform:rotate(360deg)}}.layui-anim-rotate{-webkit-animation-name:layui-rotate;animation-name:layui-rotate;-webkit-animation-duration:1s;animation-duration:1s;-webkit-animation-timing-function:linear;animation-timing-function:linear}@-webkit-keyframes layui-up{from{-webkit-transform:translate3d(0,100%,0);opacity:.3}to{-webkit-transform:translate3d(0,0,0);opacity:1}}@keyframes layui-up{from{transform:translate3d(0,100%,0);opacity:.3}to{transform:translate3d(0,0,0);opacity:1}}.layui-anim-up{-webkit-animation-name:layui-up;animation-name:layui-up}@-webkit-keyframes layui-upbit{from{-webkit-transform:translate3d(0,30px,0);opacity:.3}to{-webkit-transform:translate3d(0,0,0);opacity:1}}@keyframes layui-upbit{from{transform:translate3d(0,30px,0);opacity:.3}to{transform:translate3d(0,0,0);opacity:1}}.layui-anim-upbit{-webkit-animation-name:layui-upbit;animation-name:layui-upbit}@-webkit-keyframes layui-scale{0%{opacity:.3;-webkit-transform:scale(.5)}100%{opacity:1;-webkit-transform:scale(1)}}@keyframes layui-scale{0%{opacity:.3;-ms-transform:scale(.5);transform:scale(.5)}100%{opacity:1;-ms-transform:scale(1);transform:scale(1)}}.layui-anim-scale{-webkit-animation-name:layui-scale;animation-name:layui-scale}@-webkit-keyframes layui-scale-spring{0%{opacity:.5;-webkit-transform:scale(.5)}80%{opacity:.8;-webkit-transform:scale(1.1)}100%{opacity:1;-webkit-transform:scale(1)}}@keyframes layui-scale-spring{0%{opacity:.5;transform:scale(.5)}80%{opacity:.8;transform:scale(1.1)}100%{opacity:1;transform:scale(1)}}.layui-anim-scaleSpring{-webkit-animation-name:layui-scale-spring;animation-name:layui-scale-spring}@-webkit-keyframes layui-fadein{0%{opacity:0}100%{opacity:1}}@keyframes layui-fadein{0%{opacity:0}100%{opacity:1}}.layui-anim-fadein{-webkit-animation-name:layui-fadein;animation-name:layui-fadein}@-webkit-keyframes layui-fadeout{0%{opacity:1}100%{opacity:0}}@keyframes layui-fadeout{0%{opacity:1}100%{opacity:0}}.layui-anim-fadeout{-webkit-animation-name:layui-fadeout;animation-name:layui-fadeout} \ No newline at end of file diff --git a/main/webapp/static/lib/layui/css/layui.mobile.css b/main/webapp/static/lib/layui/css/layui.mobile.css new file mode 100644 index 0000000..73f053f --- /dev/null +++ b/main/webapp/static/lib/layui/css/layui.mobile.css @@ -0,0 +1,2 @@ +/** layui-v2.2.6 MIT License By https://www.layui.com */ + blockquote,body,button,dd,div,dl,dt,form,h1,h2,h3,h4,h5,h6,input,legend,li,ol,p,td,textarea,th,ul{margin:0;padding:0;-webkit-tap-highlight-color:rgba(0,0,0,0)}html{font:12px 'Helvetica Neue','PingFang SC',STHeitiSC-Light,Helvetica,Arial,sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}a,button,input{-webkit-tap-highlight-color:rgba(255,0,0,0)}a{text-decoration:none;background:0 0}a:active,a:hover{outline:0}table{border-collapse:collapse;border-spacing:0}li{list-style:none}b,strong{font-weight:700}h1,h2,h3,h4,h5,h6{font-weight:500}address,cite,dfn,em,var{font-style:normal}dfn{font-style:italic}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}img{border:0;vertical-align:bottom}.layui-inline,input,label{vertical-align:middle}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0;outline:0}button,select{text-transform:none}select{-webkit-appearance:none;border:none}input{line-height:normal}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}@font-face{font-family:layui-icon;src:url(../font/iconfont.eot?v=1.0.7);src:url(../font/iconfont.eot?v=1.0.7#iefix) format('embedded-opentype'),url(../font/iconfont.woff?v=1.0.7) format('woff'),url(../font/iconfont.ttf?v=1.0.7) format('truetype'),url(../font/iconfont.svg?v=1.0.7#iconfont) format('svg')}.layui-icon{font-family:layui-icon!important;font-size:16px;font-style:normal;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.layui-box,.layui-box *{-webkit-box-sizing:content-box!important;-moz-box-sizing:content-box!important;box-sizing:content-box!important}.layui-border-box,.layui-border-box *{-webkit-box-sizing:border-box!important;-moz-box-sizing:border-box!important;box-sizing:border-box!important}.layui-inline{position:relative;display:inline-block;*display:inline;*zoom:1}.layui-edge,.layui-upload-iframe{position:absolute;width:0;height:0}.layui-edge{border-style:dashed;border-color:transparent;overflow:hidden}.layui-elip{text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.layui-unselect{-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none}.layui-disabled,.layui-disabled:active{background-color:#d2d2d2!important;color:#fff!important;cursor:not-allowed!important}.layui-circle{border-radius:100%}.layui-show{display:block!important}.layui-hide{display:none!important}.layui-upload-iframe{border:0;visibility:hidden}.layui-upload-enter{border:1px solid #009E94;background-color:#009E94;color:#fff;-webkit-transform:scale(1.1);transform:scale(1.1)}@-webkit-keyframes layui-m-anim-scale{0%{opacity:0;-webkit-transform:scale(.5);transform:scale(.5)}100%{opacity:1;-webkit-transform:scale(1);transform:scale(1)}}@keyframes layui-m-anim-scale{0%{opacity:0;-webkit-transform:scale(.5);transform:scale(.5)}100%{opacity:1;-webkit-transform:scale(1);transform:scale(1)}}.layui-m-anim-scale{animation-name:layui-m-anim-scale;-webkit-animation-name:layui-m-anim-scale}@-webkit-keyframes layui-m-anim-up{0%{opacity:0;-webkit-transform:translateY(800px);transform:translateY(800px)}100%{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}}@keyframes layui-m-anim-up{0%{opacity:0;-webkit-transform:translateY(800px);transform:translateY(800px)}100%{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}}.layui-m-anim-up{-webkit-animation-name:layui-m-anim-up;animation-name:layui-m-anim-up}@-webkit-keyframes layui-m-anim-left{0%{-webkit-transform:translateX(100%);transform:translateX(100%)}100%{-webkit-transform:translateX(0);transform:translateX(0)}}@keyframes layui-m-anim-left{0%{-webkit-transform:translateX(100%);transform:translateX(100%)}100%{-webkit-transform:translateX(0);transform:translateX(0)}}.layui-m-anim-left{-webkit-animation-name:layui-m-anim-left;animation-name:layui-m-anim-left}@-webkit-keyframes layui-m-anim-right{0%{-webkit-transform:translateX(-100%);transform:translateX(-100%)}100%{-webkit-transform:translateX(0);transform:translateX(0)}}@keyframes layui-m-anim-right{0%{-webkit-transform:translateX(-100%);transform:translateX(-100%)}100%{-webkit-transform:translateX(0);transform:translateX(0)}}.layui-m-anim-right{-webkit-animation-name:layui-m-anim-right;animation-name:layui-m-anim-right}@-webkit-keyframes layui-m-anim-lout{0%{-webkit-transform:translateX(0);transform:translateX(0)}100%{-webkit-transform:translateX(-100%);transform:translateX(-100%)}}@keyframes layui-m-anim-lout{0%{-webkit-transform:translateX(0);transform:translateX(0)}100%{-webkit-transform:translateX(-100%);transform:translateX(-100%)}}.layui-m-anim-lout{-webkit-animation-name:layui-m-anim-lout;animation-name:layui-m-anim-lout}@-webkit-keyframes layui-m-anim-rout{0%{-webkit-transform:translateX(0);transform:translateX(0)}100%{-webkit-transform:translateX(100%);transform:translateX(100%)}}@keyframes layui-m-anim-rout{0%{-webkit-transform:translateX(0);transform:translateX(0)}100%{-webkit-transform:translateX(100%);transform:translateX(100%)}}.layui-m-anim-rout{-webkit-animation-name:layui-m-anim-rout;animation-name:layui-m-anim-rout}.layui-m-layer{position:relative;z-index:19891014}.layui-m-layer *{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}.layui-m-layermain,.layui-m-layershade{position:fixed;left:0;top:0;width:100%;height:100%}.layui-m-layershade{background-color:rgba(0,0,0,.7);pointer-events:auto}.layui-m-layermain{display:table;font-family:Helvetica,arial,sans-serif;pointer-events:none}.layui-m-layermain .layui-m-layersection{display:table-cell;vertical-align:middle;text-align:center}.layui-m-layerchild{position:relative;display:inline-block;text-align:left;background-color:#fff;font-size:14px;border-radius:5px;box-shadow:0 0 8px rgba(0,0,0,.1);pointer-events:auto;-webkit-overflow-scrolling:touch;-webkit-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation-duration:.2s;animation-duration:.2s}.layui-m-layer0 .layui-m-layerchild{width:90%;max-width:640px}.layui-m-layer1 .layui-m-layerchild{border:none;border-radius:0}.layui-m-layer2 .layui-m-layerchild{width:auto;max-width:260px;min-width:40px;border:none;background:0 0;box-shadow:none;color:#fff}.layui-m-layerchild h3{padding:0 10px;height:60px;line-height:60px;font-size:16px;font-weight:400;border-radius:5px 5px 0 0;text-align:center}.layui-m-layerbtn span,.layui-m-layerchild h3{text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.layui-m-layercont{padding:50px 30px;line-height:22px;text-align:center}.layui-m-layer1 .layui-m-layercont{padding:0;text-align:left}.layui-m-layer2 .layui-m-layercont{text-align:center;padding:0;line-height:0}.layui-m-layer2 .layui-m-layercont i{width:25px;height:25px;margin-left:8px;display:inline-block;background-color:#fff;border-radius:100%;-webkit-animation:layui-m-anim-loading 1.4s infinite ease-in-out;animation:layui-m-anim-loading 1.4s infinite ease-in-out;-webkit-animation-fill-mode:both;animation-fill-mode:both}.layui-m-layerbtn,.layui-m-layerbtn span{position:relative;text-align:center;border-radius:0 0 5px 5px}.layui-m-layer2 .layui-m-layercont p{margin-top:20px}@-webkit-keyframes layui-m-anim-loading{0%,100%,80%{transform:scale(0);-webkit-transform:scale(0)}40%{transform:scale(1);-webkit-transform:scale(1)}}@keyframes layui-m-anim-loading{0%,100%,80%{transform:scale(0);-webkit-transform:scale(0)}40%{transform:scale(1);-webkit-transform:scale(1)}}.layui-m-layer2 .layui-m-layercont i:first-child{margin-left:0;-webkit-animation-delay:-.32s;animation-delay:-.32s}.layui-m-layer2 .layui-m-layercont i.layui-m-layerload{-webkit-animation-delay:-.16s;animation-delay:-.16s}.layui-m-layer2 .layui-m-layercont>div{line-height:22px;padding-top:7px;margin-bottom:20px;font-size:14px}.layui-m-layerbtn{display:box;display:-moz-box;display:-webkit-box;width:100%;height:50px;line-height:50px;font-size:0;border-top:1px solid #D0D0D0;background-color:#F2F2F2}.layui-m-layerbtn span{display:block;-moz-box-flex:1;box-flex:1;-webkit-box-flex:1;font-size:14px;cursor:pointer}.layui-m-layerbtn span[yes]{color:#40AFFE}.layui-m-layerbtn span[no]{border-right:1px solid #D0D0D0;border-radius:0 0 0 5px}.layui-m-layerbtn span:active{background-color:#F6F6F6}.layui-m-layerend{position:absolute;right:7px;top:10px;width:30px;height:30px;border:0;font-weight:400;background:0 0;cursor:pointer;-webkit-appearance:none;font-size:30px}.layui-m-layerend::after,.layui-m-layerend::before{position:absolute;left:5px;top:15px;content:'';width:18px;height:1px;background-color:#999;transform:rotate(45deg);-webkit-transform:rotate(45deg);border-radius:3px}.layui-m-layerend::after{transform:rotate(-45deg);-webkit-transform:rotate(-45deg)}body .layui-m-layer .layui-m-layer-footer{position:fixed;width:95%;max-width:100%;margin:0 auto;left:0;right:0;bottom:10px;background:0 0}.layui-m-layer-footer .layui-m-layercont{padding:20px;border-radius:5px 5px 0 0;background-color:rgba(255,255,255,.8)}.layui-m-layer-footer .layui-m-layerbtn{display:block;height:auto;background:0 0;border-top:none}.layui-m-layer-footer .layui-m-layerbtn span{background-color:rgba(255,255,255,.8)}.layui-m-layer-footer .layui-m-layerbtn span[no]{color:#FD482C;border-top:1px solid #c2c2c2;border-radius:0 0 5px 5px}.layui-m-layer-footer .layui-m-layerbtn span[yes]{margin-top:10px;border-radius:5px}body .layui-m-layer .layui-m-layer-msg{width:auto;max-width:90%;margin:0 auto;bottom:-150px;background-color:rgba(0,0,0,.7);color:#fff}.layui-m-layer-msg .layui-m-layercont{padding:10px 20px} \ No newline at end of file diff --git a/main/webapp/static/lib/layui/css/modules/code.css b/main/webapp/static/lib/layui/css/modules/code.css new file mode 100644 index 0000000..18c19c2 --- /dev/null +++ b/main/webapp/static/lib/layui/css/modules/code.css @@ -0,0 +1,2 @@ +/** layui-v2.2.6 MIT License By https://www.layui.com */ + html #layuicss-skincodecss{display:none;position:absolute;width:1989px}.layui-code-h3,.layui-code-view{position:relative;font-size:12px}.layui-code-view{display:block;margin:10px 0;padding:0;border:1px solid #e2e2e2;border-left-width:6px;background-color:#F2F2F2;color:#333;font-family:Courier New}.layui-code-h3{padding:0 10px;height:32px;line-height:32px;border-bottom:1px solid #e2e2e2}.layui-code-h3 a{position:absolute;right:10px;top:0;color:#999}.layui-code-view .layui-code-ol{position:relative;overflow:auto}.layui-code-view .layui-code-ol li{position:relative;margin-left:45px;line-height:20px;padding:0 5px;border-left:1px solid #e2e2e2;list-style-type:decimal-leading-zero;*list-style-type:decimal;background-color:#fff}.layui-code-view pre{margin:0}.layui-code-notepad{border:1px solid #0C0C0C;border-left-color:#3F3F3F;background-color:#0C0C0C;color:#C2BE9E}.layui-code-notepad .layui-code-h3{border-bottom:none}.layui-code-notepad .layui-code-ol li{background-color:#3F3F3F;border-left:none} \ No newline at end of file diff --git a/main/webapp/static/lib/layui/css/modules/laydate/default/laydate.css b/main/webapp/static/lib/layui/css/modules/laydate/default/laydate.css new file mode 100644 index 0000000..47bdeb0 --- /dev/null +++ b/main/webapp/static/lib/layui/css/modules/laydate/default/laydate.css @@ -0,0 +1,2 @@ +/** layui-v2.2.6 MIT License By https://www.layui.com */ + .laydate-set-ym,.layui-laydate,.layui-laydate *,.layui-laydate-list{box-sizing:border-box}html #layuicss-laydate{display:none;position:absolute;width:1989px}.layui-laydate *{margin:0;padding:0}.layui-laydate{position:absolute;z-index:66666666;margin:5px 0;border-radius:2px;font-size:14px;-webkit-animation-duration:.3s;animation-duration:.3s;-webkit-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation-name:laydate-upbit;animation-name:laydate-upbit}.layui-laydate-main{width:272px}.layui-laydate-content td,.layui-laydate-header *,.layui-laydate-list li{transition-duration:.3s;-webkit-transition-duration:.3s}@-webkit-keyframes laydate-upbit{from{-webkit-transform:translate3d(0,20px,0);opacity:.3}to{-webkit-transform:translate3d(0,0,0);opacity:1}}@keyframes laydate-upbit{from{transform:translate3d(0,20px,0);opacity:.3}to{transform:translate3d(0,0,0);opacity:1}}.layui-laydate-static{position:relative;z-index:0;display:inline-block;margin:0;-webkit-animation:none;animation:none}.laydate-ym-show .laydate-next-m,.laydate-ym-show .laydate-prev-m{display:none!important}.laydate-ym-show .laydate-next-y,.laydate-ym-show .laydate-prev-y{display:inline-block!important}.laydate-time-show .laydate-set-ym span[lay-type=month],.laydate-time-show .laydate-set-ym span[lay-type=year],.laydate-time-show .layui-laydate-header .layui-icon,.laydate-ym-show .laydate-set-ym span[lay-type=month]{display:none!important}.layui-laydate-header{position:relative;line-height:30px;padding:10px 70px 5px}.laydate-set-ym span,.layui-laydate-header i{padding:0 5px;cursor:pointer}.layui-laydate-header *{display:inline-block;vertical-align:bottom}.layui-laydate-header i{position:absolute;top:10px;color:#999;font-size:18px}.layui-laydate-header i.laydate-prev-y{left:15px}.layui-laydate-header i.laydate-prev-m{left:45px}.layui-laydate-header i.laydate-next-y{right:15px}.layui-laydate-header i.laydate-next-m{right:45px}.laydate-set-ym{width:100%;text-align:center;text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.laydate-time-text{cursor:default!important}.layui-laydate-content{position:relative;padding:10px;-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none}.layui-laydate-content table{border-collapse:collapse;border-spacing:0}.layui-laydate-content td,.layui-laydate-content th{width:36px;height:30px;padding:5px;text-align:center}.layui-laydate-content td{position:relative;cursor:pointer}.laydate-day-mark{position:absolute;left:0;top:0;width:100%;height:100%;line-height:30px;font-size:12px;overflow:hidden}.laydate-day-mark::after{position:absolute;content:'';right:2px;top:2px;width:5px;height:5px;border-radius:50%}.layui-laydate-footer{position:relative;height:46px;line-height:26px;padding:10px 20px}.layui-laydate-footer span{margin-right:15px;display:inline-block;cursor:pointer;font-size:12px}.layui-laydate-footer span:hover{color:#5FB878}.laydate-footer-btns{position:absolute;right:10px;top:10px}.laydate-footer-btns span{height:26px;line-height:26px;margin:0 0 0 -1px;padding:0 10px;border:1px solid #C9C9C9;background-color:#fff;white-space:nowrap;vertical-align:top;border-radius:2px}.layui-laydate-list>li,.layui-laydate-range .layui-laydate-main{display:inline-block;vertical-align:middle}.layui-laydate-list{position:absolute;left:0;top:0;width:100%;height:100%;padding:10px;background-color:#fff}.layui-laydate-list>li{position:relative;width:33.3%;height:36px;line-height:36px;margin:3px 0;text-align:center;cursor:pointer}.laydate-month-list>li{width:25%;margin:17px 0}.laydate-time-list>li{height:100%;margin:0;line-height:normal;cursor:default}.laydate-time-list p{position:relative;top:-4px;line-height:29px}.laydate-time-list ol{height:181px;overflow:hidden}.laydate-time-list>li:hover ol{overflow-y:auto}.laydate-time-list ol li{width:130%;padding-left:33px;line-height:30px;text-align:left;cursor:pointer}.layui-laydate-hint{position:absolute;top:115px;left:50%;width:250px;margin-left:-125px;line-height:20px;padding:15px;text-align:center;font-size:12px}.layui-laydate-range{width:546px}.layui-laydate-range .laydate-main-list-0 .laydate-next-m,.layui-laydate-range .laydate-main-list-0 .laydate-next-y,.layui-laydate-range .laydate-main-list-1 .laydate-prev-m,.layui-laydate-range .laydate-main-list-1 .laydate-prev-y{display:none}.layui-laydate-range .laydate-main-list-1 .layui-laydate-content{border-left:1px solid #e2e2e2}.layui-laydate,.layui-laydate-hint{border:1px solid #d2d2d2;box-shadow:0 2px 4px rgba(0,0,0,.12);background-color:#fff;color:#666}.layui-laydate-header{border-bottom:1px solid #e2e2e2}.layui-laydate-header i:hover,.layui-laydate-header span:hover{color:#5FB878}.layui-laydate-content{border-top:none 0;border-bottom:none 0}.layui-laydate-content th{font-weight:400;color:#333}.layui-laydate-content td{color:#666}.layui-laydate-content td.laydate-selected{background-color:#00F7DE}.laydate-selected:hover{background-color:#00F7DE!important}.layui-laydate-content td:hover,.layui-laydate-list li:hover{background-color:#eaeaea;color:#333}.laydate-time-list li ol{margin:0;padding:0;border:1px solid #e2e2e2;border-left-width:0}.laydate-time-list li:first-child ol{border-left-width:1px}.laydate-time-list>li:hover{background:0 0}.layui-laydate-content .laydate-day-next,.layui-laydate-content .laydate-day-prev{color:#d2d2d2}.laydate-selected.laydate-day-next,.laydate-selected.laydate-day-prev{background-color:#f8f8f8!important}.layui-laydate-footer{border-top:1px solid #e2e2e2}.layui-laydate-hint{color:#FF5722}.laydate-day-mark::after{background-color:#5FB878}.layui-laydate-content td.layui-this .laydate-day-mark::after{display:none}.layui-laydate-footer span[lay-type=date]{color:#5FB878}.layui-laydate .layui-this{background-color:#009688!important;color:#fff!important}.layui-laydate .laydate-disabled,.layui-laydate .laydate-disabled:hover{background:0 0!important;color:#d2d2d2!important;cursor:not-allowed!important;-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none}.laydate-theme-molv{border:none}.laydate-theme-molv.layui-laydate-range{width:548px}.laydate-theme-molv .layui-laydate-main{width:274px}.laydate-theme-molv .layui-laydate-header{border:none;background-color:#009688}.laydate-theme-molv .layui-laydate-header i,.laydate-theme-molv .layui-laydate-header span{color:#f6f6f6}.laydate-theme-molv .layui-laydate-header i:hover,.laydate-theme-molv .layui-laydate-header span:hover{color:#fff}.laydate-theme-molv .layui-laydate-content{border:1px solid #e2e2e2;border-top:none;border-bottom:none}.laydate-theme-molv .laydate-main-list-1 .layui-laydate-content{border-left:none}.laydate-theme-grid .laydate-month-list>li,.laydate-theme-grid .laydate-year-list>li,.laydate-theme-grid .layui-laydate-content td,.laydate-theme-grid .layui-laydate-content thead,.laydate-theme-molv .layui-laydate-footer{border:1px solid #e2e2e2}.laydate-theme-grid .laydate-selected,.laydate-theme-grid .laydate-selected:hover{background-color:#f2f2f2!important;color:#009688!important}.laydate-theme-grid .laydate-selected.laydate-day-next,.laydate-theme-grid .laydate-selected.laydate-day-prev{color:#d2d2d2!important}.laydate-theme-grid .laydate-month-list,.laydate-theme-grid .laydate-year-list{margin:1px 0 0 1px}.laydate-theme-grid .laydate-month-list>li,.laydate-theme-grid .laydate-year-list>li{margin:0 -1px -1px 0}.laydate-theme-grid .laydate-year-list>li{height:43px;line-height:43px}.laydate-theme-grid .laydate-month-list>li{height:71px;line-height:71px} \ No newline at end of file diff --git a/main/webapp/static/lib/layui/css/modules/layer/default/icon-ext.png b/main/webapp/static/lib/layui/css/modules/layer/default/icon-ext.png new file mode 100644 index 0000000..bbbb669 Binary files /dev/null and b/main/webapp/static/lib/layui/css/modules/layer/default/icon-ext.png differ diff --git a/main/webapp/static/lib/layui/css/modules/layer/default/icon.png b/main/webapp/static/lib/layui/css/modules/layer/default/icon.png new file mode 100644 index 0000000..3e17da8 Binary files /dev/null and b/main/webapp/static/lib/layui/css/modules/layer/default/icon.png differ diff --git a/main/webapp/static/lib/layui/css/modules/layer/default/layer.css b/main/webapp/static/lib/layui/css/modules/layer/default/layer.css new file mode 100644 index 0000000..6f99f09 --- /dev/null +++ b/main/webapp/static/lib/layui/css/modules/layer/default/layer.css @@ -0,0 +1,2 @@ +/** layui-v2.2.6 MIT License By https://www.layui.com */ + .layui-layer-imgbar,.layui-layer-imgtit a,.layui-layer-tab .layui-layer-title span,.layui-layer-title{text-overflow:ellipsis;white-space:nowrap}html #layuicss-layer{display:none;position:absolute;width:1989px}.layui-layer,.layui-layer-shade{position:fixed;_position:absolute;pointer-events:auto}.layui-layer-shade{top:0;left:0;width:100%;height:100%;_height:expression(document.body.offsetHeight+"px")}.layui-layer{-webkit-overflow-scrolling:touch;top:150px;left:0;margin:0;padding:0;background-color:#fff;-webkit-background-clip:content;border-radius:2px;box-shadow:1px 1px 50px rgba(0,0,0,.3)}.layui-layer-close{position:absolute}.layui-layer-content{position:relative}.layui-layer-border{border:1px solid #B2B2B2;border:1px solid rgba(0,0,0,.1);box-shadow:1px 1px 5px rgba(0,0,0,.2)}.layui-layer-load{background:url(loading-1.gif) center center no-repeat #eee}.layui-layer-ico{background:url(icon.png) no-repeat}.layui-layer-btn a,.layui-layer-dialog .layui-layer-ico,.layui-layer-setwin a{display:inline-block;*display:inline;*zoom:1;vertical-align:top}.layui-layer-move{display:none;position:fixed;*position:absolute;left:0;top:0;width:100%;height:100%;cursor:move;opacity:0;filter:alpha(opacity=0);background-color:#fff;z-index:2147483647}.layui-layer-resize{position:absolute;width:15px;height:15px;right:0;bottom:0;cursor:se-resize}.layer-anim{-webkit-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation-duration:.3s;animation-duration:.3s}@-webkit-keyframes layer-bounceIn{0%{opacity:0;-webkit-transform:scale(.5);transform:scale(.5)}100%{opacity:1;-webkit-transform:scale(1);transform:scale(1)}}@keyframes layer-bounceIn{0%{opacity:0;-webkit-transform:scale(.5);-ms-transform:scale(.5);transform:scale(.5)}100%{opacity:1;-webkit-transform:scale(1);-ms-transform:scale(1);transform:scale(1)}}.layer-anim-00{-webkit-animation-name:layer-bounceIn;animation-name:layer-bounceIn}@-webkit-keyframes layer-zoomInDown{0%{opacity:0;-webkit-transform:scale(.1) translateY(-2000px);transform:scale(.1) translateY(-2000px);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}60%{opacity:1;-webkit-transform:scale(.475) translateY(60px);transform:scale(.475) translateY(60px);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}}@keyframes layer-zoomInDown{0%{opacity:0;-webkit-transform:scale(.1) translateY(-2000px);-ms-transform:scale(.1) translateY(-2000px);transform:scale(.1) translateY(-2000px);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}60%{opacity:1;-webkit-transform:scale(.475) translateY(60px);-ms-transform:scale(.475) translateY(60px);transform:scale(.475) translateY(60px);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}}.layer-anim-01{-webkit-animation-name:layer-zoomInDown;animation-name:layer-zoomInDown}@-webkit-keyframes layer-fadeInUpBig{0%{opacity:0;-webkit-transform:translateY(2000px);transform:translateY(2000px)}100%{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}}@keyframes layer-fadeInUpBig{0%{opacity:0;-webkit-transform:translateY(2000px);-ms-transform:translateY(2000px);transform:translateY(2000px)}100%{opacity:1;-webkit-transform:translateY(0);-ms-transform:translateY(0);transform:translateY(0)}}.layer-anim-02{-webkit-animation-name:layer-fadeInUpBig;animation-name:layer-fadeInUpBig}@-webkit-keyframes layer-zoomInLeft{0%{opacity:0;-webkit-transform:scale(.1) translateX(-2000px);transform:scale(.1) translateX(-2000px);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}60%{opacity:1;-webkit-transform:scale(.475) translateX(48px);transform:scale(.475) translateX(48px);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}}@keyframes layer-zoomInLeft{0%{opacity:0;-webkit-transform:scale(.1) translateX(-2000px);-ms-transform:scale(.1) translateX(-2000px);transform:scale(.1) translateX(-2000px);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}60%{opacity:1;-webkit-transform:scale(.475) translateX(48px);-ms-transform:scale(.475) translateX(48px);transform:scale(.475) translateX(48px);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}}.layer-anim-03{-webkit-animation-name:layer-zoomInLeft;animation-name:layer-zoomInLeft}@-webkit-keyframes layer-rollIn{0%{opacity:0;-webkit-transform:translateX(-100%) rotate(-120deg);transform:translateX(-100%) rotate(-120deg)}100%{opacity:1;-webkit-transform:translateX(0) rotate(0);transform:translateX(0) rotate(0)}}@keyframes layer-rollIn{0%{opacity:0;-webkit-transform:translateX(-100%) rotate(-120deg);-ms-transform:translateX(-100%) rotate(-120deg);transform:translateX(-100%) rotate(-120deg)}100%{opacity:1;-webkit-transform:translateX(0) rotate(0);-ms-transform:translateX(0) rotate(0);transform:translateX(0) rotate(0)}}.layer-anim-04{-webkit-animation-name:layer-rollIn;animation-name:layer-rollIn}@keyframes layer-fadeIn{0%{opacity:0}100%{opacity:1}}.layer-anim-05{-webkit-animation-name:layer-fadeIn;animation-name:layer-fadeIn}@-webkit-keyframes layer-shake{0%,100%{-webkit-transform:translateX(0);transform:translateX(0)}10%,30%,50%,70%,90%{-webkit-transform:translateX(-10px);transform:translateX(-10px)}20%,40%,60%,80%{-webkit-transform:translateX(10px);transform:translateX(10px)}}@keyframes layer-shake{0%,100%{-webkit-transform:translateX(0);-ms-transform:translateX(0);transform:translateX(0)}10%,30%,50%,70%,90%{-webkit-transform:translateX(-10px);-ms-transform:translateX(-10px);transform:translateX(-10px)}20%,40%,60%,80%{-webkit-transform:translateX(10px);-ms-transform:translateX(10px);transform:translateX(10px)}}.layer-anim-06{-webkit-animation-name:layer-shake;animation-name:layer-shake}@-webkit-keyframes fadeIn{0%{opacity:0}100%{opacity:1}}.layui-layer-title{padding:0 80px 0 20px;height:42px;line-height:42px;border-bottom:1px solid #eee;font-size:14px;color:#333;overflow:hidden;background-color:#F8F8F8;border-radius:2px 2px 0 0}.layui-layer-setwin{position:absolute;right:15px;*right:0;top:15px;font-size:0;line-height:initial}.layui-layer-setwin a{position:relative;width:16px;height:16px;margin-left:10px;font-size:12px;_overflow:hidden}.layui-layer-setwin .layui-layer-min cite{position:absolute;width:14px;height:2px;left:0;top:50%;margin-top:-1px;background-color:#2E2D3C;cursor:pointer;_overflow:hidden}.layui-layer-setwin .layui-layer-min:hover cite{background-color:#2D93CA}.layui-layer-setwin .layui-layer-max{background-position:-32px -40px}.layui-layer-setwin .layui-layer-max:hover{background-position:-16px -40px}.layui-layer-setwin .layui-layer-maxmin{background-position:-65px -40px}.layui-layer-setwin .layui-layer-maxmin:hover{background-position:-49px -40px}.layui-layer-setwin .layui-layer-close1{background-position:1px -40px;cursor:pointer}.layui-layer-setwin .layui-layer-close1:hover{opacity:.7}.layui-layer-setwin .layui-layer-close2{position:absolute;right:-28px;top:-28px;width:30px;height:30px;margin-left:0;background-position:-149px -31px;*right:-18px;_display:none}.layui-layer-setwin .layui-layer-close2:hover{background-position:-180px -31px}.layui-layer-btn{text-align:right;padding:0 15px 12px;pointer-events:auto;user-select:none;-webkit-user-select:none}.layui-layer-btn a{height:28px;line-height:28px;margin:5px 5px 0;padding:0 15px;border:1px solid #dedede;background-color:#fff;color:#333;border-radius:2px;font-weight:400;cursor:pointer;text-decoration:none}.layui-layer-btn a:hover{opacity:.9;text-decoration:none}.layui-layer-btn a:active{opacity:.8}.layui-layer-btn .layui-layer-btn0{border-color:#1E9FFF;background-color:#1E9FFF;color:#fff}.layui-layer-btn-l{text-align:left}.layui-layer-btn-c{text-align:center}.layui-layer-dialog{min-width:260px}.layui-layer-dialog .layui-layer-content{position:relative;padding:20px;line-height:24px;word-break:break-all;overflow:hidden;font-size:14px;overflow-x:hidden;overflow-y:auto}.layui-layer-dialog .layui-layer-content .layui-layer-ico{position:absolute;top:16px;left:15px;_left:-40px;width:30px;height:30px}.layui-layer-ico1{background-position:-30px 0}.layui-layer-ico2{background-position:-60px 0}.layui-layer-ico3{background-position:-90px 0}.layui-layer-ico4{background-position:-120px 0}.layui-layer-ico5{background-position:-150px 0}.layui-layer-ico6{background-position:-180px 0}.layui-layer-rim{border:6px solid #8D8D8D;border:6px solid rgba(0,0,0,.3);border-radius:5px;box-shadow:none}.layui-layer-msg{min-width:180px;border:1px solid #D3D4D3;box-shadow:none}.layui-layer-hui{min-width:100px;background-color:#000;filter:alpha(opacity=60);background-color:rgba(0,0,0,.6);color:#fff;border:none}.layui-layer-hui .layui-layer-content{padding:12px 25px;text-align:center}.layui-layer-dialog .layui-layer-padding{padding:20px 20px 20px 55px;text-align:left}.layui-layer-page .layui-layer-content{position:relative;overflow:auto}.layui-layer-iframe .layui-layer-btn,.layui-layer-page .layui-layer-btn{padding-top:10px}.layui-layer-nobg{background:0 0}.layui-layer-iframe iframe{display:block;width:100%}.layui-layer-loading{border-radius:100%;background:0 0;box-shadow:none;border:none}.layui-layer-loading .layui-layer-content{width:60px;height:24px;background:url(loading-0.gif) no-repeat}.layui-layer-loading .layui-layer-loading1{width:37px;height:37px;background:url(loading-1.gif) no-repeat}.layui-layer-ico16,.layui-layer-loading .layui-layer-loading2{width:32px;height:32px;background:url(loading-2.gif) no-repeat}.layui-layer-tips{background:0 0;box-shadow:none;border:none}.layui-layer-tips .layui-layer-content{position:relative;line-height:22px;min-width:12px;padding:8px 15px;font-size:12px;_float:left;border-radius:2px;box-shadow:1px 1px 3px rgba(0,0,0,.2);background-color:#000;color:#fff}.layui-layer-tips .layui-layer-close{right:-2px;top:-1px}.layui-layer-tips i.layui-layer-TipsG{position:absolute;width:0;height:0;border-width:8px;border-color:transparent;border-style:dashed;*overflow:hidden}.layui-layer-tips i.layui-layer-TipsB,.layui-layer-tips i.layui-layer-TipsT{left:5px;border-right-style:solid;border-right-color:#000}.layui-layer-tips i.layui-layer-TipsT{bottom:-8px}.layui-layer-tips i.layui-layer-TipsB{top:-8px}.layui-layer-tips i.layui-layer-TipsL,.layui-layer-tips i.layui-layer-TipsR{top:5px;border-bottom-style:solid;border-bottom-color:#000}.layui-layer-tips i.layui-layer-TipsR{left:-8px}.layui-layer-tips i.layui-layer-TipsL{right:-8px}.layui-layer-lan[type=dialog]{min-width:280px}.layui-layer-lan .layui-layer-title{background:#4476A7;color:#fff;border:none}.layui-layer-lan .layui-layer-btn{padding:5px 10px 10px;text-align:right;border-top:1px solid #E9E7E7}.layui-layer-lan .layui-layer-btn a{background:#fff;border-color:#E9E7E7;color:#333}.layui-layer-lan .layui-layer-btn .layui-layer-btn1{background:#C9C5C5}.layui-layer-molv .layui-layer-title{background:#009f95;color:#fff;border:none}.layui-layer-molv .layui-layer-btn a{background:#009f95;border-color:#009f95}.layui-layer-molv .layui-layer-btn .layui-layer-btn1{background:#92B8B1}.layui-layer-iconext{background:url(icon-ext.png) no-repeat}.layui-layer-prompt .layui-layer-input{display:block;width:230px;height:36px;margin:0 auto;line-height:30px;padding-left:10px;border:1px solid #e6e6e6;color:#333}.layui-layer-prompt textarea.layui-layer-input{width:300px;height:100px;line-height:20px;padding:6px 10px}.layui-layer-prompt .layui-layer-content{padding:20px}.layui-layer-prompt .layui-layer-btn{padding-top:0}.layui-layer-tab{box-shadow:1px 1px 50px rgba(0,0,0,.4)}.layui-layer-tab .layui-layer-title{padding-left:0;overflow:visible}.layui-layer-tab .layui-layer-title span{position:relative;float:left;min-width:80px;max-width:260px;padding:0 20px;text-align:center;overflow:hidden;cursor:pointer}.layui-layer-tab .layui-layer-title span.layui-this{height:43px;border-left:1px solid #eee;border-right:1px solid #eee;background-color:#fff;z-index:10}.layui-layer-tab .layui-layer-title span:first-child{border-left:none}.layui-layer-tabmain{line-height:24px;clear:both}.layui-layer-tabmain .layui-layer-tabli{display:none}.layui-layer-tabmain .layui-layer-tabli.layui-this{display:block}.layui-layer-photos{-webkit-animation-duration:.8s;animation-duration:.8s}.layui-layer-photos .layui-layer-content{overflow:hidden;text-align:center}.layui-layer-photos .layui-layer-phimg img{position:relative;width:100%;display:inline-block;*display:inline;*zoom:1;vertical-align:top}.layui-layer-imgbar,.layui-layer-imguide{display:none}.layui-layer-imgnext,.layui-layer-imgprev{position:absolute;top:50%;width:27px;_width:44px;height:44px;margin-top:-22px;outline:0;blr:expression(this.onFocus=this.blur())}.layui-layer-imgprev{left:10px;background-position:-5px -5px;_background-position:-70px -5px}.layui-layer-imgprev:hover{background-position:-33px -5px;_background-position:-120px -5px}.layui-layer-imgnext{right:10px;_right:8px;background-position:-5px -50px;_background-position:-70px -50px}.layui-layer-imgnext:hover{background-position:-33px -50px;_background-position:-120px -50px}.layui-layer-imgbar{position:absolute;left:0;bottom:0;width:100%;height:32px;line-height:32px;background-color:rgba(0,0,0,.8);background-color:#000\9;filter:Alpha(opacity=80);color:#fff;overflow:hidden;font-size:0}.layui-layer-imgtit *{display:inline-block;*display:inline;*zoom:1;vertical-align:top;font-size:12px}.layui-layer-imgtit a{max-width:65%;overflow:hidden;color:#fff}.layui-layer-imgtit a:hover{color:#fff;text-decoration:underline}.layui-layer-imgtit em{padding-left:10px;font-style:normal}@-webkit-keyframes layer-bounceOut{100%{opacity:0;-webkit-transform:scale(.7);transform:scale(.7)}30%{-webkit-transform:scale(1.05);transform:scale(1.05)}0%{-webkit-transform:scale(1);transform:scale(1)}}@keyframes layer-bounceOut{100%{opacity:0;-webkit-transform:scale(.7);-ms-transform:scale(.7);transform:scale(.7)}30%{-webkit-transform:scale(1.05);-ms-transform:scale(1.05);transform:scale(1.05)}0%{-webkit-transform:scale(1);-ms-transform:scale(1);transform:scale(1)}}.layer-anim-close{-webkit-animation-name:layer-bounceOut;animation-name:layer-bounceOut;-webkit-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation-duration:.2s;animation-duration:.2s}@media screen and (max-width:1100px){.layui-layer-iframe{overflow-y:auto;-webkit-overflow-scrolling:touch}} \ No newline at end of file diff --git a/main/webapp/static/lib/layui/css/modules/layer/default/loading-0.gif b/main/webapp/static/lib/layui/css/modules/layer/default/loading-0.gif new file mode 100644 index 0000000..6f3c953 Binary files /dev/null and b/main/webapp/static/lib/layui/css/modules/layer/default/loading-0.gif differ diff --git a/main/webapp/static/lib/layui/css/modules/layer/default/loading-1.gif b/main/webapp/static/lib/layui/css/modules/layer/default/loading-1.gif new file mode 100644 index 0000000..db3a483 Binary files /dev/null and b/main/webapp/static/lib/layui/css/modules/layer/default/loading-1.gif differ diff --git a/main/webapp/static/lib/layui/css/modules/layer/default/loading-2.gif b/main/webapp/static/lib/layui/css/modules/layer/default/loading-2.gif new file mode 100644 index 0000000..5bb90fd Binary files /dev/null and b/main/webapp/static/lib/layui/css/modules/layer/default/loading-2.gif differ diff --git a/main/webapp/static/lib/layui/font/iconfont.eot b/main/webapp/static/lib/layui/font/iconfont.eot new file mode 100644 index 0000000..8202e60 Binary files /dev/null and b/main/webapp/static/lib/layui/font/iconfont.eot differ diff --git a/main/webapp/static/lib/layui/font/iconfont.svg b/main/webapp/static/lib/layui/font/iconfont.svg new file mode 100644 index 0000000..58b0602 --- /dev/null +++ b/main/webapp/static/lib/layui/font/iconfont.svg @@ -0,0 +1,483 @@ + + + + + +Created by iconfont + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/main/webapp/static/lib/layui/font/iconfont.ttf b/main/webapp/static/lib/layui/font/iconfont.ttf new file mode 100644 index 0000000..9b3f2d3 Binary files /dev/null and b/main/webapp/static/lib/layui/font/iconfont.ttf differ diff --git a/main/webapp/static/lib/layui/font/iconfont.woff b/main/webapp/static/lib/layui/font/iconfont.woff new file mode 100644 index 0000000..d747d49 Binary files /dev/null and b/main/webapp/static/lib/layui/font/iconfont.woff differ diff --git a/main/webapp/static/lib/layui/images/face/0.gif b/main/webapp/static/lib/layui/images/face/0.gif new file mode 100644 index 0000000..a63f0d5 Binary files /dev/null and b/main/webapp/static/lib/layui/images/face/0.gif differ diff --git a/main/webapp/static/lib/layui/images/face/1.gif b/main/webapp/static/lib/layui/images/face/1.gif new file mode 100644 index 0000000..b2b78b2 Binary files /dev/null and b/main/webapp/static/lib/layui/images/face/1.gif differ diff --git a/main/webapp/static/lib/layui/images/face/10.gif b/main/webapp/static/lib/layui/images/face/10.gif new file mode 100644 index 0000000..556c7e3 Binary files /dev/null and b/main/webapp/static/lib/layui/images/face/10.gif differ diff --git a/main/webapp/static/lib/layui/images/face/11.gif b/main/webapp/static/lib/layui/images/face/11.gif new file mode 100644 index 0000000..2bfc58b Binary files /dev/null and b/main/webapp/static/lib/layui/images/face/11.gif differ diff --git a/main/webapp/static/lib/layui/images/face/12.gif b/main/webapp/static/lib/layui/images/face/12.gif new file mode 100644 index 0000000..1c321c7 Binary files /dev/null and b/main/webapp/static/lib/layui/images/face/12.gif differ diff --git a/main/webapp/static/lib/layui/images/face/13.gif b/main/webapp/static/lib/layui/images/face/13.gif new file mode 100644 index 0000000..300bbc2 Binary files /dev/null and b/main/webapp/static/lib/layui/images/face/13.gif differ diff --git a/main/webapp/static/lib/layui/images/face/14.gif b/main/webapp/static/lib/layui/images/face/14.gif new file mode 100644 index 0000000..43b6d0a Binary files /dev/null and b/main/webapp/static/lib/layui/images/face/14.gif differ diff --git a/main/webapp/static/lib/layui/images/face/15.gif b/main/webapp/static/lib/layui/images/face/15.gif new file mode 100644 index 0000000..c9f25fa Binary files /dev/null and b/main/webapp/static/lib/layui/images/face/15.gif differ diff --git a/main/webapp/static/lib/layui/images/face/16.gif b/main/webapp/static/lib/layui/images/face/16.gif new file mode 100644 index 0000000..34f28e4 Binary files /dev/null and b/main/webapp/static/lib/layui/images/face/16.gif differ diff --git a/main/webapp/static/lib/layui/images/face/17.gif b/main/webapp/static/lib/layui/images/face/17.gif new file mode 100644 index 0000000..39cd035 Binary files /dev/null and b/main/webapp/static/lib/layui/images/face/17.gif differ diff --git a/main/webapp/static/lib/layui/images/face/18.gif b/main/webapp/static/lib/layui/images/face/18.gif new file mode 100644 index 0000000..7bce299 Binary files /dev/null and b/main/webapp/static/lib/layui/images/face/18.gif differ diff --git a/main/webapp/static/lib/layui/images/face/19.gif b/main/webapp/static/lib/layui/images/face/19.gif new file mode 100644 index 0000000..adac542 Binary files /dev/null and b/main/webapp/static/lib/layui/images/face/19.gif differ diff --git a/main/webapp/static/lib/layui/images/face/2.gif b/main/webapp/static/lib/layui/images/face/2.gif new file mode 100644 index 0000000..7edbb58 Binary files /dev/null and b/main/webapp/static/lib/layui/images/face/2.gif differ diff --git a/main/webapp/static/lib/layui/images/face/20.gif b/main/webapp/static/lib/layui/images/face/20.gif new file mode 100644 index 0000000..50631a6 Binary files /dev/null and b/main/webapp/static/lib/layui/images/face/20.gif differ diff --git a/main/webapp/static/lib/layui/images/face/21.gif b/main/webapp/static/lib/layui/images/face/21.gif new file mode 100644 index 0000000..b984212 Binary files /dev/null and b/main/webapp/static/lib/layui/images/face/21.gif differ diff --git a/main/webapp/static/lib/layui/images/face/22.gif b/main/webapp/static/lib/layui/images/face/22.gif new file mode 100644 index 0000000..1f0bd8b Binary files /dev/null and b/main/webapp/static/lib/layui/images/face/22.gif differ diff --git a/main/webapp/static/lib/layui/images/face/23.gif b/main/webapp/static/lib/layui/images/face/23.gif new file mode 100644 index 0000000..e05e0f9 Binary files /dev/null and b/main/webapp/static/lib/layui/images/face/23.gif differ diff --git a/main/webapp/static/lib/layui/images/face/24.gif b/main/webapp/static/lib/layui/images/face/24.gif new file mode 100644 index 0000000..f35928a Binary files /dev/null and b/main/webapp/static/lib/layui/images/face/24.gif differ diff --git a/main/webapp/static/lib/layui/images/face/25.gif b/main/webapp/static/lib/layui/images/face/25.gif new file mode 100644 index 0000000..0b4a883 Binary files /dev/null and b/main/webapp/static/lib/layui/images/face/25.gif differ diff --git a/main/webapp/static/lib/layui/images/face/26.gif b/main/webapp/static/lib/layui/images/face/26.gif new file mode 100644 index 0000000..45c4fb5 Binary files /dev/null and b/main/webapp/static/lib/layui/images/face/26.gif differ diff --git a/main/webapp/static/lib/layui/images/face/27.gif b/main/webapp/static/lib/layui/images/face/27.gif new file mode 100644 index 0000000..7a4c013 Binary files /dev/null and b/main/webapp/static/lib/layui/images/face/27.gif differ diff --git a/main/webapp/static/lib/layui/images/face/28.gif b/main/webapp/static/lib/layui/images/face/28.gif new file mode 100644 index 0000000..fc5a0cf Binary files /dev/null and b/main/webapp/static/lib/layui/images/face/28.gif differ diff --git a/main/webapp/static/lib/layui/images/face/29.gif b/main/webapp/static/lib/layui/images/face/29.gif new file mode 100644 index 0000000..5dd7442 Binary files /dev/null and b/main/webapp/static/lib/layui/images/face/29.gif differ diff --git a/main/webapp/static/lib/layui/images/face/3.gif b/main/webapp/static/lib/layui/images/face/3.gif new file mode 100644 index 0000000..86df67b Binary files /dev/null and b/main/webapp/static/lib/layui/images/face/3.gif differ diff --git a/main/webapp/static/lib/layui/images/face/30.gif b/main/webapp/static/lib/layui/images/face/30.gif new file mode 100644 index 0000000..b751f98 Binary files /dev/null and b/main/webapp/static/lib/layui/images/face/30.gif differ diff --git a/main/webapp/static/lib/layui/images/face/31.gif b/main/webapp/static/lib/layui/images/face/31.gif new file mode 100644 index 0000000..c9476d7 Binary files /dev/null and b/main/webapp/static/lib/layui/images/face/31.gif differ diff --git a/main/webapp/static/lib/layui/images/face/32.gif b/main/webapp/static/lib/layui/images/face/32.gif new file mode 100644 index 0000000..9931b06 Binary files /dev/null and b/main/webapp/static/lib/layui/images/face/32.gif differ diff --git a/main/webapp/static/lib/layui/images/face/33.gif b/main/webapp/static/lib/layui/images/face/33.gif new file mode 100644 index 0000000..59111a3 Binary files /dev/null and b/main/webapp/static/lib/layui/images/face/33.gif differ diff --git a/main/webapp/static/lib/layui/images/face/34.gif b/main/webapp/static/lib/layui/images/face/34.gif new file mode 100644 index 0000000..a334548 Binary files /dev/null and b/main/webapp/static/lib/layui/images/face/34.gif differ diff --git a/main/webapp/static/lib/layui/images/face/35.gif b/main/webapp/static/lib/layui/images/face/35.gif new file mode 100644 index 0000000..a932264 Binary files /dev/null and b/main/webapp/static/lib/layui/images/face/35.gif differ diff --git a/main/webapp/static/lib/layui/images/face/36.gif b/main/webapp/static/lib/layui/images/face/36.gif new file mode 100644 index 0000000..6de432a Binary files /dev/null and b/main/webapp/static/lib/layui/images/face/36.gif differ diff --git a/main/webapp/static/lib/layui/images/face/37.gif b/main/webapp/static/lib/layui/images/face/37.gif new file mode 100644 index 0000000..d05f2da Binary files /dev/null and b/main/webapp/static/lib/layui/images/face/37.gif differ diff --git a/main/webapp/static/lib/layui/images/face/38.gif b/main/webapp/static/lib/layui/images/face/38.gif new file mode 100644 index 0000000..8b1c88a Binary files /dev/null and b/main/webapp/static/lib/layui/images/face/38.gif differ diff --git a/main/webapp/static/lib/layui/images/face/39.gif b/main/webapp/static/lib/layui/images/face/39.gif new file mode 100644 index 0000000..38b84a5 Binary files /dev/null and b/main/webapp/static/lib/layui/images/face/39.gif differ diff --git a/main/webapp/static/lib/layui/images/face/4.gif b/main/webapp/static/lib/layui/images/face/4.gif new file mode 100644 index 0000000..d52200c Binary files /dev/null and b/main/webapp/static/lib/layui/images/face/4.gif differ diff --git a/main/webapp/static/lib/layui/images/face/40.gif b/main/webapp/static/lib/layui/images/face/40.gif new file mode 100644 index 0000000..ae42991 Binary files /dev/null and b/main/webapp/static/lib/layui/images/face/40.gif differ diff --git a/main/webapp/static/lib/layui/images/face/41.gif b/main/webapp/static/lib/layui/images/face/41.gif new file mode 100644 index 0000000..b9c715c Binary files /dev/null and b/main/webapp/static/lib/layui/images/face/41.gif differ diff --git a/main/webapp/static/lib/layui/images/face/42.gif b/main/webapp/static/lib/layui/images/face/42.gif new file mode 100644 index 0000000..0eb1434 Binary files /dev/null and b/main/webapp/static/lib/layui/images/face/42.gif differ diff --git a/main/webapp/static/lib/layui/images/face/43.gif b/main/webapp/static/lib/layui/images/face/43.gif new file mode 100644 index 0000000..ac0b700 Binary files /dev/null and b/main/webapp/static/lib/layui/images/face/43.gif differ diff --git a/main/webapp/static/lib/layui/images/face/44.gif b/main/webapp/static/lib/layui/images/face/44.gif new file mode 100644 index 0000000..ad44497 Binary files /dev/null and b/main/webapp/static/lib/layui/images/face/44.gif differ diff --git a/main/webapp/static/lib/layui/images/face/45.gif b/main/webapp/static/lib/layui/images/face/45.gif new file mode 100644 index 0000000..6837fca Binary files /dev/null and b/main/webapp/static/lib/layui/images/face/45.gif differ diff --git a/main/webapp/static/lib/layui/images/face/46.gif b/main/webapp/static/lib/layui/images/face/46.gif new file mode 100644 index 0000000..d62916d Binary files /dev/null and b/main/webapp/static/lib/layui/images/face/46.gif differ diff --git a/main/webapp/static/lib/layui/images/face/47.gif b/main/webapp/static/lib/layui/images/face/47.gif new file mode 100644 index 0000000..58a0836 Binary files /dev/null and b/main/webapp/static/lib/layui/images/face/47.gif differ diff --git a/main/webapp/static/lib/layui/images/face/48.gif b/main/webapp/static/lib/layui/images/face/48.gif new file mode 100644 index 0000000..7ffd161 Binary files /dev/null and b/main/webapp/static/lib/layui/images/face/48.gif differ diff --git a/main/webapp/static/lib/layui/images/face/49.gif b/main/webapp/static/lib/layui/images/face/49.gif new file mode 100644 index 0000000..959b992 Binary files /dev/null and b/main/webapp/static/lib/layui/images/face/49.gif differ diff --git a/main/webapp/static/lib/layui/images/face/5.gif b/main/webapp/static/lib/layui/images/face/5.gif new file mode 100644 index 0000000..4e8b09f Binary files /dev/null and b/main/webapp/static/lib/layui/images/face/5.gif differ diff --git a/main/webapp/static/lib/layui/images/face/50.gif b/main/webapp/static/lib/layui/images/face/50.gif new file mode 100644 index 0000000..6e22e7f Binary files /dev/null and b/main/webapp/static/lib/layui/images/face/50.gif differ diff --git a/main/webapp/static/lib/layui/images/face/51.gif b/main/webapp/static/lib/layui/images/face/51.gif new file mode 100644 index 0000000..ad3f4d3 Binary files /dev/null and b/main/webapp/static/lib/layui/images/face/51.gif differ diff --git a/main/webapp/static/lib/layui/images/face/52.gif b/main/webapp/static/lib/layui/images/face/52.gif new file mode 100644 index 0000000..39f8a22 Binary files /dev/null and b/main/webapp/static/lib/layui/images/face/52.gif differ diff --git a/main/webapp/static/lib/layui/images/face/53.gif b/main/webapp/static/lib/layui/images/face/53.gif new file mode 100644 index 0000000..a181ee7 Binary files /dev/null and b/main/webapp/static/lib/layui/images/face/53.gif differ diff --git a/main/webapp/static/lib/layui/images/face/54.gif b/main/webapp/static/lib/layui/images/face/54.gif new file mode 100644 index 0000000..e289d92 Binary files /dev/null and b/main/webapp/static/lib/layui/images/face/54.gif differ diff --git a/main/webapp/static/lib/layui/images/face/55.gif b/main/webapp/static/lib/layui/images/face/55.gif new file mode 100644 index 0000000..4351083 Binary files /dev/null and b/main/webapp/static/lib/layui/images/face/55.gif differ diff --git a/main/webapp/static/lib/layui/images/face/56.gif b/main/webapp/static/lib/layui/images/face/56.gif new file mode 100644 index 0000000..e0eff22 Binary files /dev/null and b/main/webapp/static/lib/layui/images/face/56.gif differ diff --git a/main/webapp/static/lib/layui/images/face/57.gif b/main/webapp/static/lib/layui/images/face/57.gif new file mode 100644 index 0000000..0bf130f Binary files /dev/null and b/main/webapp/static/lib/layui/images/face/57.gif differ diff --git a/main/webapp/static/lib/layui/images/face/58.gif b/main/webapp/static/lib/layui/images/face/58.gif new file mode 100644 index 0000000..0f06508 Binary files /dev/null and b/main/webapp/static/lib/layui/images/face/58.gif differ diff --git a/main/webapp/static/lib/layui/images/face/59.gif b/main/webapp/static/lib/layui/images/face/59.gif new file mode 100644 index 0000000..7081e4f Binary files /dev/null and b/main/webapp/static/lib/layui/images/face/59.gif differ diff --git a/main/webapp/static/lib/layui/images/face/6.gif b/main/webapp/static/lib/layui/images/face/6.gif new file mode 100644 index 0000000..f7715bf Binary files /dev/null and b/main/webapp/static/lib/layui/images/face/6.gif differ diff --git a/main/webapp/static/lib/layui/images/face/60.gif b/main/webapp/static/lib/layui/images/face/60.gif new file mode 100644 index 0000000..6e15f89 Binary files /dev/null and b/main/webapp/static/lib/layui/images/face/60.gif differ diff --git a/main/webapp/static/lib/layui/images/face/61.gif b/main/webapp/static/lib/layui/images/face/61.gif new file mode 100644 index 0000000..f092d7e Binary files /dev/null and b/main/webapp/static/lib/layui/images/face/61.gif differ diff --git a/main/webapp/static/lib/layui/images/face/62.gif b/main/webapp/static/lib/layui/images/face/62.gif new file mode 100644 index 0000000..7fe4984 Binary files /dev/null and b/main/webapp/static/lib/layui/images/face/62.gif differ diff --git a/main/webapp/static/lib/layui/images/face/63.gif b/main/webapp/static/lib/layui/images/face/63.gif new file mode 100644 index 0000000..cf8e23e Binary files /dev/null and b/main/webapp/static/lib/layui/images/face/63.gif differ diff --git a/main/webapp/static/lib/layui/images/face/64.gif b/main/webapp/static/lib/layui/images/face/64.gif new file mode 100644 index 0000000..a779719 Binary files /dev/null and b/main/webapp/static/lib/layui/images/face/64.gif differ diff --git a/main/webapp/static/lib/layui/images/face/65.gif b/main/webapp/static/lib/layui/images/face/65.gif new file mode 100644 index 0000000..7bb98f2 Binary files /dev/null and b/main/webapp/static/lib/layui/images/face/65.gif differ diff --git a/main/webapp/static/lib/layui/images/face/66.gif b/main/webapp/static/lib/layui/images/face/66.gif new file mode 100644 index 0000000..bb6d077 Binary files /dev/null and b/main/webapp/static/lib/layui/images/face/66.gif differ diff --git a/main/webapp/static/lib/layui/images/face/67.gif b/main/webapp/static/lib/layui/images/face/67.gif new file mode 100644 index 0000000..6e33f7c Binary files /dev/null and b/main/webapp/static/lib/layui/images/face/67.gif differ diff --git a/main/webapp/static/lib/layui/images/face/68.gif b/main/webapp/static/lib/layui/images/face/68.gif new file mode 100644 index 0000000..1a6c400 Binary files /dev/null and b/main/webapp/static/lib/layui/images/face/68.gif differ diff --git a/main/webapp/static/lib/layui/images/face/69.gif b/main/webapp/static/lib/layui/images/face/69.gif new file mode 100644 index 0000000..a02f0b2 Binary files /dev/null and b/main/webapp/static/lib/layui/images/face/69.gif differ diff --git a/main/webapp/static/lib/layui/images/face/7.gif b/main/webapp/static/lib/layui/images/face/7.gif new file mode 100644 index 0000000..e6d4db8 Binary files /dev/null and b/main/webapp/static/lib/layui/images/face/7.gif differ diff --git a/main/webapp/static/lib/layui/images/face/70.gif b/main/webapp/static/lib/layui/images/face/70.gif new file mode 100644 index 0000000..416c5c1 Binary files /dev/null and b/main/webapp/static/lib/layui/images/face/70.gif differ diff --git a/main/webapp/static/lib/layui/images/face/71.gif b/main/webapp/static/lib/layui/images/face/71.gif new file mode 100644 index 0000000..c17d60c Binary files /dev/null and b/main/webapp/static/lib/layui/images/face/71.gif differ diff --git a/main/webapp/static/lib/layui/images/face/8.gif b/main/webapp/static/lib/layui/images/face/8.gif new file mode 100644 index 0000000..66f967b Binary files /dev/null and b/main/webapp/static/lib/layui/images/face/8.gif differ diff --git a/main/webapp/static/lib/layui/images/face/9.gif b/main/webapp/static/lib/layui/images/face/9.gif new file mode 100644 index 0000000..6044740 Binary files /dev/null and b/main/webapp/static/lib/layui/images/face/9.gif differ diff --git a/main/webapp/static/lib/layui/lay/modules/carousel.js b/main/webapp/static/lib/layui/lay/modules/carousel.js new file mode 100644 index 0000000..54b5cbc --- /dev/null +++ b/main/webapp/static/lib/layui/lay/modules/carousel.js @@ -0,0 +1,2 @@ +/** layui-v2.2.6 MIT License By https://www.layui.com */ + ;layui.define("jquery",function(e){"use strict";var i=layui.$,n=(layui.hint(),layui.device(),{config:{},set:function(e){var n=this;return n.config=i.extend({},n.config,e),n},on:function(e,i){return layui.onevent.call(this,t,e,i)}}),t="carousel",a="layui-this",l=">*[carousel-item]>*",o="layui-carousel-left",r="layui-carousel-right",d="layui-carousel-prev",s="layui-carousel-next",u="layui-carousel-arrow",c="layui-carousel-ind",m=function(e){var t=this;t.config=i.extend({},t.config,n.config,e),t.render()};m.prototype.config={width:"600px",height:"280px",full:!1,arrow:"hover",indicator:"inside",autoplay:!0,interval:3e3,anim:"",trigger:"click",index:0},m.prototype.render=function(){var e=this,n=e.config;n.elem=i(n.elem),n.elem[0]&&(e.elemItem=n.elem.find(l),n.index<0&&(n.index=0),n.index>=e.elemItem.length&&(n.index=e.elemItem.length-1),n.interval<800&&(n.interval=800),n.full?n.elem.css({position:"fixed",width:"100%",height:"100%",zIndex:9999}):n.elem.css({width:n.width,height:n.height}),n.elem.attr("lay-anim",n.anim),e.elemItem.eq(n.index).addClass(a),e.elemItem.length<=1||(e.indicator(),e.arrow(),e.autoplay(),e.events()))},m.prototype.reload=function(e){var n=this;clearInterval(n.timer),n.config=i.extend({},n.config,e),n.render()},m.prototype.prevIndex=function(){var e=this,i=e.config,n=i.index-1;return n<0&&(n=e.elemItem.length-1),n},m.prototype.nextIndex=function(){var e=this,i=e.config,n=i.index+1;return n>=e.elemItem.length&&(n=0),n},m.prototype.addIndex=function(e){var i=this,n=i.config;e=e||1,n.index=n.index+e,n.index>=i.elemItem.length&&(n.index=0)},m.prototype.subIndex=function(e){var i=this,n=i.config;e=e||1,n.index=n.index-e,n.index<0&&(n.index=i.elemItem.length-1)},m.prototype.autoplay=function(){var e=this,i=e.config;i.autoplay&&(e.timer=setInterval(function(){e.slide()},i.interval))},m.prototype.arrow=function(){var e=this,n=e.config,t=i(['",'"].join(""));n.elem.attr("lay-arrow",n.arrow),n.elem.find("."+u)[0]&&n.elem.find("."+u).remove(),n.elem.append(t),t.on("click",function(){var n=i(this),t=n.attr("lay-type");e.slide(t)})},m.prototype.indicator=function(){var e=this,n=e.config,t=e.elemInd=i(['
    ',function(){var i=[];return layui.each(e.elemItem,function(e){i.push("")}),i.join("")}(),"
"].join(""));n.elem.attr("lay-indicator",n.indicator),n.elem.find("."+c)[0]&&n.elem.find("."+c).remove(),n.elem.append(t),"updown"===n.anim&&t.css("margin-top",-(t.height()/2)),t.find("li").on("hover"===n.trigger?"mouseover":n.trigger,function(){var t=i(this),a=t.index();a>n.index?e.slide("add",a-n.index):a/g,">").replace(/'/g,"'").replace(/"/g,""")),c.html('
  1. '+o.replace(/[\r\t\n]+/g,"
  2. ")+"
"),c.find(">.layui-code-h3")[0]||c.prepend('

'+(c.attr("lay-title")||e.title||"code")+(e.about?'layui.code':"")+"

");var d=c.find(">.layui-code-ol");c.addClass("layui-box layui-code-view"),(c.attr("lay-skin")||e.skin)&&c.addClass("layui-code-"+(c.attr("lay-skin")||e.skin)),(d.find("li").length/100|0)>0&&d.css("margin-left",(d.find("li").length/100|0)+"px"),(c.attr("lay-height")||e.height)&&d.css("max-height",c.attr("lay-height")||e.height)})})}).addcss("modules/code.css","skincodecss"); \ No newline at end of file diff --git a/main/webapp/static/lib/layui/lay/modules/element.js b/main/webapp/static/lib/layui/lay/modules/element.js new file mode 100644 index 0000000..53f6b38 --- /dev/null +++ b/main/webapp/static/lib/layui/lay/modules/element.js @@ -0,0 +1,2 @@ +/** layui-v2.2.6 MIT License By https://www.layui.com */ + ;layui.define("jquery",function(t){"use strict";var a=layui.$,i=(layui.hint(),layui.device()),e="element",l="layui-this",n="layui-show",s=function(){this.config={}};s.prototype.set=function(t){var i=this;return a.extend(!0,i.config,t),i},s.prototype.on=function(t,a){return layui.onevent.call(this,e,t,a)},s.prototype.tabAdd=function(t,i){var e=".layui-tab-title",l=a(".layui-tab[lay-filter="+t+"]"),n=l.children(e),s=n.children(".layui-tab-bar"),o=l.children(".layui-tab-content"),r='
  • "+(i.title||"unnaming")+"
  • ";return s[0]?s.before(r):n.append(r),o.append('
    '+(i.content||"")+"
    "),f.hideTabMore(!0),f.tabAuto(),this},s.prototype.tabDelete=function(t,i){var e=".layui-tab-title",l=a(".layui-tab[lay-filter="+t+"]"),n=l.children(e),s=n.find('>li[lay-id="'+i+'"]');return f.tabDelete(null,s),this},s.prototype.tabChange=function(t,i){var e=".layui-tab-title",l=a(".layui-tab[lay-filter="+t+"]"),n=l.children(e),s=n.find('>li[lay-id="'+i+'"]');return f.tabClick.call(s[0],null,null,s),this},s.prototype.tab=function(t){t=t||{},b.on("click",t.headerElem,function(i){var e=a(this).index();f.tabClick.call(this,i,e,null,t)})},s.prototype.progress=function(t,i){var e="layui-progress",l=a("."+e+"[lay-filter="+t+"]"),n=l.find("."+e+"-bar"),s=n.find("."+e+"-text");return n.css("width",i),s.text(i),this};var o=".layui-nav",r="layui-nav-item",c="layui-nav-bar",u="layui-nav-tree",d="layui-nav-child",y="layui-nav-more",h="layui-anim layui-anim-upbit",f={tabClick:function(t,i,s,o){o=o||{};var r=s||a(this),i=i||r.parent().children("li").index(r),c=o.headerElem?r.parent():r.parents(".layui-tab").eq(0),u=o.bodyElem?a(o.bodyElem):c.children(".layui-tab-content").children(".layui-tab-item"),d=r.find("a"),y=c.attr("lay-filter");"javascript:;"!==d.attr("href")&&"_blank"===d.attr("target")||(r.addClass(l).siblings().removeClass(l),u.eq(i).addClass(n).siblings().removeClass(n)),layui.event.call(this,e,"tab("+y+")",{elem:c,index:i})},tabDelete:function(t,i){var n=i||a(this).parent(),s=n.index(),o=n.parents(".layui-tab").eq(0),r=o.children(".layui-tab-content").children(".layui-tab-item"),c=o.attr("lay-filter");n.hasClass(l)&&(n.next()[0]?f.tabClick.call(n.next()[0],null,s+1):n.prev()[0]&&f.tabClick.call(n.prev()[0],null,s-1)),n.remove(),r.eq(s).remove(),setTimeout(function(){f.tabAuto()},50),layui.event.call(this,e,"tabDelete("+c+")",{elem:o,index:s})},tabAuto:function(){var t="layui-tab-more",e="layui-tab-bar",l="layui-tab-close",n=this;a(".layui-tab").each(function(){var s=a(this),o=s.children(".layui-tab-title"),r=(s.children(".layui-tab-content").children(".layui-tab-item"),'lay-stope="tabmore"'),c=a('');if(n===window&&8!=i.ie&&f.hideTabMore(!0),s.attr("lay-allowClose")&&o.find("li").each(function(){var t=a(this);if(!t.find("."+l)[0]){var i=a('');i.on("click",f.tabDelete),t.append(i)}}),"string"!=typeof s.attr("lay-unauto"))if(o.prop("scrollWidth")>o.outerWidth()+1){if(o.find("."+e)[0])return;o.append(c),s.attr("overflow",""),c.on("click",function(a){o[this.title?"removeClass":"addClass"](t),this.title=this.title?"":"收缩"})}else o.find("."+e).remove(),s.removeAttr("overflow")})},hideTabMore:function(t){var i=a(".layui-tab-title");t!==!0&&"tabmore"===a(t.target).attr("lay-stope")||(i.removeClass("layui-tab-more"),i.find(".layui-tab-bar").attr("title",""))},clickThis:function(){var t=a(this),i=t.parents(o),n=i.attr("lay-filter"),s=t.parent(),c=t.siblings("."+d),y="string"==typeof s.attr("lay-unselect");"javascript:;"!==t.attr("href")&&"_blank"===t.attr("target")||y||c[0]||(i.find("."+l).removeClass(l),s.addClass(l)),i.hasClass(u)&&(c.removeClass(h),c[0]&&(s["none"===c.css("display")?"addClass":"removeClass"](r+"ed"),"all"===i.attr("lay-shrink")&&s.siblings().removeClass(r+"ed"))),layui.event.call(this,e,"nav("+n+")",t)},collapse:function(){var t=a(this),i=t.find(".layui-colla-icon"),l=t.siblings(".layui-colla-content"),s=t.parents(".layui-collapse").eq(0),o=s.attr("lay-filter"),r="none"===l.css("display");if("string"==typeof s.attr("lay-accordion")){var c=s.children(".layui-colla-item").children("."+n);c.siblings(".layui-colla-title").children(".layui-colla-icon").html(""),c.removeClass(n)}l[r?"addClass":"removeClass"](n),i.html(r?"":""),layui.event.call(this,e,"collapse("+o+")",{title:t,content:l,show:r})}};s.prototype.init=function(t,e){var l=function(){return e?'[lay-filter="'+e+'"]':""}(),s={tab:function(){f.tabAuto.call({})},nav:function(){var t=200,e={},s={},p={},b=function(l,o,r){var c=a(this),f=c.find("."+d);o.hasClass(u)?l.css({top:c.position().top,height:c.children("a").outerHeight(),opacity:1}):(f.addClass(h),l.css({left:c.position().left+parseFloat(c.css("marginLeft")),top:c.position().top+c.height()-l.height()}),e[r]=setTimeout(function(){l.css({width:c.width(),opacity:1})},i.ie&&i.ie<10?0:t),clearTimeout(p[r]),"block"===f.css("display")&&clearTimeout(s[r]),s[r]=setTimeout(function(){f.addClass(n),c.find("."+y).addClass(y+"d")},300))};a(o+l).each(function(i){var l=a(this),o=a(''),h=l.find("."+r);l.find("."+c)[0]||(l.append(o),h.on("mouseenter",function(){b.call(this,o,l,i)}).on("mouseleave",function(){l.hasClass(u)||(clearTimeout(s[i]),s[i]=setTimeout(function(){l.find("."+d).removeClass(n),l.find("."+y).removeClass(y+"d")},300))}),l.on("mouseleave",function(){clearTimeout(e[i]),p[i]=setTimeout(function(){l.hasClass(u)?o.css({height:0,top:o.position().top+o.height()/2,opacity:0}):o.css({width:0,left:o.position().left+o.width()/2,opacity:0})},t)})),h.find("a").each(function(){var t=a(this),i=(t.parent(),t.siblings("."+d));i[0]&&!t.children("."+y)[0]&&t.append(''),t.off("click",f.clickThis).on("click",f.clickThis)})})},breadcrumb:function(){var t=".layui-breadcrumb";a(t+l).each(function(){var t=a(this),i="lay-separator",e=t.attr(i)||"/",l=t.find("a");l.next("span["+i+"]")[0]||(l.each(function(t){t!==l.length-1&&a(this).after(""+e+"")}),t.css("visibility","visible"))})},progress:function(){var t="layui-progress";a("."+t+l).each(function(){var i=a(this),e=i.find(".layui-progress-bar"),l=e.attr("lay-percent");e.css("width",function(){return/^.+\/.+$/.test(l)?100*new Function("return "+l)()+"%":l}()),i.attr("lay-showPercent")&&setTimeout(function(){e.html(''+l+"")},350)})},collapse:function(){var t="layui-collapse";a("."+t+l).each(function(){var t=a(this).find(".layui-colla-item");t.each(function(){var t=a(this),i=t.find(".layui-colla-title"),e=t.find(".layui-colla-content"),l="none"===e.css("display");i.find(".layui-colla-icon").remove(),i.append(''+(l?"":"")+""),i.off("click",f.collapse).on("click",f.collapse)})})}};return s[t]?s[t]():layui.each(s,function(t,a){a()})},s.prototype.render=s.prototype.init;var p=new s,b=a(document);p.render();var v=".layui-tab-title li";b.on("click",v,f.tabClick),b.on("click",f.hideTabMore),a(window).on("resize",f.tabAuto),t(e,p)}); \ No newline at end of file diff --git a/main/webapp/static/lib/layui/lay/modules/flow.js b/main/webapp/static/lib/layui/lay/modules/flow.js new file mode 100644 index 0000000..64afe93 --- /dev/null +++ b/main/webapp/static/lib/layui/lay/modules/flow.js @@ -0,0 +1,2 @@ +/** layui-v2.2.6 MIT License By https://www.layui.com */ + ;layui.define("jquery",function(e){"use strict";var l=layui.$,o=function(e){},t='';o.prototype.load=function(e){var o,i,n,r,a=this,c=0;e=e||{};var f=l(e.elem);if(f[0]){var m=l(e.scrollElem||document),u=e.mb||50,s=!("isAuto"in e)||e.isAuto,v=e.end||"没有更多了",y=e.scrollElem&&e.scrollElem!==document,d="加载更多",h=l('");f.find(".layui-flow-more")[0]||f.append(h);var p=function(e,t){e=l(e),h.before(e),t=0==t||null,t?h.html(v):h.find("a").html(d),i=t,o=null,n&&n()},g=function(){o=!0,h.find("a").html(t),"function"==typeof e.done&&e.done(++c,p)};if(g(),h.find("a").on("click",function(){l(this);i||o||g()}),e.isLazyimg)var n=a.lazyimg({elem:e.elem+" img",scrollElem:e.scrollElem});return s?(m.on("scroll",function(){var e=l(this),t=e.scrollTop();r&&clearTimeout(r),i||(r=setTimeout(function(){var i=y?e.height():l(window).height(),n=y?e.prop("scrollHeight"):document.documentElement.scrollHeight;n-t-i<=u&&(o||g())},100))}),a):a}},o.prototype.lazyimg=function(e){var o,t=this,i=0;e=e||{};var n=l(e.scrollElem||document),r=e.elem||"img",a=e.scrollElem&&e.scrollElem!==document,c=function(e,l){var o=n.scrollTop(),r=o+l,c=a?function(){return e.offset().top-n.offset().top+o}():e.offset().top;if(c>=o&&c<=r&&!e.attr("src")){var m=e.attr("lay-src");layui.img(m,function(){var l=t.lazyimg.elem.eq(i);e.attr("src",m).removeAttr("lay-src"),l[0]&&f(l),i++})}},f=function(e,o){var f=a?(o||n).height():l(window).height(),m=n.scrollTop(),u=m+f;if(t.lazyimg.elem=l(r),e)c(e,f);else for(var s=0;su)break}};if(f(),!o){var m;n.on("scroll",function(){var e=l(this);m&&clearTimeout(m),m=setTimeout(function(){f(null,e)},50)}),o=!0}return f},e("flow",new o)}); \ No newline at end of file diff --git a/main/webapp/static/lib/layui/lay/modules/form.js b/main/webapp/static/lib/layui/lay/modules/form.js new file mode 100644 index 0000000..3fa3c72 --- /dev/null +++ b/main/webapp/static/lib/layui/lay/modules/form.js @@ -0,0 +1,2 @@ +/** layui-v2.2.6 MIT License By https://www.layui.com */ + ;layui.define("layer",function(e){"use strict";var t=layui.$,i=layui.layer,a=layui.hint(),n=layui.device(),l="form",r=".layui-form",s="layui-this",o="layui-hide",u="layui-disabled",c=function(){this.config={verify:{required:[/[\S]+/,"必填项不能为空"],phone:[/^1\d{10}$/,"请输入正确的手机号"],email:[/^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/,"邮箱格式不正确"],url:[/(^#)|(^http(s*):\/\/[^\s]+\.[^\s]+)/,"链接格式不正确"],number:function(e){if(!e||isNaN(e))return"只能填写数字"},date:[/^(\d{4})[-\/](\d{1}|0\d{1}|1[0-2])([-\/](\d{1}|0\d{1}|[1-2][0-9]|3[0-1]))*$/,"日期格式不正确"],identity:[/(^\d{15}$)|(^\d{17}(x|X|\d)$)/,"请输入正确的身份证号"]}}};c.prototype.set=function(e){var i=this;return t.extend(!0,i.config,e),i},c.prototype.verify=function(e){var i=this;return t.extend(!0,i.config.verify,e),i},c.prototype.on=function(e,t){return layui.onevent.call(this,l,e,t)},c.prototype.render=function(e,i){var n=this,c=t(r+function(){return i?'[lay-filter="'+i+'"]':""}()),d={select:function(){var e,i="请选择",a="layui-form-select",n="layui-select-title",r="layui-select-none",d="",f=c.find("select"),y=function(i,l){t(i.target).parent().hasClass(n)&&!l||(t("."+a).removeClass(a+"ed "+a+"up"),e&&d&&e.val(d)),e=null},h=function(i,c,f){var h=t(this),p=i.find("."+n),m=p.find("input"),k=i.find("dl"),g=k.children("dd");if(!c){var x=function(){var e=i.offset().top+i.outerHeight()+5-v.scrollTop(),t=k.outerHeight();i.addClass(a+"ed"),g.removeClass(o),e+t>v.height()&&e>=t&&i.addClass(a+"up")},b=function(e){i.removeClass(a+"ed "+a+"up"),m.blur(),e||C(m.val(),function(e){e&&(d=k.find("."+s).html(),m&&m.val(d))})};p.on("click",function(e){i.hasClass(a+"ed")?b():(y(e,!0),x()),k.find("."+r).remove()}),p.find(".layui-edge").on("click",function(){m.focus()}),m.on("keyup",function(e){var t=e.keyCode;9===t&&x()}).on("keydown",function(e){var t=e.keyCode;9===t?b():13===t&&e.preventDefault()});var C=function(e,i,a){var n=0;layui.each(g,function(){var i=t(this),l=i.text(),r=l.indexOf(e)===-1;(""===e||"blur"===a?e!==l:r)&&n++,"keyup"===a&&i[r?"addClass":"removeClass"](o)});var l=n===g.length;return i(l),l},w=function(e){var t=this.value,i=e.keyCode;return 9!==i&&13!==i&&37!==i&&38!==i&&39!==i&&40!==i&&(C(t,function(e){e?k.find("."+r)[0]||k.append('

    无匹配项

    '):k.find("."+r).remove()},"keyup"),void(""===t&&k.find("."+r).remove()))};f&&m.on("keyup",w).on("blur",function(t){e=m,d=k.find("."+s).html(),setTimeout(function(){C(m.val(),function(e){d||m.val("")},"blur")},200)}),g.on("click",function(){var e=t(this),a=e.attr("lay-value"),n=h.attr("lay-filter");return!e.hasClass(u)&&(e.hasClass("layui-select-tips")?m.val(""):(m.val(e.text()),e.addClass(s)),e.siblings().removeClass(s),h.val(a).removeClass("layui-form-danger"),layui.event.call(this,l,"select("+n+")",{elem:h[0],value:a,othis:i}),b(!0),!1)}),i.find("dl>dt").on("click",function(e){return!1}),t(document).off("click",y).on("click",y)}};f.each(function(e,l){var r=t(this),o=r.next("."+a),c=this.disabled,d=l.value,f=t(l.options[l.selectedIndex]),y=l.options[0];if("string"==typeof r.attr("lay-ignore"))return r.show();var v="string"==typeof r.attr("lay-search"),p=y?y.value?i:y.innerHTML||i:i,m=t(['
    ','
    ','
    ','
    '+function(e){var t=[];return layui.each(e,function(e,a){0!==e||a.value?"optgroup"===a.tagName.toLowerCase()?t.push("
    "+a.label+"
    "):t.push('
    '+a.innerHTML+"
    "):t.push('
    '+(a.innerHTML||i)+"
    ")}),0===t.length&&t.push('
    没有选项
    '),t.join("")}(r.find("*"))+"
    ","
    "].join(""));o[0]&&o.remove(),r.after(m),h.call(this,m,c,v)})},checkbox:function(){var e={checkbox:["layui-form-checkbox","layui-form-checked","checkbox"],_switch:["layui-form-switch","layui-form-onswitch","switch"]},i=c.find("input[type=checkbox]"),a=function(e,i){var a=t(this);e.on("click",function(){var t=a.attr("lay-filter"),n=(a.attr("lay-text")||"").split("|");a[0].disabled||(a[0].checked?(a[0].checked=!1,e.removeClass(i[1]).find("em").text(n[1])):(a[0].checked=!0,e.addClass(i[1]).find("em").text(n[0])),layui.event.call(a[0],l,i[2]+"("+t+")",{elem:a[0],value:a[0].value,othis:e}))})};i.each(function(i,n){var l=t(this),r=l.attr("lay-skin"),s=(l.attr("lay-text")||"").split("|"),o=this.disabled;"switch"===r&&(r="_"+r);var c=e[r]||e.checkbox;if("string"==typeof l.attr("lay-ignore"))return l.show();var d=l.next("."+c[0]),f=t(['
    ',{_switch:""+((n.checked?s[0]:s[1])||"")+""}[r]||(n.title.replace(/\s/g,"")?""+n.title+"":"")+''+(r?"":"")+"","
    "].join(""));d[0]&&d.remove(),l.after(f),a.call(this,f,c)})},radio:function(){var e="layui-form-radio",i=["",""],a=c.find("input[type=radio]"),n=function(a){var n=t(this),s="layui-anim-scaleSpring";a.on("click",function(){var o=n[0].name,u=n.parents(r),c=n.attr("lay-filter"),d=u.find("input[name="+o.replace(/(\.|#|\[|\])/g,"\\$1")+"]");n[0].disabled||(layui.each(d,function(){var a=t(this).next("."+e);this.checked=!1,a.removeClass(e+"ed"),a.find(".layui-icon").removeClass(s).html(i[1])}),n[0].checked=!0,a.addClass(e+"ed"),a.find(".layui-icon").addClass(s).html(i[0]),layui.event.call(n[0],l,"radio("+c+")",{elem:n[0],value:n[0].value,othis:a}))})};a.each(function(a,l){var r=t(this),s=r.next("."+e),o=this.disabled;if("string"==typeof r.attr("lay-ignore"))return r.show();s[0]&&s.remove();var c=t(['
    ',''+i[l.checked?0:1]+"","
    "+function(){var e=l.title||"";return"string"==typeof r.next().attr("lay-radio")&&(e=r.next().html(),r.next().remove()),e}()+"
    ","
    "].join(""));r.after(c),n.call(this,c)})}};return e?d[e]?d[e]():a.error("不支持的"+e+"表单渲染"):layui.each(d,function(e,t){t()}),n};var d=function(){var e=t(this),a=f.config.verify,s=null,o="layui-form-danger",u={},c=e.parents(r),d=c.find("*[lay-verify]"),y=e.parents("form")[0],v=c.find("input,select,textarea"),h=e.attr("lay-filter");if(layui.each(d,function(e,l){var r=t(this),u=r.attr("lay-verify").split("|"),c=r.attr("lay-verType"),d=r.val();if(r.removeClass(o),layui.each(u,function(e,t){var u,f="",y="function"==typeof a[t];if(a[t]){var u=y?f=a[t](d,l):!a[t][0].test(d);if(f=f||a[t][1],u)return"tips"===c?i.tips(f,function(){return"string"==typeof r.attr("lay-ignore")||"select"!==l.tagName.toLowerCase()&&!/^checkbox|radio$/.test(l.type)?r:r.next()}(),{tips:1}):"alert"===c?i.alert(f,{title:"提示",shadeClose:!0}):i.msg(f,{icon:5,shift:6}),n.android||n.ios||l.focus(),r.addClass(o),s=!0}}),s)return s}),s)return!1;var p={};return layui.each(v,function(e,t){if(t.name=(t.name||"").replace(/^\s*|\s*&/,""),t.name){if(/^.*\[\]$/.test(t.name)){var i=t.name.match(/^(.*)\[\]$/g)[0];p[i]=0|p[i],t.name=t.name.replace(/^(.*)\[\]$/,"$1["+p[i]++ +"]")}/^checkbox|radio$/.test(t.type)&&!t.checked||(u[t.name]=t.value)}}),layui.event.call(this,l,"submit("+h+")",{elem:this,form:y,field:u})},f=new c,y=t(document),v=t(window);f.render(),y.on("reset",r,function(){var e=t(this).attr("lay-filter");setTimeout(function(){f.render(null,e)},50)}),y.on("submit",r,d).on("click","*[lay-submit]",d),e(l,f)}); \ No newline at end of file diff --git a/main/webapp/static/lib/layui/lay/modules/jquery.js b/main/webapp/static/lib/layui/lay/modules/jquery.js new file mode 100644 index 0000000..7e9c9fa --- /dev/null +++ b/main/webapp/static/lib/layui/lay/modules/jquery.js @@ -0,0 +1,5 @@ +/** layui-v2.2.6 MIT License By https://www.layui.com */ + ;!function(e,t){"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(e,t){function n(e){var t=!!e&&"length"in e&&e.length,n=pe.type(e);return"function"!==n&&!pe.isWindow(e)&&("array"===n||0===t||"number"==typeof t&&t>0&&t-1 in e)}function r(e,t,n){if(pe.isFunction(t))return pe.grep(e,function(e,r){return!!t.call(e,r,e)!==n});if(t.nodeType)return pe.grep(e,function(e){return e===t!==n});if("string"==typeof t){if(Ce.test(t))return pe.filter(t,e,n);t=pe.filter(t,e)}return pe.grep(e,function(e){return pe.inArray(e,t)>-1!==n})}function i(e,t){do e=e[t];while(e&&1!==e.nodeType);return e}function o(e){var t={};return pe.each(e.match(De)||[],function(e,n){t[n]=!0}),t}function a(){re.addEventListener?(re.removeEventListener("DOMContentLoaded",s),e.removeEventListener("load",s)):(re.detachEvent("onreadystatechange",s),e.detachEvent("onload",s))}function s(){(re.addEventListener||"load"===e.event.type||"complete"===re.readyState)&&(a(),pe.ready())}function u(e,t,n){if(void 0===n&&1===e.nodeType){var r="data-"+t.replace(_e,"-$1").toLowerCase();if(n=e.getAttribute(r),"string"==typeof n){try{n="true"===n||"false"!==n&&("null"===n?null:+n+""===n?+n:qe.test(n)?pe.parseJSON(n):n)}catch(i){}pe.data(e,t,n)}else n=void 0}return n}function l(e){var t;for(t in e)if(("data"!==t||!pe.isEmptyObject(e[t]))&&"toJSON"!==t)return!1;return!0}function c(e,t,n,r){if(He(e)){var i,o,a=pe.expando,s=e.nodeType,u=s?pe.cache:e,l=s?e[a]:e[a]&&a;if(l&&u[l]&&(r||u[l].data)||void 0!==n||"string"!=typeof t)return l||(l=s?e[a]=ne.pop()||pe.guid++:a),u[l]||(u[l]=s?{}:{toJSON:pe.noop}),"object"!=typeof t&&"function"!=typeof t||(r?u[l]=pe.extend(u[l],t):u[l].data=pe.extend(u[l].data,t)),o=u[l],r||(o.data||(o.data={}),o=o.data),void 0!==n&&(o[pe.camelCase(t)]=n),"string"==typeof t?(i=o[t],null==i&&(i=o[pe.camelCase(t)])):i=o,i}}function f(e,t,n){if(He(e)){var r,i,o=e.nodeType,a=o?pe.cache:e,s=o?e[pe.expando]:pe.expando;if(a[s]){if(t&&(r=n?a[s]:a[s].data)){pe.isArray(t)?t=t.concat(pe.map(t,pe.camelCase)):t in r?t=[t]:(t=pe.camelCase(t),t=t in r?[t]:t.split(" ")),i=t.length;for(;i--;)delete r[t[i]];if(n?!l(r):!pe.isEmptyObject(r))return}(n||(delete a[s].data,l(a[s])))&&(o?pe.cleanData([e],!0):fe.deleteExpando||a!=a.window?delete a[s]:a[s]=void 0)}}}function d(e,t,n,r){var i,o=1,a=20,s=r?function(){return r.cur()}:function(){return pe.css(e,t,"")},u=s(),l=n&&n[3]||(pe.cssNumber[t]?"":"px"),c=(pe.cssNumber[t]||"px"!==l&&+u)&&Me.exec(pe.css(e,t));if(c&&c[3]!==l){l=l||c[3],n=n||[],c=+u||1;do o=o||".5",c/=o,pe.style(e,t,c+l);while(o!==(o=s()/u)&&1!==o&&--a)}return n&&(c=+c||+u||0,i=n[1]?c+(n[1]+1)*n[2]:+n[2],r&&(r.unit=l,r.start=c,r.end=i)),i}function p(e){var t=ze.split("|"),n=e.createDocumentFragment();if(n.createElement)for(;t.length;)n.createElement(t.pop());return n}function h(e,t){var n,r,i=0,o="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):void 0;if(!o)for(o=[],n=e.childNodes||e;null!=(r=n[i]);i++)!t||pe.nodeName(r,t)?o.push(r):pe.merge(o,h(r,t));return void 0===t||t&&pe.nodeName(e,t)?pe.merge([e],o):o}function g(e,t){for(var n,r=0;null!=(n=e[r]);r++)pe._data(n,"globalEval",!t||pe._data(t[r],"globalEval"))}function m(e){Be.test(e.type)&&(e.defaultChecked=e.checked)}function y(e,t,n,r,i){for(var o,a,s,u,l,c,f,d=e.length,y=p(t),v=[],x=0;x"!==f[1]||Ve.test(a)?0:u:u.firstChild,o=a&&a.childNodes.length;o--;)pe.nodeName(c=a.childNodes[o],"tbody")&&!c.childNodes.length&&a.removeChild(c);for(pe.merge(v,u.childNodes),u.textContent="";u.firstChild;)u.removeChild(u.firstChild);u=y.lastChild}else v.push(t.createTextNode(a));for(u&&y.removeChild(u),fe.appendChecked||pe.grep(h(v,"input"),m),x=0;a=v[x++];)if(r&&pe.inArray(a,r)>-1)i&&i.push(a);else if(s=pe.contains(a.ownerDocument,a),u=h(y.appendChild(a),"script"),s&&g(u),n)for(o=0;a=u[o++];)Ie.test(a.type||"")&&n.push(a);return u=null,y}function v(){return!0}function x(){return!1}function b(){try{return re.activeElement}catch(e){}}function w(e,t,n,r,i,o){var a,s;if("object"==typeof t){"string"!=typeof n&&(r=r||n,n=void 0);for(s in t)w(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),i===!1)i=x;else if(!i)return e;return 1===o&&(a=i,i=function(e){return pe().off(e),a.apply(this,arguments)},i.guid=a.guid||(a.guid=pe.guid++)),e.each(function(){pe.event.add(this,t,i,r,n)})}function T(e,t){return pe.nodeName(e,"table")&&pe.nodeName(11!==t.nodeType?t:t.firstChild,"tr")?e.getElementsByTagName("tbody")[0]||e.appendChild(e.ownerDocument.createElement("tbody")):e}function C(e){return e.type=(null!==pe.find.attr(e,"type"))+"/"+e.type,e}function E(e){var t=it.exec(e.type);return t?e.type=t[1]:e.removeAttribute("type"),e}function N(e,t){if(1===t.nodeType&&pe.hasData(e)){var n,r,i,o=pe._data(e),a=pe._data(t,o),s=o.events;if(s){delete a.handle,a.events={};for(n in s)for(r=0,i=s[n].length;r1&&"string"==typeof p&&!fe.checkClone&&rt.test(p))return e.each(function(i){var o=e.eq(i);g&&(t[0]=p.call(this,i,o.html())),S(o,t,n,r)});if(f&&(l=y(t,e[0].ownerDocument,!1,e,r),i=l.firstChild,1===l.childNodes.length&&(l=i),i||r)){for(s=pe.map(h(l,"script"),C),a=s.length;c")).appendTo(t.documentElement),t=(ut[0].contentWindow||ut[0].contentDocument).document,t.write(),t.close(),n=D(e,t),ut.detach()),lt[e]=n),n}function L(e,t){return{get:function(){return e()?void delete this.get:(this.get=t).apply(this,arguments)}}}function H(e){if(e in Et)return e;for(var t=e.charAt(0).toUpperCase()+e.slice(1),n=Ct.length;n--;)if(e=Ct[n]+t,e in Et)return e}function q(e,t){for(var n,r,i,o=[],a=0,s=e.length;a=0&&n=0},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},isPlainObject:function(e){var t;if(!e||"object"!==pe.type(e)||e.nodeType||pe.isWindow(e))return!1;try{if(e.constructor&&!ce.call(e,"constructor")&&!ce.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(n){return!1}if(!fe.ownFirst)for(t in e)return ce.call(e,t);for(t in e);return void 0===t||ce.call(e,t)},type:function(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?ue[le.call(e)]||"object":typeof e},globalEval:function(t){t&&pe.trim(t)&&(e.execScript||function(t){e.eval.call(e,t)})(t)},camelCase:function(e){return e.replace(ge,"ms-").replace(me,ye)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,t){var r,i=0;if(n(e))for(r=e.length;iT.cacheLength&&delete e[t.shift()],e[n+" "]=r}var t=[];return e}function r(e){return e[P]=!0,e}function i(e){var t=H.createElement("div");try{return!!e(t)}catch(n){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function o(e,t){for(var n=e.split("|"),r=n.length;r--;)T.attrHandle[n[r]]=t}function a(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&(~t.sourceIndex||V)-(~e.sourceIndex||V);if(r)return r;if(n)for(;n=n.nextSibling;)if(n===t)return-1;return e?1:-1}function s(e){return function(t){var n=t.nodeName.toLowerCase();return"input"===n&&t.type===e}}function u(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function l(e){return r(function(t){return t=+t,r(function(n,r){for(var i,o=e([],n.length,t),a=o.length;a--;)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}function c(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}function f(){}function d(e){for(var t=0,n=e.length,r="";t1?function(t,n,r){for(var i=e.length;i--;)if(!e[i](t,n,r))return!1;return!0}:e[0]}function g(e,n,r){for(var i=0,o=n.length;i-1&&(r[l]=!(a[l]=f))}}else x=m(x===a?x.splice(h,x.length):x),o?o(null,a,x,u):Q.apply(a,x)})}function v(e){for(var t,n,r,i=e.length,o=T.relative[e[0].type],a=o||T.relative[" "],s=o?1:0,u=p(function(e){return e===t},a,!0),l=p(function(e){return ee(t,e)>-1},a,!0),c=[function(e,n,r){var i=!o&&(r||n!==A)||((t=n).nodeType?u(e,n,r):l(e,n,r));return t=null,i}];s1&&h(c),s>1&&d(e.slice(0,s-1).concat({value:" "===e[s-2].type?"*":""})).replace(se,"$1"),n,s0,o=e.length>0,a=function(r,a,s,u,l){var c,f,d,p=0,h="0",g=r&&[],y=[],v=A,x=r||o&&T.find.TAG("*",l),b=W+=null==v?1:Math.random()||.1,w=x.length;for(l&&(A=a===H||a||l);h!==w&&null!=(c=x[h]);h++){if(o&&c){for(f=0,a||c.ownerDocument===H||(L(c),s=!_);d=e[f++];)if(d(c,a||H,s)){u.push(c);break}l&&(W=b)}i&&((c=!d&&c)&&p--,r&&g.push(c))}if(p+=h,i&&h!==p){for(f=0;d=n[f++];)d(g,y,a,s);if(r){if(p>0)for(;h--;)g[h]||y[h]||(y[h]=G.call(u));y=m(y)}Q.apply(u,y),l&&!r&&y.length>0&&p+n.length>1&&t.uniqueSort(u)}return l&&(W=b,A=v),g};return i?r(a):a}var b,w,T,C,E,N,k,S,A,D,j,L,H,q,_,F,M,O,R,P="sizzle"+1*new Date,B=e.document,W=0,I=0,$=n(),z=n(),X=n(),U=function(e,t){return e===t&&(j=!0),0},V=1<<31,Y={}.hasOwnProperty,J=[],G=J.pop,K=J.push,Q=J.push,Z=J.slice,ee=function(e,t){for(var n=0,r=e.length;n+~]|"+ne+")"+ne+"*"),ce=new RegExp("="+ne+"*([^\\]'\"]*?)"+ne+"*\\]","g"),fe=new RegExp(oe),de=new RegExp("^"+re+"$"),pe={ID:new RegExp("^#("+re+")"),CLASS:new RegExp("^\\.("+re+")"),TAG:new RegExp("^("+re+"|[*])"),ATTR:new RegExp("^"+ie),PSEUDO:new RegExp("^"+oe),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+ne+"*(even|odd|(([+-]|)(\\d*)n|)"+ne+"*(?:([+-]|)"+ne+"*(\\d+)|))"+ne+"*\\)|)","i"),bool:new RegExp("^(?:"+te+")$","i"),needsContext:new RegExp("^"+ne+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+ne+"*((?:-\\d)?\\d*)"+ne+"*\\)|)(?=[^-]|$)","i")},he=/^(?:input|select|textarea|button)$/i,ge=/^h\d$/i,me=/^[^{]+\{\s*\[native \w/,ye=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ve=/[+~]/,xe=/'|\\/g,be=new RegExp("\\\\([\\da-f]{1,6}"+ne+"?|("+ne+")|.)","ig"),we=function(e,t,n){var r="0x"+t-65536;return r!==r||n?t:r<0?String.fromCharCode(r+65536):String.fromCharCode(r>>10|55296,1023&r|56320)},Te=function(){L()};try{Q.apply(J=Z.call(B.childNodes),B.childNodes),J[B.childNodes.length].nodeType}catch(Ce){Q={apply:J.length?function(e,t){K.apply(e,Z.call(t))}:function(e,t){for(var n=e.length,r=0;e[n++]=t[r++];);e.length=n-1}}}w=t.support={},E=t.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return!!t&&"HTML"!==t.nodeName},L=t.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:B;return r!==H&&9===r.nodeType&&r.documentElement?(H=r,q=H.documentElement,_=!E(H),(n=H.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",Te,!1):n.attachEvent&&n.attachEvent("onunload",Te)),w.attributes=i(function(e){return e.className="i",!e.getAttribute("className")}),w.getElementsByTagName=i(function(e){return e.appendChild(H.createComment("")),!e.getElementsByTagName("*").length}),w.getElementsByClassName=me.test(H.getElementsByClassName),w.getById=i(function(e){return q.appendChild(e).id=P,!H.getElementsByName||!H.getElementsByName(P).length}),w.getById?(T.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&_){var n=t.getElementById(e);return n?[n]:[]}},T.filter.ID=function(e){var t=e.replace(be,we);return function(e){return e.getAttribute("id")===t}}):(delete T.find.ID,T.filter.ID=function(e){var t=e.replace(be,we);return function(e){var n="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return n&&n.value===t}}),T.find.TAG=w.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):w.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){for(;n=o[i++];)1===n.nodeType&&r.push(n);return r}return o},T.find.CLASS=w.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&_)return t.getElementsByClassName(e)},M=[],F=[],(w.qsa=me.test(H.querySelectorAll))&&(i(function(e){q.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&F.push("[*^$]="+ne+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||F.push("\\["+ne+"*(?:value|"+te+")"),e.querySelectorAll("[id~="+P+"-]").length||F.push("~="),e.querySelectorAll(":checked").length||F.push(":checked"),e.querySelectorAll("a#"+P+"+*").length||F.push(".#.+[+~]")}),i(function(e){var t=H.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&F.push("name"+ne+"*[*^$|!~]?="),e.querySelectorAll(":enabled").length||F.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),F.push(",.*:")})),(w.matchesSelector=me.test(O=q.matches||q.webkitMatchesSelector||q.mozMatchesSelector||q.oMatchesSelector||q.msMatchesSelector))&&i(function(e){w.disconnectedMatch=O.call(e,"div"),O.call(e,"[s!='']:x"),M.push("!=",oe)}),F=F.length&&new RegExp(F.join("|")),M=M.length&&new RegExp(M.join("|")),t=me.test(q.compareDocumentPosition),R=t||me.test(q.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)for(;t=t.parentNode;)if(t===e)return!0;return!1},U=t?function(e,t){if(e===t)return j=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n?n:(n=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1,1&n||!w.sortDetached&&t.compareDocumentPosition(e)===n?e===H||e.ownerDocument===B&&R(B,e)?-1:t===H||t.ownerDocument===B&&R(B,t)?1:D?ee(D,e)-ee(D,t):0:4&n?-1:1)}:function(e,t){if(e===t)return j=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,s=[e],u=[t];if(!i||!o)return e===H?-1:t===H?1:i?-1:o?1:D?ee(D,e)-ee(D,t):0;if(i===o)return a(e,t);for(n=e;n=n.parentNode;)s.unshift(n);for(n=t;n=n.parentNode;)u.unshift(n);for(;s[r]===u[r];)r++;return r?a(s[r],u[r]):s[r]===B?-1:u[r]===B?1:0},H):H},t.matches=function(e,n){return t(e,null,null,n)},t.matchesSelector=function(e,n){if((e.ownerDocument||e)!==H&&L(e),n=n.replace(ce,"='$1']"),w.matchesSelector&&_&&!X[n+" "]&&(!M||!M.test(n))&&(!F||!F.test(n)))try{var r=O.call(e,n);if(r||w.disconnectedMatch||e.document&&11!==e.document.nodeType)return r}catch(i){}return t(n,H,null,[e]).length>0},t.contains=function(e,t){return(e.ownerDocument||e)!==H&&L(e),R(e,t)},t.attr=function(e,t){(e.ownerDocument||e)!==H&&L(e);var n=T.attrHandle[t.toLowerCase()],r=n&&Y.call(T.attrHandle,t.toLowerCase())?n(e,t,!_):void 0;return void 0!==r?r:w.attributes||!_?e.getAttribute(t):(r=e.getAttributeNode(t))&&r.specified?r.value:null},t.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},t.uniqueSort=function(e){var t,n=[],r=0,i=0;if(j=!w.detectDuplicates,D=!w.sortStable&&e.slice(0),e.sort(U),j){for(;t=e[i++];)t===e[i]&&(r=n.push(i));for(;r--;)e.splice(n[r],1)}return D=null,e},C=t.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=C(e)}else if(3===i||4===i)return e.nodeValue}else for(;t=e[r++];)n+=C(t);return n},T=t.selectors={cacheLength:50,createPseudo:r,match:pe,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(be,we),e[3]=(e[3]||e[4]||e[5]||"").replace(be,we),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||t.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&t.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return pe.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&fe.test(n)&&(t=N(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(be,we).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=$[e+" "];return t||(t=new RegExp("(^|"+ne+")"+e+"("+ne+"|$)"))&&$(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(e,n,r){return function(i){var o=t.attr(i,e);return null==o?"!="===n:!n||(o+="","="===n?o===r:"!="===n?o!==r:"^="===n?r&&0===o.indexOf(r):"*="===n?r&&o.indexOf(r)>-1:"$="===n?r&&o.slice(-r.length)===r:"~="===n?(" "+o.replace(ae," ")+" ").indexOf(r)>-1:"|="===n&&(o===r||o.slice(0,r.length+1)===r+"-"))}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,u){var l,c,f,d,p,h,g=o!==a?"nextSibling":"previousSibling",m=t.parentNode,y=s&&t.nodeName.toLowerCase(),v=!u&&!s,x=!1;if(m){if(o){for(;g;){for(d=t;d=d[g];)if(s?d.nodeName.toLowerCase()===y:1===d.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?m.firstChild:m.lastChild],a&&v){for(d=m,f=d[P]||(d[P]={}),c=f[d.uniqueID]||(f[d.uniqueID]={}), +l=c[e]||[],p=l[0]===W&&l[1],x=p&&l[2],d=p&&m.childNodes[p];d=++p&&d&&d[g]||(x=p=0)||h.pop();)if(1===d.nodeType&&++x&&d===t){c[e]=[W,p,x];break}}else if(v&&(d=t,f=d[P]||(d[P]={}),c=f[d.uniqueID]||(f[d.uniqueID]={}),l=c[e]||[],p=l[0]===W&&l[1],x=p),x===!1)for(;(d=++p&&d&&d[g]||(x=p=0)||h.pop())&&((s?d.nodeName.toLowerCase()!==y:1!==d.nodeType)||!++x||(v&&(f=d[P]||(d[P]={}),c=f[d.uniqueID]||(f[d.uniqueID]={}),c[e]=[W,x]),d!==t)););return x-=i,x===r||x%r===0&&x/r>=0}}},PSEUDO:function(e,n){var i,o=T.pseudos[e]||T.setFilters[e.toLowerCase()]||t.error("unsupported pseudo: "+e);return o[P]?o(n):o.length>1?(i=[e,e,"",n],T.setFilters.hasOwnProperty(e.toLowerCase())?r(function(e,t){for(var r,i=o(e,n),a=i.length;a--;)r=ee(e,i[a]),e[r]=!(t[r]=i[a])}):function(e){return o(e,0,i)}):o}},pseudos:{not:r(function(e){var t=[],n=[],i=k(e.replace(se,"$1"));return i[P]?r(function(e,t,n,r){for(var o,a=i(e,null,r,[]),s=e.length;s--;)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,r,o){return t[0]=e,i(t,null,o,n),t[0]=null,!n.pop()}}),has:r(function(e){return function(n){return t(e,n).length>0}}),contains:r(function(e){return e=e.replace(be,we),function(t){return(t.textContent||t.innerText||C(t)).indexOf(e)>-1}}),lang:r(function(e){return de.test(e||"")||t.error("unsupported lang: "+e),e=e.replace(be,we).toLowerCase(),function(t){var n;do if(n=_?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return n=n.toLowerCase(),n===e||0===n.indexOf(e+"-");while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===q},focus:function(e){return e===H.activeElement&&(!H.hasFocus||H.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!T.pseudos.empty(e)},header:function(e){return ge.test(e.nodeName)},input:function(e){return he.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:l(function(){return[0]}),last:l(function(e,t){return[t-1]}),eq:l(function(e,t,n){return[n<0?n+t:n]}),even:l(function(e,t){for(var n=0;n=0;)e.push(r);return e}),gt:l(function(e,t,n){for(var r=n<0?n+t:n;++r2&&"ID"===(a=o[0]).type&&w.getById&&9===t.nodeType&&_&&T.relative[o[1].type]){if(t=(T.find.ID(a.matches[0].replace(be,we),t)||[])[0],!t)return n;l&&(t=t.parentNode),e=e.slice(o.shift().value.length)}for(i=pe.needsContext.test(e)?0:o.length;i--&&(a=o[i],!T.relative[s=a.type]);)if((u=T.find[s])&&(r=u(a.matches[0].replace(be,we),ve.test(o[0].type)&&c(t.parentNode)||t))){if(o.splice(i,1),e=r.length&&d(o),!e)return Q.apply(n,r),n;break}}return(l||k(e,f))(r,t,!_,n,!t||ve.test(e)&&c(t.parentNode)||t),n},w.sortStable=P.split("").sort(U).join("")===P,w.detectDuplicates=!!j,L(),w.sortDetached=i(function(e){return 1&e.compareDocumentPosition(H.createElement("div"))}),i(function(e){return e.innerHTML="","#"===e.firstChild.getAttribute("href")})||o("type|href|height|width",function(e,t,n){if(!n)return e.getAttribute(t,"type"===t.toLowerCase()?1:2)}),w.attributes&&i(function(e){return e.innerHTML="",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||o("value",function(e,t,n){if(!n&&"input"===e.nodeName.toLowerCase())return e.defaultValue}),i(function(e){return null==e.getAttribute("disabled")})||o(te,function(e,t,n){var r;if(!n)return e[t]===!0?t.toLowerCase():(r=e.getAttributeNode(t))&&r.specified?r.value:null}),t}(e);pe.find=ve,pe.expr=ve.selectors,pe.expr[":"]=pe.expr.pseudos,pe.uniqueSort=pe.unique=ve.uniqueSort,pe.text=ve.getText,pe.isXMLDoc=ve.isXML,pe.contains=ve.contains;var xe=function(e,t,n){for(var r=[],i=void 0!==n;(e=e[t])&&9!==e.nodeType;)if(1===e.nodeType){if(i&&pe(e).is(n))break;r.push(e)}return r},be=function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n},we=pe.expr.match.needsContext,Te=/^<([\w-]+)\s*\/?>(?:<\/\1>|)$/,Ce=/^.[^:#\[\.,]*$/;pe.filter=function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?pe.find.matchesSelector(r,e)?[r]:[]:pe.find.matches(e,pe.grep(t,function(e){return 1===e.nodeType}))},pe.fn.extend({find:function(e){var t,n=[],r=this,i=r.length;if("string"!=typeof e)return this.pushStack(pe(e).filter(function(){for(t=0;t1?pe.unique(n):n),n.selector=this.selector?this.selector+" "+e:e,n},filter:function(e){return this.pushStack(r(this,e||[],!1))},not:function(e){return this.pushStack(r(this,e||[],!0))},is:function(e){return!!r(this,"string"==typeof e&&we.test(e)?pe(e):e||[],!1).length}});var Ee,Ne=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,ke=pe.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||Ee,"string"==typeof e){if(r="<"===e.charAt(0)&&">"===e.charAt(e.length-1)&&e.length>=3?[null,e,null]:Ne.exec(e),!r||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof pe?t[0]:t,pe.merge(this,pe.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:re,!0)),Te.test(r[1])&&pe.isPlainObject(t))for(r in t)pe.isFunction(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}if(i=re.getElementById(r[2]),i&&i.parentNode){if(i.id!==r[2])return Ee.find(e);this.length=1,this[0]=i}return this.context=re,this.selector=e,this}return e.nodeType?(this.context=this[0]=e,this.length=1,this):pe.isFunction(e)?"undefined"!=typeof n.ready?n.ready(e):e(pe):(void 0!==e.selector&&(this.selector=e.selector,this.context=e.context),pe.makeArray(e,this))};ke.prototype=pe.fn,Ee=pe(re);var Se=/^(?:parents|prev(?:Until|All))/,Ae={children:!0,contents:!0,next:!0,prev:!0};pe.fn.extend({has:function(e){var t,n=pe(e,this),r=n.length;return this.filter(function(){for(t=0;t-1:1===n.nodeType&&pe.find.matchesSelector(n,e))){o.push(n);break}return this.pushStack(o.length>1?pe.uniqueSort(o):o)},index:function(e){return e?"string"==typeof e?pe.inArray(this[0],pe(e)):pe.inArray(e.jquery?e[0]:e,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(pe.uniqueSort(pe.merge(this.get(),pe(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),pe.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return xe(e,"parentNode")},parentsUntil:function(e,t,n){return xe(e,"parentNode",n)},next:function(e){return i(e,"nextSibling")},prev:function(e){return i(e,"previousSibling")},nextAll:function(e){return xe(e,"nextSibling")},prevAll:function(e){return xe(e,"previousSibling")},nextUntil:function(e,t,n){return xe(e,"nextSibling",n)},prevUntil:function(e,t,n){return xe(e,"previousSibling",n)},siblings:function(e){return be((e.parentNode||{}).firstChild,e)},children:function(e){return be(e.firstChild)},contents:function(e){return pe.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:pe.merge([],e.childNodes)}},function(e,t){pe.fn[e]=function(n,r){var i=pe.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(i=pe.filter(r,i)),this.length>1&&(Ae[e]||(i=pe.uniqueSort(i)),Se.test(e)&&(i=i.reverse())),this.pushStack(i)}});var De=/\S+/g;pe.Callbacks=function(e){e="string"==typeof e?o(e):pe.extend({},e);var t,n,r,i,a=[],s=[],u=-1,l=function(){for(i=e.once,r=t=!0;s.length;u=-1)for(n=s.shift();++u-1;)a.splice(n,1),n<=u&&u--}),this},has:function(e){return e?pe.inArray(e,a)>-1:a.length>0},empty:function(){return a&&(a=[]),this},disable:function(){return i=s=[],a=n="",this},disabled:function(){return!a},lock:function(){return i=!0,n||c.disable(),this},locked:function(){return!!i},fireWith:function(e,n){return i||(n=n||[],n=[e,n.slice?n.slice():n],s.push(n),t||l()),this},fire:function(){return c.fireWith(this,arguments),this},fired:function(){return!!r}};return c},pe.extend({Deferred:function(e){var t=[["resolve","done",pe.Callbacks("once memory"),"resolved"],["reject","fail",pe.Callbacks("once memory"),"rejected"],["notify","progress",pe.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return pe.Deferred(function(n){pe.each(t,function(t,o){var a=pe.isFunction(e[t])&&e[t];i[o[1]](function(){var e=a&&a.apply(this,arguments);e&&pe.isFunction(e.promise)?e.promise().progress(n.notify).done(n.resolve).fail(n.reject):n[o[0]+"With"](this===r?n.promise():this,a?[e]:arguments)})}),e=null}).promise()},promise:function(e){return null!=e?pe.extend(e,r):r}},i={};return r.pipe=r.then,pe.each(t,function(e,o){var a=o[2],s=o[3];r[o[1]]=a.add,s&&a.add(function(){n=s},t[1^e][2].disable,t[2][2].lock),i[o[0]]=function(){return i[o[0]+"With"](this===i?r:this,arguments),this},i[o[0]+"With"]=a.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t,n,r,i=0,o=ie.call(arguments),a=o.length,s=1!==a||e&&pe.isFunction(e.promise)?a:0,u=1===s?e:pe.Deferred(),l=function(e,n,r){return function(i){n[e]=this,r[e]=arguments.length>1?ie.call(arguments):i,r===t?u.notifyWith(n,r):--s||u.resolveWith(n,r)}};if(a>1)for(t=new Array(a),n=new Array(a),r=new Array(a);i0||(je.resolveWith(re,[pe]),pe.fn.triggerHandler&&(pe(re).triggerHandler("ready"),pe(re).off("ready"))))}}),pe.ready.promise=function(t){if(!je)if(je=pe.Deferred(),"complete"===re.readyState||"loading"!==re.readyState&&!re.documentElement.doScroll)e.setTimeout(pe.ready);else if(re.addEventListener)re.addEventListener("DOMContentLoaded",s),e.addEventListener("load",s);else{re.attachEvent("onreadystatechange",s),e.attachEvent("onload",s);var n=!1;try{n=null==e.frameElement&&re.documentElement}catch(r){}n&&n.doScroll&&!function i(){if(!pe.isReady){try{n.doScroll("left")}catch(t){return e.setTimeout(i,50)}a(),pe.ready()}}()}return je.promise(t)},pe.ready.promise();var Le;for(Le in pe(fe))break;fe.ownFirst="0"===Le,fe.inlineBlockNeedsLayout=!1,pe(function(){var e,t,n,r;n=re.getElementsByTagName("body")[0],n&&n.style&&(t=re.createElement("div"),r=re.createElement("div"),r.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",n.appendChild(r).appendChild(t),"undefined"!=typeof t.style.zoom&&(t.style.cssText="display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1",fe.inlineBlockNeedsLayout=e=3===t.offsetWidth,e&&(n.style.zoom=1)),n.removeChild(r))}),function(){var e=re.createElement("div");fe.deleteExpando=!0;try{delete e.test}catch(t){fe.deleteExpando=!1}e=null}();var He=function(e){var t=pe.noData[(e.nodeName+" ").toLowerCase()],n=+e.nodeType||1;return(1===n||9===n)&&(!t||t!==!0&&e.getAttribute("classid")===t)},qe=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,_e=/([A-Z])/g;pe.extend({cache:{},noData:{"applet ":!0,"embed ":!0,"object ":"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(e){return e=e.nodeType?pe.cache[e[pe.expando]]:e[pe.expando],!!e&&!l(e)},data:function(e,t,n){return c(e,t,n)},removeData:function(e,t){return f(e,t)},_data:function(e,t,n){return c(e,t,n,!0)},_removeData:function(e,t){return f(e,t,!0)}}),pe.fn.extend({data:function(e,t){var n,r,i,o=this[0],a=o&&o.attributes;if(void 0===e){if(this.length&&(i=pe.data(o),1===o.nodeType&&!pe._data(o,"parsedAttrs"))){for(n=a.length;n--;)a[n]&&(r=a[n].name,0===r.indexOf("data-")&&(r=pe.camelCase(r.slice(5)),u(o,r,i[r])));pe._data(o,"parsedAttrs",!0)}return i}return"object"==typeof e?this.each(function(){pe.data(this,e)}):arguments.length>1?this.each(function(){pe.data(this,e,t)}):o?u(o,e,pe.data(o,e)):void 0},removeData:function(e){return this.each(function(){pe.removeData(this,e)})}}),pe.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=pe._data(e,t),n&&(!r||pe.isArray(n)?r=pe._data(e,t,pe.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=pe.queue(e,t),r=n.length,i=n.shift(),o=pe._queueHooks(e,t),a=function(){pe.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return pe._data(e,n)||pe._data(e,n,{empty:pe.Callbacks("once memory").add(function(){pe._removeData(e,t+"queue"),pe._removeData(e,n)})})}}),pe.fn.extend({queue:function(e,t){var n=2;return"string"!=typeof e&&(t=e,e="fx",n--),arguments.length
    a",fe.leadingWhitespace=3===e.firstChild.nodeType,fe.tbody=!e.getElementsByTagName("tbody").length,fe.htmlSerialize=!!e.getElementsByTagName("link").length,fe.html5Clone="<:nav>"!==re.createElement("nav").cloneNode(!0).outerHTML,n.type="checkbox",n.checked=!0,t.appendChild(n),fe.appendChecked=n.checked,e.innerHTML="",fe.noCloneChecked=!!e.cloneNode(!0).lastChild.defaultValue,t.appendChild(e),n=re.createElement("input"),n.setAttribute("type","radio"),n.setAttribute("checked","checked"),n.setAttribute("name","t"),e.appendChild(n),fe.checkClone=e.cloneNode(!0).cloneNode(!0).lastChild.checked,fe.noCloneEvent=!!e.addEventListener,e[pe.expando]=1,fe.attributes=!e.getAttribute(pe.expando)}();var Xe={option:[1,""],legend:[1,"
    ","
    "],area:[1,"",""],param:[1,"",""],thead:[1,"","
    "],tr:[2,"","
    "],col:[2,"","
    "],td:[3,"","
    "],_default:fe.htmlSerialize?[0,"",""]:[1,"X
    ","
    "]};Xe.optgroup=Xe.option,Xe.tbody=Xe.tfoot=Xe.colgroup=Xe.caption=Xe.thead,Xe.th=Xe.td;var Ue=/<|&#?\w+;/,Ve=/-1&&(h=p.split("."),p=h.shift(),h.sort()),a=p.indexOf(":")<0&&"on"+p,t=t[pe.expando]?t:new pe.Event(p,"object"==typeof t&&t),t.isTrigger=i?2:3,t.namespace=h.join("."),t.rnamespace=t.namespace?new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,t.result=void 0,t.target||(t.target=r),n=null==n?[t]:pe.makeArray(n,[t]),l=pe.event.special[p]||{},i||!l.trigger||l.trigger.apply(r,n)!==!1)){if(!i&&!l.noBubble&&!pe.isWindow(r)){for(u=l.delegateType||p,Ke.test(u+p)||(s=s.parentNode);s;s=s.parentNode)d.push(s),c=s;c===(r.ownerDocument||re)&&d.push(c.defaultView||c.parentWindow||e)}for(f=0;(s=d[f++])&&!t.isPropagationStopped();)t.type=f>1?u:l.bindType||p,o=(pe._data(s,"events")||{})[t.type]&&pe._data(s,"handle"),o&&o.apply(s,n),o=a&&s[a],o&&o.apply&&He(s)&&(t.result=o.apply(s,n),t.result===!1&&t.preventDefault());if(t.type=p,!i&&!t.isDefaultPrevented()&&(!l._default||l._default.apply(d.pop(),n)===!1)&&He(r)&&a&&r[p]&&!pe.isWindow(r)){c=r[a],c&&(r[a]=null),pe.event.triggered=p;try{r[p]()}catch(g){}pe.event.triggered=void 0,c&&(r[a]=c)}return t.result}},dispatch:function(e){e=pe.event.fix(e);var t,n,r,i,o,a=[],s=ie.call(arguments),u=(pe._data(this,"events")||{})[e.type]||[],l=pe.event.special[e.type]||{};if(s[0]=e,e.delegateTarget=this,!l.preDispatch||l.preDispatch.call(this,e)!==!1){for(a=pe.event.handlers.call(this,e,u),t=0;(i=a[t++])&&!e.isPropagationStopped();)for(e.currentTarget=i.elem,n=0;(o=i.handlers[n++])&&!e.isImmediatePropagationStopped();)e.rnamespace&&!e.rnamespace.test(o.namespace)||(e.handleObj=o,e.data=o.data,r=((pe.event.special[o.origType]||{}).handle||o.handler).apply(i.elem,s),void 0!==r&&(e.result=r)===!1&&(e.preventDefault(),e.stopPropagation()));return l.postDispatch&&l.postDispatch.call(this,e),e.result}},handlers:function(e,t){var n,r,i,o,a=[],s=t.delegateCount,u=e.target;if(s&&u.nodeType&&("click"!==e.type||isNaN(e.button)||e.button<1))for(;u!=this;u=u.parentNode||this)if(1===u.nodeType&&(u.disabled!==!0||"click"!==e.type)){for(r=[],n=0;n-1:pe.find(i,this,null,[u]).length),r[i]&&r.push(o);r.length&&a.push({elem:u,handlers:r})}return s]","i"),tt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi,nt=/\s*$/g,at=p(re),st=at.appendChild(re.createElement("div"));pe.extend({htmlPrefilter:function(e){return e.replace(tt,"<$1>")},clone:function(e,t,n){var r,i,o,a,s,u=pe.contains(e.ownerDocument,e);if(fe.html5Clone||pe.isXMLDoc(e)||!et.test("<"+e.nodeName+">")?o=e.cloneNode(!0):(st.innerHTML=e.outerHTML,st.removeChild(o=st.firstChild)),!(fe.noCloneEvent&&fe.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||pe.isXMLDoc(e)))for(r=h(o),s=h(e),a=0;null!=(i=s[a]);++a)r[a]&&k(i,r[a]);if(t)if(n)for(s=s||h(e),r=r||h(o),a=0;null!=(i=s[a]);a++)N(i,r[a]);else N(e,o);return r=h(o,"script"),r.length>0&&g(r,!u&&h(e,"script")),r=s=i=null,o},cleanData:function(e,t){for(var n,r,i,o,a=0,s=pe.expando,u=pe.cache,l=fe.attributes,c=pe.event.special;null!=(n=e[a]);a++)if((t||He(n))&&(i=n[s],o=i&&u[i])){if(o.events)for(r in o.events)c[r]?pe.event.remove(n,r):pe.removeEvent(n,r,o.handle);u[i]&&(delete u[i],l||"undefined"==typeof n.removeAttribute?n[s]=void 0:n.removeAttribute(s),ne.push(i))}}}),pe.fn.extend({domManip:S,detach:function(e){return A(this,e,!0)},remove:function(e){return A(this,e)},text:function(e){return Pe(this,function(e){return void 0===e?pe.text(this):this.empty().append((this[0]&&this[0].ownerDocument||re).createTextNode(e))},null,e,arguments.length)},append:function(){return S(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=T(this,e);t.appendChild(e)}})},prepend:function(){return S(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=T(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return S(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return S(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},empty:function(){for(var e,t=0;null!=(e=this[t]);t++){for(1===e.nodeType&&pe.cleanData(h(e,!1));e.firstChild;)e.removeChild(e.firstChild);e.options&&pe.nodeName(e,"select")&&(e.options.length=0)}return this},clone:function(e,t){return e=null!=e&&e,t=null==t?e:t,this.map(function(){return pe.clone(this,e,t)})},html:function(e){return Pe(this,function(e){var t=this[0]||{},n=0,r=this.length;if(void 0===e)return 1===t.nodeType?t.innerHTML.replace(Ze,""):void 0;if("string"==typeof e&&!nt.test(e)&&(fe.htmlSerialize||!et.test(e))&&(fe.leadingWhitespace||!$e.test(e))&&!Xe[(We.exec(e)||["",""])[1].toLowerCase()]){e=pe.htmlPrefilter(e);try{for(;nt",t=l.getElementsByTagName("td"),t[0].style.cssText="margin:0;border:0;padding:0;display:none",o=0===t[0].offsetHeight,o&&(t[0].style.display="",t[1].style.display="none",o=0===t[0].offsetHeight)),f.removeChild(u)}var n,r,i,o,a,s,u=re.createElement("div"),l=re.createElement("div");l.style&&(l.style.cssText="float:left;opacity:.5",fe.opacity="0.5"===l.style.opacity,fe.cssFloat=!!l.style.cssFloat,l.style.backgroundClip="content-box",l.cloneNode(!0).style.backgroundClip="",fe.clearCloneStyle="content-box"===l.style.backgroundClip,u=re.createElement("div"),u.style.cssText="border:0;width:8px;height:0;top:0;left:-9999px;padding:0;margin-top:1px;position:absolute",l.innerHTML="",u.appendChild(l),fe.boxSizing=""===l.style.boxSizing||""===l.style.MozBoxSizing||""===l.style.WebkitBoxSizing,pe.extend(fe,{reliableHiddenOffsets:function(){return null==n&&t(),o},boxSizingReliable:function(){return null==n&&t(),i},pixelMarginRight:function(){return null==n&&t(),r},pixelPosition:function(){return null==n&&t(),n},reliableMarginRight:function(){return null==n&&t(),a},reliableMarginLeft:function(){return null==n&&t(),s}}))}();var ht,gt,mt=/^(top|right|bottom|left)$/;e.getComputedStyle?(ht=function(t){var n=t.ownerDocument.defaultView;return n&&n.opener||(n=e),n.getComputedStyle(t)},gt=function(e,t,n){var r,i,o,a,s=e.style;return n=n||ht(e),a=n?n.getPropertyValue(t)||n[t]:void 0,""!==a&&void 0!==a||pe.contains(e.ownerDocument,e)||(a=pe.style(e,t)),n&&!fe.pixelMarginRight()&&ft.test(a)&&ct.test(t)&&(r=s.width,i=s.minWidth,o=s.maxWidth,s.minWidth=s.maxWidth=s.width=a,a=n.width,s.width=r,s.minWidth=i,s.maxWidth=o),void 0===a?a:a+""}):pt.currentStyle&&(ht=function(e){return e.currentStyle},gt=function(e,t,n){var r,i,o,a,s=e.style;return n=n||ht(e),a=n?n[t]:void 0,null==a&&s&&s[t]&&(a=s[t]),ft.test(a)&&!mt.test(t)&&(r=s.left,i=e.runtimeStyle,o=i&&i.left,o&&(i.left=e.currentStyle.left),s.left="fontSize"===t?"1em":a,a=s.pixelLeft+"px",s.left=r,o&&(i.left=o)),void 0===a?a:a+""||"auto"});var yt=/alpha\([^)]*\)/i,vt=/opacity\s*=\s*([^)]*)/i,xt=/^(none|table(?!-c[ea]).+)/,bt=new RegExp("^("+Fe+")(.*)$","i"),wt={position:"absolute",visibility:"hidden",display:"block"},Tt={letterSpacing:"0",fontWeight:"400"},Ct=["Webkit","O","Moz","ms"],Et=re.createElement("div").style;pe.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=gt(e,"opacity");return""===n?"1":n}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":fe.cssFloat?"cssFloat":"styleFloat"},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var i,o,a,s=pe.camelCase(t),u=e.style;if(t=pe.cssProps[s]||(pe.cssProps[s]=H(s)||s),a=pe.cssHooks[t]||pe.cssHooks[s],void 0===n)return a&&"get"in a&&void 0!==(i=a.get(e,!1,r))?i:u[t];if(o=typeof n,"string"===o&&(i=Me.exec(n))&&i[1]&&(n=d(e,t,i),o="number"),null!=n&&n===n&&("number"===o&&(n+=i&&i[3]||(pe.cssNumber[s]?"":"px")),fe.clearCloneStyle||""!==n||0!==t.indexOf("background")||(u[t]="inherit"),!(a&&"set"in a&&void 0===(n=a.set(e,n,r)))))try{u[t]=n}catch(l){}}},css:function(e,t,n,r){var i,o,a,s=pe.camelCase(t);return t=pe.cssProps[s]||(pe.cssProps[s]=H(s)||s),a=pe.cssHooks[t]||pe.cssHooks[s],a&&"get"in a&&(o=a.get(e,!0,n)),void 0===o&&(o=gt(e,t,r)),"normal"===o&&t in Tt&&(o=Tt[t]),""===n||n?(i=parseFloat(o),n===!0||isFinite(i)?i||0:o):o}}),pe.each(["height","width"],function(e,t){pe.cssHooks[t]={get:function(e,n,r){if(n)return xt.test(pe.css(e,"display"))&&0===e.offsetWidth?dt(e,wt,function(){return M(e,t,r)}):M(e,t,r)},set:function(e,n,r){var i=r&&ht(e);return _(e,n,r?F(e,t,r,fe.boxSizing&&"border-box"===pe.css(e,"boxSizing",!1,i),i):0)}}}),fe.opacity||(pe.cssHooks.opacity={get:function(e,t){return vt.test((t&&e.currentStyle?e.currentStyle.filter:e.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":t?"1":""},set:function(e,t){var n=e.style,r=e.currentStyle,i=pe.isNumeric(t)?"alpha(opacity="+100*t+")":"",o=r&&r.filter||n.filter||"";n.zoom=1,(t>=1||""===t)&&""===pe.trim(o.replace(yt,""))&&n.removeAttribute&&(n.removeAttribute("filter"),""===t||r&&!r.filter)||(n.filter=yt.test(o)?o.replace(yt,i):o+" "+i)}}),pe.cssHooks.marginRight=L(fe.reliableMarginRight,function(e,t){if(t)return dt(e,{display:"inline-block"},gt,[e,"marginRight"])}),pe.cssHooks.marginLeft=L(fe.reliableMarginLeft,function(e,t){if(t)return(parseFloat(gt(e,"marginLeft"))||(pe.contains(e.ownerDocument,e)?e.getBoundingClientRect().left-dt(e,{marginLeft:0},function(){return e.getBoundingClientRect().left}):0))+"px"}),pe.each({margin:"",padding:"",border:"Width"},function(e,t){pe.cssHooks[e+t]={expand:function(n){for(var r=0,i={},o="string"==typeof n?n.split(" "):[n];r<4;r++)i[e+Oe[r]+t]=o[r]||o[r-2]||o[0];return i}},ct.test(e)||(pe.cssHooks[e+t].set=_)}),pe.fn.extend({css:function(e,t){return Pe(this,function(e,t,n){var r,i,o={},a=0;if(pe.isArray(t)){for(r=ht(e),i=t.length;a1)},show:function(){return q(this,!0)},hide:function(){return q(this)},toggle:function(e){return"boolean"==typeof e?e?this.show():this.hide():this.each(function(){Re(this)?pe(this).show():pe(this).hide()})}}),pe.Tween=O,O.prototype={constructor:O,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||pe.easing._default,this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(pe.cssNumber[n]?"":"px")},cur:function(){var e=O.propHooks[this.prop];return e&&e.get?e.get(this):O.propHooks._default.get(this)},run:function(e){var t,n=O.propHooks[this.prop];return this.options.duration?this.pos=t=pe.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):this.pos=t=e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):O.propHooks._default.set(this),this}},O.prototype.init.prototype=O.prototype,O.propHooks={_default:{get:function(e){var t;return 1!==e.elem.nodeType||null!=e.elem[e.prop]&&null==e.elem.style[e.prop]?e.elem[e.prop]:(t=pe.css(e.elem,e.prop,""),t&&"auto"!==t?t:0)},set:function(e){pe.fx.step[e.prop]?pe.fx.step[e.prop](e):1!==e.elem.nodeType||null==e.elem.style[pe.cssProps[e.prop]]&&!pe.cssHooks[e.prop]?e.elem[e.prop]=e.now:pe.style(e.elem,e.prop,e.now+e.unit)}}},O.propHooks.scrollTop=O.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},pe.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2},_default:"swing"},pe.fx=O.prototype.init,pe.fx.step={};var Nt,kt,St=/^(?:toggle|show|hide)$/,At=/queueHooks$/;pe.Animation=pe.extend($,{tweeners:{"*":[function(e,t){var n=this.createTween(e,t);return d(n.elem,e,Me.exec(t),n),n}]},tweener:function(e,t){pe.isFunction(e)?(t=e,e=["*"]):e=e.match(De);for(var n,r=0,i=e.length;r
    a",e=n.getElementsByTagName("a")[0],t.setAttribute("type","checkbox"),n.appendChild(t),e=n.getElementsByTagName("a")[0],e.style.cssText="top:1px",fe.getSetAttribute="t"!==n.className,fe.style=/top/.test(e.getAttribute("style")),fe.hrefNormalized="/a"===e.getAttribute("href"),fe.checkOn=!!t.value,fe.optSelected=i.selected,fe.enctype=!!re.createElement("form").enctype,r.disabled=!0,fe.optDisabled=!i.disabled,t=re.createElement("input"),t.setAttribute("value",""),fe.input=""===t.getAttribute("value"),t.value="t",t.setAttribute("type","radio"),fe.radioValue="t"===t.value}();var Dt=/\r/g,jt=/[\x20\t\r\n\f]+/g;pe.fn.extend({val:function(e){var t,n,r,i=this[0];{if(arguments.length)return r=pe.isFunction(e),this.each(function(n){var i;1===this.nodeType&&(i=r?e.call(this,n,pe(this).val()):e,null==i?i="":"number"==typeof i?i+="":pe.isArray(i)&&(i=pe.map(i,function(e){return null==e?"":e+""})),t=pe.valHooks[this.type]||pe.valHooks[this.nodeName.toLowerCase()],t&&"set"in t&&void 0!==t.set(this,i,"value")||(this.value=i))});if(i)return t=pe.valHooks[i.type]||pe.valHooks[i.nodeName.toLowerCase()],t&&"get"in t&&void 0!==(n=t.get(i,"value"))?n:(n=i.value,"string"==typeof n?n.replace(Dt,""):null==n?"":n)}}}),pe.extend({valHooks:{option:{get:function(e){var t=pe.find.attr(e,"value");return null!=t?t:pe.trim(pe.text(e)).replace(jt," ")}},select:{get:function(e){for(var t,n,r=e.options,i=e.selectedIndex,o="select-one"===e.type||i<0,a=o?null:[],s=o?i+1:r.length,u=i<0?s:o?i:0;u-1)try{r.selected=n=!0}catch(s){r.scrollHeight}else r.selected=!1;return n||(e.selectedIndex=-1),i}}}}),pe.each(["radio","checkbox"],function(){pe.valHooks[this]={set:function(e,t){if(pe.isArray(t))return e.checked=pe.inArray(pe(e).val(),t)>-1}},fe.checkOn||(pe.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})});var Lt,Ht,qt=pe.expr.attrHandle,_t=/^(?:checked|selected)$/i,Ft=fe.getSetAttribute,Mt=fe.input;pe.fn.extend({attr:function(e,t){return Pe(this,pe.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){pe.removeAttr(this,e)})}}),pe.extend({attr:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return"undefined"==typeof e.getAttribute?pe.prop(e,t,n):(1===o&&pe.isXMLDoc(e)||(t=t.toLowerCase(),i=pe.attrHooks[t]||(pe.expr.match.bool.test(t)?Ht:Lt)),void 0!==n?null===n?void pe.removeAttr(e,t):i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:(e.setAttribute(t,n+""),n):i&&"get"in i&&null!==(r=i.get(e,t))?r:(r=pe.find.attr(e,t),null==r?void 0:r))},attrHooks:{type:{set:function(e,t){if(!fe.radioValue&&"radio"===t&&pe.nodeName(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},removeAttr:function(e,t){var n,r,i=0,o=t&&t.match(De);if(o&&1===e.nodeType)for(;n=o[i++];)r=pe.propFix[n]||n,pe.expr.match.bool.test(n)?Mt&&Ft||!_t.test(n)?e[r]=!1:e[pe.camelCase("default-"+n)]=e[r]=!1:pe.attr(e,n,""),e.removeAttribute(Ft?n:r)}}),Ht={set:function(e,t,n){return t===!1?pe.removeAttr(e,n):Mt&&Ft||!_t.test(n)?e.setAttribute(!Ft&&pe.propFix[n]||n,n):e[pe.camelCase("default-"+n)]=e[n]=!0,n}},pe.each(pe.expr.match.bool.source.match(/\w+/g),function(e,t){var n=qt[t]||pe.find.attr;Mt&&Ft||!_t.test(t)?qt[t]=function(e,t,r){var i,o;return r||(o=qt[t],qt[t]=i,i=null!=n(e,t,r)?t.toLowerCase():null,qt[t]=o),i}:qt[t]=function(e,t,n){if(!n)return e[pe.camelCase("default-"+t)]?t.toLowerCase():null}}),Mt&&Ft||(pe.attrHooks.value={set:function(e,t,n){return pe.nodeName(e,"input")?void(e.defaultValue=t):Lt&&Lt.set(e,t,n)}}),Ft||(Lt={set:function(e,t,n){var r=e.getAttributeNode(n);if(r||e.setAttributeNode(r=e.ownerDocument.createAttribute(n)),r.value=t+="","value"===n||t===e.getAttribute(n))return t}},qt.id=qt.name=qt.coords=function(e,t,n){var r;if(!n)return(r=e.getAttributeNode(t))&&""!==r.value?r.value:null},pe.valHooks.button={get:function(e,t){var n=e.getAttributeNode(t);if(n&&n.specified)return n.value},set:Lt.set},pe.attrHooks.contenteditable={set:function(e,t,n){Lt.set(e,""!==t&&t,n)}},pe.each(["width","height"],function(e,t){pe.attrHooks[t]={set:function(e,n){if(""===n)return e.setAttribute(t,"auto"),n}}})),fe.style||(pe.attrHooks.style={get:function(e){return e.style.cssText||void 0},set:function(e,t){return e.style.cssText=t+""}});var Ot=/^(?:input|select|textarea|button|object)$/i,Rt=/^(?:a|area)$/i;pe.fn.extend({prop:function(e,t){return Pe(this,pe.prop,e,t,arguments.length>1)},removeProp:function(e){return e=pe.propFix[e]||e,this.each(function(){try{this[e]=void 0,delete this[e]}catch(t){}})}}),pe.extend({prop:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return 1===o&&pe.isXMLDoc(e)||(t=pe.propFix[t]||t,i=pe.propHooks[t]),void 0!==n?i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:e[t]=n:i&&"get"in i&&null!==(r=i.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){var t=pe.find.attr(e,"tabindex");return t?parseInt(t,10):Ot.test(e.nodeName)||Rt.test(e.nodeName)&&e.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),fe.hrefNormalized||pe.each(["href","src"],function(e,t){pe.propHooks[t]={get:function(e){return e.getAttribute(t,4)}}}),fe.optSelected||(pe.propHooks.selected={get:function(e){var t=e.parentNode;return t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex),null},set:function(e){var t=e.parentNode;t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex)}}),pe.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){pe.propFix[this.toLowerCase()]=this}),fe.enctype||(pe.propFix.enctype="encoding");var Pt=/[\t\r\n\f]/g;pe.fn.extend({addClass:function(e){var t,n,r,i,o,a,s,u=0;if(pe.isFunction(e))return this.each(function(t){pe(this).addClass(e.call(this,t,z(this)))});if("string"==typeof e&&e)for(t=e.match(De)||[];n=this[u++];)if(i=z(n),r=1===n.nodeType&&(" "+i+" ").replace(Pt," ")){for(a=0;o=t[a++];)r.indexOf(" "+o+" ")<0&&(r+=o+" ");s=pe.trim(r),i!==s&&pe.attr(n,"class",s)}return this},removeClass:function(e){var t,n,r,i,o,a,s,u=0;if(pe.isFunction(e))return this.each(function(t){pe(this).removeClass(e.call(this,t,z(this)))});if(!arguments.length)return this.attr("class","");if("string"==typeof e&&e)for(t=e.match(De)||[];n=this[u++];)if(i=z(n),r=1===n.nodeType&&(" "+i+" ").replace(Pt," ")){for(a=0;o=t[a++];)for(;r.indexOf(" "+o+" ")>-1;)r=r.replace(" "+o+" "," ");s=pe.trim(r),i!==s&&pe.attr(n,"class",s)}return this},toggleClass:function(e,t){var n=typeof e;return"boolean"==typeof t&&"string"===n?t?this.addClass(e):this.removeClass(e):pe.isFunction(e)?this.each(function(n){pe(this).toggleClass(e.call(this,n,z(this),t),t)}):this.each(function(){var t,r,i,o;if("string"===n)for(r=0,i=pe(this),o=e.match(De)||[];t=o[r++];)i.hasClass(t)?i.removeClass(t):i.addClass(t);else void 0!==e&&"boolean"!==n||(t=z(this),t&&pe._data(this,"__className__",t),pe.attr(this,"class",t||e===!1?"":pe._data(this,"__className__")||""))})},hasClass:function(e){var t,n,r=0;for(t=" "+e+" ";n=this[r++];)if(1===n.nodeType&&(" "+z(n)+" ").replace(Pt," ").indexOf(t)>-1)return!0;return!1}}),pe.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(e,t){pe.fn[t]=function(e,n){return arguments.length>0?this.on(t,null,e,n):this.trigger(t)}}),pe.fn.extend({hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}});var Bt=e.location,Wt=pe.now(),It=/\?/,$t=/(,)|(\[|{)|(}|])|"(?:[^"\\\r\n]|\\["\\\/bfnrt]|\\u[\da-fA-F]{4})*"\s*:?|true|false|null|-?(?!0\d)\d+(?:\.\d+|)(?:[eE][+-]?\d+|)/g;pe.parseJSON=function(t){if(e.JSON&&e.JSON.parse)return e.JSON.parse(t+"");var n,r=null,i=pe.trim(t+"");return i&&!pe.trim(i.replace($t,function(e,t,i,o){return n&&t&&(r=0),0===r?e:(n=i||t,r+=!o-!i,"")}))?Function("return "+i)():pe.error("Invalid JSON: "+t)},pe.parseXML=function(t){var n,r;if(!t||"string"!=typeof t)return null;try{e.DOMParser?(r=new e.DOMParser,n=r.parseFromString(t,"text/xml")):(n=new e.ActiveXObject("Microsoft.XMLDOM"),n.async="false",n.loadXML(t))}catch(i){n=void 0}return n&&n.documentElement&&!n.getElementsByTagName("parsererror").length||pe.error("Invalid XML: "+t),n};var zt=/#.*$/,Xt=/([?&])_=[^&]*/,Ut=/^(.*?):[ \t]*([^\r\n]*)\r?$/gm,Vt=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Yt=/^(?:GET|HEAD)$/,Jt=/^\/\//,Gt=/^([\w.+-]+:)(?:\/\/(?:[^\/?#]*@|)([^\/?#:]*)(?::(\d+)|)|)/,Kt={},Qt={},Zt="*/".concat("*"),en=Bt.href,tn=Gt.exec(en.toLowerCase())||[];pe.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:en,type:"GET",isLocal:Vt.test(tn[1]),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Zt,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":pe.parseJSON,"text xml":pe.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?V(V(e,pe.ajaxSettings),t):V(pe.ajaxSettings,e)},ajaxPrefilter:X(Kt),ajaxTransport:X(Qt),ajax:function(t,n){function r(t,n,r,i){var o,f,v,x,w,C=n;2!==b&&(b=2,u&&e.clearTimeout(u),c=void 0,s=i||"",T.readyState=t>0?4:0,o=t>=200&&t<300||304===t,r&&(x=Y(d,T,r)),x=J(d,x,T,o),o?(d.ifModified&&(w=T.getResponseHeader("Last-Modified"),w&&(pe.lastModified[a]=w),w=T.getResponseHeader("etag"),w&&(pe.etag[a]=w)),204===t||"HEAD"===d.type?C="nocontent":304===t?C="notmodified":(C=x.state,f=x.data,v=x.error,o=!v)):(v=C,!t&&C||(C="error",t<0&&(t=0))),T.status=t,T.statusText=(n||C)+"",o?g.resolveWith(p,[f,C,T]):g.rejectWith(p,[T,C,v]),T.statusCode(y),y=void 0,l&&h.trigger(o?"ajaxSuccess":"ajaxError",[T,d,o?f:v]),m.fireWith(p,[T,C]),l&&(h.trigger("ajaxComplete",[T,d]),--pe.active||pe.event.trigger("ajaxStop")))}"object"==typeof t&&(n=t,t=void 0),n=n||{};var i,o,a,s,u,l,c,f,d=pe.ajaxSetup({},n),p=d.context||d,h=d.context&&(p.nodeType||p.jquery)?pe(p):pe.event,g=pe.Deferred(),m=pe.Callbacks("once memory"),y=d.statusCode||{},v={},x={},b=0,w="canceled",T={readyState:0,getResponseHeader:function(e){var t;if(2===b){if(!f)for(f={};t=Ut.exec(s);)f[t[1].toLowerCase()]=t[2];t=f[e.toLowerCase()]}return null==t?null:t},getAllResponseHeaders:function(){return 2===b?s:null},setRequestHeader:function(e,t){var n=e.toLowerCase();return b||(e=x[n]=x[n]||e,v[e]=t),this},overrideMimeType:function(e){return b||(d.mimeType=e),this},statusCode:function(e){var t;if(e)if(b<2)for(t in e)y[t]=[y[t],e[t]];else T.always(e[T.status]);return this},abort:function(e){var t=e||w;return c&&c.abort(t),r(0,t),this}};if(g.promise(T).complete=m.add,T.success=T.done,T.error=T.fail,d.url=((t||d.url||en)+"").replace(zt,"").replace(Jt,tn[1]+"//"),d.type=n.method||n.type||d.method||d.type,d.dataTypes=pe.trim(d.dataType||"*").toLowerCase().match(De)||[""],null==d.crossDomain&&(i=Gt.exec(d.url.toLowerCase()),d.crossDomain=!(!i||i[1]===tn[1]&&i[2]===tn[2]&&(i[3]||("http:"===i[1]?"80":"443"))===(tn[3]||("http:"===tn[1]?"80":"443")))),d.data&&d.processData&&"string"!=typeof d.data&&(d.data=pe.param(d.data,d.traditional)),U(Kt,d,n,T),2===b)return T;l=pe.event&&d.global,l&&0===pe.active++&&pe.event.trigger("ajaxStart"),d.type=d.type.toUpperCase(),d.hasContent=!Yt.test(d.type),a=d.url,d.hasContent||(d.data&&(a=d.url+=(It.test(a)?"&":"?")+d.data,delete d.data),d.cache===!1&&(d.url=Xt.test(a)?a.replace(Xt,"$1_="+Wt++):a+(It.test(a)?"&":"?")+"_="+Wt++)),d.ifModified&&(pe.lastModified[a]&&T.setRequestHeader("If-Modified-Since",pe.lastModified[a]),pe.etag[a]&&T.setRequestHeader("If-None-Match",pe.etag[a])),(d.data&&d.hasContent&&d.contentType!==!1||n.contentType)&&T.setRequestHeader("Content-Type",d.contentType),T.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+("*"!==d.dataTypes[0]?", "+Zt+"; q=0.01":""):d.accepts["*"]);for(o in d.headers)T.setRequestHeader(o,d.headers[o]);if(d.beforeSend&&(d.beforeSend.call(p,T,d)===!1||2===b))return T.abort();w="abort";for(o in{success:1,error:1,complete:1})T[o](d[o]);if(c=U(Qt,d,n,T)){if(T.readyState=1,l&&h.trigger("ajaxSend",[T,d]),2===b)return T;d.async&&d.timeout>0&&(u=e.setTimeout(function(){T.abort("timeout")},d.timeout));try{b=1,c.send(v,r)}catch(C){if(!(b<2))throw C;r(-1,C)}}else r(-1,"No Transport");return T},getJSON:function(e,t,n){return pe.get(e,t,n,"json")},getScript:function(e,t){return pe.get(e,void 0,t,"script")}}),pe.each(["get","post"],function(e,t){pe[t]=function(e,n,r,i){return pe.isFunction(n)&&(i=i||r,r=n,n=void 0),pe.ajax(pe.extend({url:e,type:t,dataType:i,data:n,success:r},pe.isPlainObject(e)&&e))}}),pe._evalUrl=function(e){return pe.ajax({url:e,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,"throws":!0})},pe.fn.extend({wrapAll:function(e){if(pe.isFunction(e))return this.each(function(t){pe(this).wrapAll(e.call(this,t))});if(this[0]){var t=pe(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){for(var e=this;e.firstChild&&1===e.firstChild.nodeType;)e=e.firstChild;return e}).append(this)}return this},wrapInner:function(e){return pe.isFunction(e)?this.each(function(t){pe(this).wrapInner(e.call(this,t))}):this.each(function(){var t=pe(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=pe.isFunction(e);return this.each(function(n){pe(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){pe.nodeName(this,"body")||pe(this).replaceWith(this.childNodes)}).end()}}),pe.expr.filters.hidden=function(e){return fe.reliableHiddenOffsets()?e.offsetWidth<=0&&e.offsetHeight<=0&&!e.getClientRects().length:K(e)},pe.expr.filters.visible=function(e){return!pe.expr.filters.hidden(e)};var nn=/%20/g,rn=/\[\]$/,on=/\r?\n/g,an=/^(?:submit|button|image|reset|file)$/i,sn=/^(?:input|select|textarea|keygen)/i;pe.param=function(e,t){var n,r=[],i=function(e,t){t=pe.isFunction(t)?t():null==t?"":t,r[r.length]=encodeURIComponent(e)+"="+encodeURIComponent(t)};if(void 0===t&&(t=pe.ajaxSettings&&pe.ajaxSettings.traditional),pe.isArray(e)||e.jquery&&!pe.isPlainObject(e))pe.each(e,function(){i(this.name,this.value)});else for(n in e)Q(n,e[n],t,i);return r.join("&").replace(nn,"+")},pe.fn.extend({serialize:function(){return pe.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=pe.prop(this,"elements");return e?pe.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!pe(this).is(":disabled")&&sn.test(this.nodeName)&&!an.test(e)&&(this.checked||!Be.test(e))}).map(function(e,t){var n=pe(this).val();return null==n?null:pe.isArray(n)?pe.map(n,function(e){return{name:t.name,value:e.replace(on,"\r\n")}}):{name:t.name,value:n.replace(on,"\r\n")}}).get()}}),pe.ajaxSettings.xhr=void 0!==e.ActiveXObject?function(){return this.isLocal?ee():re.documentMode>8?Z():/^(get|post|head|put|delete|options)$/i.test(this.type)&&Z()||ee()}:Z;var un=0,ln={},cn=pe.ajaxSettings.xhr();e.attachEvent&&e.attachEvent("onunload",function(){for(var e in ln)ln[e](void 0,!0)}),fe.cors=!!cn&&"withCredentials"in cn,cn=fe.ajax=!!cn,cn&&pe.ajaxTransport(function(t){if(!t.crossDomain||fe.cors){var n;return{send:function(r,i){var o,a=t.xhr(),s=++un;if(a.open(t.type,t.url,t.async,t.username,t.password),t.xhrFields)for(o in t.xhrFields)a[o]=t.xhrFields[o];t.mimeType&&a.overrideMimeType&&a.overrideMimeType(t.mimeType),t.crossDomain||r["X-Requested-With"]||(r["X-Requested-With"]="XMLHttpRequest");for(o in r)void 0!==r[o]&&a.setRequestHeader(o,r[o]+"");a.send(t.hasContent&&t.data||null),n=function(e,r){var o,u,l;if(n&&(r||4===a.readyState))if(delete ln[s],n=void 0,a.onreadystatechange=pe.noop,r)4!==a.readyState&&a.abort();else{l={},o=a.status,"string"==typeof a.responseText&&(l.text=a.responseText);try{u=a.statusText}catch(c){u=""}o||!t.isLocal||t.crossDomain?1223===o&&(o=204):o=l.text?200:404}l&&i(o,u,l,a.getAllResponseHeaders())},t.async?4===a.readyState?e.setTimeout(n):a.onreadystatechange=ln[s]=n:n()},abort:function(){n&&n(void 0,!0)}}}}),pe.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(e){return pe.globalEval(e),e}}}),pe.ajaxPrefilter("script",function(e){void 0===e.cache&&(e.cache=!1),e.crossDomain&&(e.type="GET",e.global=!1)}),pe.ajaxTransport("script",function(e){if(e.crossDomain){var t,n=re.head||pe("head")[0]||re.documentElement;return{send:function(r,i){t=re.createElement("script"),t.async=!0,e.scriptCharset&&(t.charset=e.scriptCharset),t.src=e.url,t.onload=t.onreadystatechange=function(e,n){(n||!t.readyState||/loaded|complete/.test(t.readyState))&&(t.onload=t.onreadystatechange=null,t.parentNode&&t.parentNode.removeChild(t),t=null,n||i(200,"success"))},n.insertBefore(t,n.firstChild)},abort:function(){t&&t.onload(void 0,!0)}}}});var fn=[],dn=/(=)\?(?=&|$)|\?\?/;pe.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=fn.pop()||pe.expando+"_"+Wt++;return this[e]=!0,e}}),pe.ajaxPrefilter("json jsonp",function(t,n,r){var i,o,a,s=t.jsonp!==!1&&(dn.test(t.url)?"url":"string"==typeof t.data&&0===(t.contentType||"").indexOf("application/x-www-form-urlencoded")&&dn.test(t.data)&&"data");if(s||"jsonp"===t.dataTypes[0])return i=t.jsonpCallback=pe.isFunction(t.jsonpCallback)?t.jsonpCallback():t.jsonpCallback,s?t[s]=t[s].replace(dn,"$1"+i):t.jsonp!==!1&&(t.url+=(It.test(t.url)?"&":"?")+t.jsonp+"="+i),t.converters["script json"]=function(){return a||pe.error(i+" was not called"),a[0]},t.dataTypes[0]="json",o=e[i],e[i]=function(){a=arguments},r.always(function(){void 0===o?pe(e).removeProp(i):e[i]=o,t[i]&&(t.jsonpCallback=n.jsonpCallback,fn.push(i)),a&&pe.isFunction(o)&&o(a[0]),a=o=void 0}),"script"}),pe.parseHTML=function(e,t,n){if(!e||"string"!=typeof e)return null;"boolean"==typeof t&&(n=t,t=!1),t=t||re;var r=Te.exec(e),i=!n&&[];return r?[t.createElement(r[1])]:(r=y([e],t,i),i&&i.length&&pe(i).remove(),pe.merge([],r.childNodes))};var pn=pe.fn.load;return pe.fn.load=function(e,t,n){if("string"!=typeof e&&pn)return pn.apply(this,arguments);var r,i,o,a=this,s=e.indexOf(" ");return s>-1&&(r=pe.trim(e.slice(s,e.length)),e=e.slice(0,s)),pe.isFunction(t)?(n=t,t=void 0):t&&"object"==typeof t&&(i="POST"),a.length>0&&pe.ajax({url:e,type:i||"GET",dataType:"html",data:t}).done(function(e){o=arguments,a.html(r?pe("
    ").append(pe.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},pe.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){pe.fn[t]=function(e){return this.on(t,e)}}),pe.expr.filters.animated=function(e){return pe.grep(pe.timers,function(t){return e===t.elem}).length},pe.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l,c=pe.css(e,"position"),f=pe(e),d={};"static"===c&&(e.style.position="relative"),s=f.offset(),o=pe.css(e,"top"),u=pe.css(e,"left"),l=("absolute"===c||"fixed"===c)&&pe.inArray("auto",[o,u])>-1,l?(r=f.position(),a=r.top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),pe.isFunction(t)&&(t=t.call(e,n,pe.extend({},s))),null!=t.top&&(d.top=t.top-s.top+a),null!=t.left&&(d.left=t.left-s.left+i),"using"in t?t.using.call(e,d):f.css(d)}},pe.fn.extend({offset:function(e){if(arguments.length)return void 0===e?this:this.each(function(t){pe.offset.setOffset(this,e,t)});var t,n,r={top:0,left:0},i=this[0],o=i&&i.ownerDocument;if(o)return t=o.documentElement,pe.contains(t,i)?("undefined"!=typeof i.getBoundingClientRect&&(r=i.getBoundingClientRect()),n=te(o),{top:r.top+(n.pageYOffset||t.scrollTop)-(t.clientTop||0),left:r.left+(n.pageXOffset||t.scrollLeft)-(t.clientLeft||0)}):r},position:function(){if(this[0]){var e,t,n={top:0,left:0},r=this[0];return"fixed"===pe.css(r,"position")?t=r.getBoundingClientRect():(e=this.offsetParent(),t=this.offset(),pe.nodeName(e[0],"html")||(n=e.offset()),n.top+=pe.css(e[0],"borderTopWidth",!0),n.left+=pe.css(e[0],"borderLeftWidth",!0)),{top:t.top-n.top-pe.css(r,"marginTop",!0),left:t.left-n.left-pe.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){ +for(var e=this.offsetParent;e&&!pe.nodeName(e,"html")&&"static"===pe.css(e,"position");)e=e.offsetParent;return e||pt})}}),pe.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(e,t){var n=/Y/.test(t);pe.fn[e]=function(r){return Pe(this,function(e,r,i){var o=te(e);return void 0===i?o?t in o?o[t]:o.document.documentElement[r]:e[r]:void(o?o.scrollTo(n?pe(o).scrollLeft():i,n?i:pe(o).scrollTop()):e[r]=i)},e,r,arguments.length,null)}}),pe.each(["top","left"],function(e,t){pe.cssHooks[t]=L(fe.pixelPosition,function(e,n){if(n)return n=gt(e,t),ft.test(n)?pe(e).position()[t]+"px":n})}),pe.each({Height:"height",Width:"width"},function(e,t){pe.each({padding:"inner"+e,content:t,"":"outer"+e},function(n,r){pe.fn[r]=function(r,i){var o=arguments.length&&(n||"boolean"!=typeof r),a=n||(r===!0||i===!0?"margin":"border");return Pe(this,function(t,n,r){var i;return pe.isWindow(t)?t.document.documentElement["client"+e]:9===t.nodeType?(i=t.documentElement,Math.max(t.body["scroll"+e],i["scroll"+e],t.body["offset"+e],i["offset"+e],i["client"+e])):void 0===r?pe.css(t,n,a):pe.style(t,n,r,a)},t,o?r:void 0,o,null)}})}),pe.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)}}),pe.fn.size=function(){return this.length},pe.fn.andSelf=pe.fn.addBack,layui.define(function(e){layui.$=pe,e("jquery",pe)}),pe}); \ No newline at end of file diff --git a/main/webapp/static/lib/layui/lay/modules/laydate.js b/main/webapp/static/lib/layui/lay/modules/laydate.js new file mode 100644 index 0000000..3cb99d6 --- /dev/null +++ b/main/webapp/static/lib/layui/lay/modules/laydate.js @@ -0,0 +1,2 @@ +/** layui-v2.2.6 MIT License By https://www.layui.com */ + ;!function(){"use strict";var e=window.layui&&layui.define,t={getPath:function(){var e=document.currentScript?document.currentScript.src:function(){for(var e,t=document.scripts,n=t.length-1,a=n;a>0;a--)if("interactive"===t[a].readyState){e=t[a].src;break}return e||t[n].src}();return e.substring(0,e.lastIndexOf("/")+1)}(),getStyle:function(e,t){var n=e.currentStyle?e.currentStyle:window.getComputedStyle(e,null);return n[n.getPropertyValue?"getPropertyValue":"getAttribute"](t)},link:function(e,a,i){if(n.path){var r=document.getElementsByTagName("head")[0],o=document.createElement("link");"string"==typeof a&&(i=a);var s=(i||e).replace(/\.|\//g,""),l="layuicss-"+s,d=0;o.rel="stylesheet",o.href=n.path+e,o.id=l,document.getElementById(l)||r.appendChild(o),"function"==typeof a&&!function c(){return++d>80?window.console&&console.error("laydate.css: Invalid"):void(1989===parseInt(t.getStyle(document.getElementById(l),"width"))?a():setTimeout(c,100))}()}}},n={v:"5.0.9",config:{},index:window.laydate&&window.laydate.v?1e5:0,path:t.getPath,set:function(e){var t=this;return t.config=w.extend({},t.config,e),t},ready:function(a){var i="laydate",r="",o=(e?"modules/laydate/":"theme/")+"default/laydate.css?v="+n.v+r;return e?layui.addcss(o,a,i):t.link(o,a,i),this}},a=function(){var e=this;return{hint:function(t){e.hint.call(e,t)},config:e.config}},i="laydate",r=".layui-laydate",o="layui-this",s="laydate-disabled",l="开始日期超出了结束日期
    建议重新选择",d=[100,2e5],c="layui-laydate-static",m="layui-laydate-list",u="laydate-selected",h="layui-laydate-hint",y="laydate-day-prev",f="laydate-day-next",p="layui-laydate-footer",g=".laydate-btns-confirm",v="laydate-time-text",D=".laydate-btns-time",T=function(e){var t=this;t.index=++n.index,t.config=w.extend({},t.config,n.config,e),n.ready(function(){t.init()})},w=function(e){return new C(e)},C=function(e){for(var t=0,n="object"==typeof e?[e]:(this.selector=e,document.querySelectorAll(e||null));t0)return n[0].getAttribute(e)}():n.each(function(n,a){a.setAttribute(e,t)})},C.prototype.removeAttr=function(e){return this.each(function(t,n){n.removeAttribute(e)})},C.prototype.html=function(e){return this.each(function(t,n){n.innerHTML=e})},C.prototype.val=function(e){return this.each(function(t,n){n.value=e})},C.prototype.append=function(e){return this.each(function(t,n){"object"==typeof e?n.appendChild(e):n.innerHTML=n.innerHTML+e})},C.prototype.remove=function(e){return this.each(function(t,n){e?n.removeChild(e):n.parentNode.removeChild(n)})},C.prototype.on=function(e,t){return this.each(function(n,a){a.attachEvent?a.attachEvent("on"+e,function(e){e.target=e.srcElement,t.call(a,e)}):a.addEventListener(e,t,!1)})},C.prototype.off=function(e,t){return this.each(function(n,a){a.detachEvent?a.detachEvent("on"+e,t):a.removeEventListener(e,t,!1)})},T.isLeapYear=function(e){return e%4===0&&e%100!==0||e%400===0},T.prototype.config={type:"date",range:!1,format:"yyyy-MM-dd",value:null,min:"1900-1-1",max:"2099-12-31",trigger:"focus",show:!1,showBottom:!0,btns:["clear","now","confirm"],lang:"cn",theme:"default",position:null,calendar:!1,mark:{},zIndex:null,done:null,change:null},T.prototype.lang=function(){var e=this,t=e.config,n={cn:{weeks:["日","一","二","三","四","五","六"],time:["时","分","秒"],timeTips:"选择时间",startTime:"开始时间",endTime:"结束时间",dateTips:"返回日期",month:["一","二","三","四","五","六","七","八","九","十","十一","十二"],tools:{confirm:"确定",clear:"清空",now:"现在"}},en:{weeks:["Su","Mo","Tu","We","Th","Fr","Sa"],time:["Hours","Minutes","Seconds"],timeTips:"Select Time",startTime:"Start Time",endTime:"End Time",dateTips:"Select Date",month:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],tools:{confirm:"Confirm",clear:"Clear",now:"Now"}}};return n[t.lang]||n.cn},T.prototype.init=function(){var e=this,t=e.config,n="yyyy|y|MM|M|dd|d|HH|H|mm|m|ss|s",a="static"===t.position,i={year:"yyyy",month:"yyyy-MM",date:"yyyy-MM-dd",time:"HH:mm:ss",datetime:"yyyy-MM-dd HH:mm:ss"};t.elem=w(t.elem),t.eventElem=w(t.eventElem),t.elem[0]&&(t.range===!0&&(t.range="-"),t.format===i.date&&(t.format=i[t.type]),e.format=t.format.match(new RegExp(n+"|.","g"))||[],e.EXP_IF="",e.EXP_SPLIT="",w.each(e.format,function(t,a){var i=new RegExp(n).test(a)?"\\d{"+function(){return new RegExp(n).test(e.format[0===t?t+1:t-1]||"")?/^yyyy|y$/.test(a)?4:a.length:/^yyyy$/.test(a)?"1,4":/^y$/.test(a)?"1,308":"1,2"}()+"}":"\\"+a;e.EXP_IF=e.EXP_IF+i,e.EXP_SPLIT=e.EXP_SPLIT+"("+i+")"}),e.EXP_IF=new RegExp("^"+(t.range?e.EXP_IF+"\\s\\"+t.range+"\\s"+e.EXP_IF:e.EXP_IF)+"$"),e.EXP_SPLIT=new RegExp("^"+e.EXP_SPLIT+"$",""),e.isInput(t.elem[0])||"focus"===t.trigger&&(t.trigger="click"),t.elem.attr("lay-key")||(t.elem.attr("lay-key",e.index),t.eventElem.attr("lay-key",e.index)),t.mark=w.extend({},t.calendar&&"cn"===t.lang?{"0-1-1":"元旦","0-2-14":"情人","0-3-8":"妇女","0-3-12":"植树","0-4-1":"愚人","0-5-1":"劳动","0-5-4":"青年","0-6-1":"儿童","0-9-10":"教师","0-9-18":"国耻","0-10-1":"国庆","0-12-25":"圣诞"}:{},t.mark),w.each(["min","max"],function(e,n){var a=[],i=[];if("number"==typeof t[n]){var r=t[n],o=(new Date).getTime(),s=864e5,l=new Date(r?r0)return!0;var a=w.elem("div",{"class":"layui-laydate-header"}),i=[function(){var e=w.elem("i",{"class":"layui-icon laydate-icon laydate-prev-y"});return e.innerHTML="",e}(),function(){var e=w.elem("i",{"class":"layui-icon laydate-icon laydate-prev-m"});return e.innerHTML="",e}(),function(){var e=w.elem("div",{"class":"laydate-set-ym"}),t=w.elem("span"),n=w.elem("span");return e.appendChild(t),e.appendChild(n),e}(),function(){var e=w.elem("i",{"class":"layui-icon laydate-icon laydate-next-m"});return e.innerHTML="",e}(),function(){var e=w.elem("i",{"class":"layui-icon laydate-icon laydate-next-y"});return e.innerHTML="",e}()],d=w.elem("div",{"class":"layui-laydate-content"}),c=w.elem("table"),m=w.elem("thead"),u=w.elem("tr");w.each(i,function(e,t){a.appendChild(t)}),m.appendChild(u),w.each(new Array(6),function(e){var t=c.insertRow(0);w.each(new Array(7),function(a){if(0===e){var i=w.elem("th");i.innerHTML=n.weeks[a],u.appendChild(i)}t.insertCell(a)})}),c.insertBefore(m,c.children[0]),d.appendChild(c),r[e]=w.elem("div",{"class":"layui-laydate-main laydate-main-list-"+e}),r[e].appendChild(a),r[e].appendChild(d),o.push(i),s.push(d),l.push(c)}),w(d).html(function(){var e=[],i=[];return"datetime"===t.type&&e.push(''+n.timeTips+""),w.each(t.btns,function(e,r){var o=n.tools[r]||"btn";t.range&&"now"===r||(a&&"clear"===r&&(o="cn"===t.lang?"重置":"Reset"),i.push(''+o+""))}),e.push('"),e.join("")}()),w.each(r,function(e,t){i.appendChild(t)}),t.showBottom&&i.appendChild(d),/^#/.test(t.theme)){var m=w.elem("style"),u=["#{{id}} .layui-laydate-header{background-color:{{theme}};}","#{{id}} .layui-this{background-color:{{theme}} !important;}"].join("").replace(/{{id}}/g,e.elemID).replace(/{{theme}}/g,t.theme);"styleSheet"in m?(m.setAttribute("type","text/css"),m.styleSheet.cssText=u):m.innerHTML=u,w(i).addClass("laydate-theme-molv"),i.appendChild(m)}e.remove(T.thisElemDate),a?t.elem.append(i):(document.body.appendChild(i),e.position()),e.checkDate().calendar(),e.changeEvent(),T.thisElemDate=e.elemID,"function"==typeof t.ready&&t.ready(w.extend({},t.dateTime,{month:t.dateTime.month+1}))},T.prototype.remove=function(e){var t=this,n=(t.config,w("#"+(e||t.elemID)));return n.hasClass(c)||t.checkDate(function(){n.remove()}),t},T.prototype.position=function(){var e=this,t=e.config,n=e.bindElem||t.elem[0],a=n.getBoundingClientRect(),i=e.elem.offsetWidth,r=e.elem.offsetHeight,o=function(e){return e=e?"scrollLeft":"scrollTop",document.body[e]|document.documentElement[e]},s=function(e){return document.documentElement[e?"clientWidth":"clientHeight"]},l=5,d=a.left,c=a.bottom;d+i+l>s("width")&&(d=s("width")-i-l),c+r+l>s()&&(c=a.top>r?a.top-r:s()-r,c-=2*l),t.position&&(e.elem.style.position=t.position),e.elem.style.left=d+("fixed"===t.position?0:o(1))+"px",e.elem.style.top=c+("fixed"===t.position?0:o())+"px"},T.prototype.hint=function(e){var t=this,n=(t.config,w.elem("div",{"class":h}));n.innerHTML=e||"",w(t.elem).find("."+h).remove(),t.elem.appendChild(n),clearTimeout(t.hinTimer),t.hinTimer=setTimeout(function(){w(t.elem).find("."+h).remove()},3e3)},T.prototype.getAsYM=function(e,t,n){return n?t--:t++,t<0&&(t=11,e--),t>11&&(t=0,e++),[e,t]},T.prototype.systemDate=function(e){var t=e||new Date;return{year:t.getFullYear(),month:t.getMonth(),date:t.getDate(),hours:e?e.getHours():0,minutes:e?e.getMinutes():0,seconds:e?e.getSeconds():0}},T.prototype.checkDate=function(e){var t,a,i=this,r=(new Date,i.config),o=r.dateTime=r.dateTime||i.systemDate(),s=i.bindElem||r.elem[0],l=(i.isInput(s)?"val":"html",i.isInput(s)?s.value:"static"===r.position?"":s.innerHTML),c=function(e){e.year>d[1]&&(e.year=d[1],a=!0),e.month>11&&(e.month=11,a=!0),e.hours>23&&(e.hours=0,a=!0),e.minutes>59&&(e.minutes=0,e.hours++,a=!0),e.seconds>59&&(e.seconds=0,e.minutes++,a=!0),t=n.getEndDate(e.month+1,e.year),e.date>t&&(e.date=t,a=!0)},m=function(e,t,n){var o=["startTime","endTime"];t=(t.match(i.EXP_SPLIT)||[]).slice(1),n=n||0,r.range&&(i[o[n]]=i[o[n]]||{}),w.each(i.format,function(s,l){var c=parseFloat(t[s]);t[s].length必须遵循下述格式:
    "+(r.range?r.format+" "+r.range+" "+r.format:r.format)+"
    已为你重置"),a=!0):l&&l.constructor===Date?r.dateTime=i.systemDate(l):(r.dateTime=i.systemDate(),delete i.startState,delete i.endState,delete i.startDate,delete i.endDate,delete i.startTime,delete i.endTime),c(o),a&&l&&i.setValue(r.range?i.endDate?i.parse():"":i.parse()),e&&e(),i)},T.prototype.mark=function(e,t){var n,a=this,i=a.config;return w.each(i.mark,function(e,a){var i=e.split("-");i[0]!=t[0]&&0!=i[0]||i[1]!=t[1]&&0!=i[1]||i[2]!=t[2]||(n=a||t[2])}),n&&e.html(''+n+""),a},T.prototype.limit=function(e,t,n,a){var i,r=this,o=r.config,l={},d=o[n>41?"endDate":"dateTime"],c=w.extend({},d,t||{});return w.each({now:c,min:o.min,max:o.max},function(e,t){l[e]=r.newDate(w.extend({year:t.year,month:t.month,date:t.date},function(){var e={};return w.each(a,function(n,a){e[a]=t[a]}),e}())).getTime()}),i=l.nowl.max,e&&e[i?"addClass":"removeClass"](s),i},T.prototype.calendar=function(e){var t,a,i,r=this,s=r.config,l=e||s.dateTime,c=new Date,m=r.lang(),u="date"!==s.type&&"datetime"!==s.type,h=e?1:0,y=w(r.table[h]).find("td"),f=w(r.elemHeader[h][2]).find("span");if(l.yeard[1]&&(l.year=d[1],r.hint("最高只能支持到公元"+d[1]+"年")),r.firstDate||(r.firstDate=w.extend({},l)),c.setFullYear(l.year,l.month,1),t=c.getDay(),a=n.getEndDate(l.month||12,l.year),i=n.getEndDate(l.month+1,l.year),w.each(y,function(e,n){var d=[l.year,l.month],c=0;n=w(n),n.removeAttr("class"),e=t&&e=n.firstDate.year&&(r.month=a.max.month,r.date=a.max.date),n.limit(w(i),r,t),M++}),w(u[f?0:1]).attr("lay-ym",M-8+"-"+T[1]).html(b+p+" - "+(M-1+p))}else if("month"===e)w.each(new Array(12),function(e){var i=w.elem("li",{"lay-ym":e}),s={year:T[0],month:e};e+1==T[1]&&w(i).addClass(o),i.innerHTML=r.month[e]+(f?"月":""),d.appendChild(i),T[0]=n.firstDate.year&&(s.date=a.max.date),n.limit(w(i),s,t)}),w(u[f?0:1]).attr("lay-ym",T[0]+"-"+T[1]).html(T[0]+p);else if("time"===e){var E=function(){w(d).find("ol").each(function(e,a){w(a).find("li").each(function(a,i){n.limit(w(i),[{hours:a},{hours:n[x].hours,minutes:a},{hours:n[x].hours,minutes:n[x].minutes,seconds:a}][e],t,[["hours"],["hours","minutes"],["hours","minutes","seconds"]][e])})}),a.range||n.limit(w(n.footer).find(g),n[x],0,["hours","minutes","seconds"])};a.range?n[x]||(n[x]={hours:0,minutes:0,seconds:0}):n[x]=i,w.each([24,60,60],function(e,t){var a=w.elem("li"),i=["

    "+r.time[e]+"

      "];w.each(new Array(t),function(t){i.push(""+w.digit(t,2)+"")}),a.innerHTML=i.join("")+"
    ",d.appendChild(a)}),E()}if(y&&h.removeChild(y),h.appendChild(d),"year"===e||"month"===e)w(n.elemMain[t]).addClass("laydate-ym-show"),w(d).find("li").on("click",function(){var r=0|w(this).attr("lay-ym");if(!w(this).hasClass(s)){if(0===t)i[e]=r,l&&(n.startDate[e]=r),n.limit(w(n.footer).find(g),null,0);else if(l)n.endDate[e]=r;else{var c="year"===e?n.getAsYM(r,T[1]-1,"sub"):n.getAsYM(T[0],r,"sub");w.extend(i,{year:c[0],month:c[1]})}"year"===a.type||"month"===a.type?(w(d).find("."+o).removeClass(o),w(this).addClass(o),"month"===a.type&&"year"===e&&(n.listYM[t][0]=r,l&&(n[["startDate","endDate"][t]].year=r),n.list("month",t))):(n.checkDate("limit").calendar(),n.closeList()),n.setBtnStatus(),a.range||n.done(null,"change"),w(n.footer).find(D).removeClass(s)}});else{var S=w.elem("span",{"class":v}),k=function(){w(d).find("ol").each(function(e){var t=this,a=w(t).find("li");t.scrollTop=30*(n[x][C[e]]-2),t.scrollTop<=0&&a.each(function(e,n){if(!w(this).hasClass(s))return t.scrollTop=30*(e-2),!0})})},H=w(c[2]).find("."+v);k(),S.innerHTML=a.range?[r.startTime,r.endTime][t]:r.timeTips,w(n.elemMain[t]).addClass("laydate-time-show"),H[0]&&H.remove(),c[2].appendChild(S),w(d).find("ol").each(function(e){var t=this;w(t).find("li").on("click",function(){var r=0|this.innerHTML;w(this).hasClass(s)||(a.range?n[x][C[e]]=r:i[C[e]]=r,w(t).find("."+o).removeClass(o),w(this).addClass(o),E(),k(),(n.endDate||"time"===a.type)&&n.done(null,"change"),n.setBtnStatus())})})}return n},T.prototype.listYM=[],T.prototype.closeList=function(){var e=this;e.config;w.each(e.elemCont,function(t,n){w(this).find("."+m).remove(),w(e.elemMain[t]).removeClass("laydate-ym-show laydate-time-show")}),w(e.elem).find("."+v).remove()},T.prototype.setBtnStatus=function(e,t,n){var a,i=this,r=i.config,o=w(i.footer).find(g),d=r.range&&"date"!==r.type&&"time"!==r.type;d&&(t=t||i.startDate,n=n||i.endDate,a=i.newDate(t).getTime()>i.newDate(n).getTime(),i.limit(null,t)||i.limit(null,n)?o.addClass(s):o[a?"addClass":"removeClass"](s),e&&a&&i.hint("string"==typeof e?l.replace(/日期/g,e):l))},T.prototype.parse=function(e,t){var n=this,a=n.config,i=t||(e?w.extend({},n.endDate,n.endTime):a.range?w.extend({},n.startDate,n.startTime):a.dateTime),r=n.format.concat();return w.each(r,function(e,t){/yyyy|y/.test(t)?r[e]=w.digit(i.year,t.length):/MM|M/.test(t)?r[e]=w.digit(i.month+1,t.length):/dd|d/.test(t)?r[e]=w.digit(i.date,t.length):/HH|H/.test(t)?r[e]=w.digit(i.hours,t.length):/mm|m/.test(t)?r[e]=w.digit(i.minutes,t.length):/ss|s/.test(t)&&(r[e]=w.digit(i.seconds,t.length))}),a.range&&!e?r.join("")+" "+a.range+" "+n.parse(1):r.join("")},T.prototype.newDate=function(e){return e=e||{},new Date(e.year||1,e.month||0,e.date||1,e.hours||0,e.minutes||0,e.seconds||0)},T.prototype.setValue=function(e){var t=this,n=t.config,a=t.bindElem||n.elem[0],i=t.isInput(a)?"val":"html";return"static"===n.position||w(a)[i](e||""),this},T.prototype.stampRange=function(){var e,t,n=this,a=n.config,i=w(n.elem).find("td");if(a.range&&!n.endDate&&w(n.footer).find(g).addClass(s),n.endDate)return e=n.newDate({year:n.startDate.year,month:n.startDate.month,date:n.startDate.date}).getTime(),t=n.newDate({year:n.endDate.year,month:n.endDate.month,date:n.endDate.date}).getTime(),e>t?n.hint(l):void w.each(i,function(a,i){var r=w(i).attr("lay-ymd").split("-"),s=n.newDate({year:r[0],month:r[1]-1,date:r[2]}).getTime();w(i).removeClass(u+" "+o),s!==e&&s!==t||w(i).addClass(w(i).hasClass(y)||w(i).hasClass(f)?u:o),s>e&&s','
    '+f+"
    ",'
    ','',"
    ","
    "].join(""));return l.ie&&l.ie<8?c.removeClass("layui-hide").addClass(o):(d[0]&&d.remove(),s.call(a,m,c[0],y),c.addClass("layui-hide").after(m),a.index)},c.prototype.getContent=function(t){var e=u(t);if(e[0])return d(e[0].document.body.innerHTML)},c.prototype.getText=function(t){var i=u(t);if(i[0])return e(i[0].document.body).text()},c.prototype.setContent=function(t,i,a){var l=u(t);l[0]&&(a?e(l[0].document.body).append(i):e(l[0].document.body).html(i),layedit.sync(t))},c.prototype.sync=function(t){var i=u(t);if(i[0]){var a=e("#"+i[1].attr("textarea"));a.val(d(i[0].document.body.innerHTML))}},c.prototype.getSelection=function(t){var e=u(t);if(e[0]){var i=m(e[0].document);return document.selection?i.text:i.toString()}};var s=function(t,i,a){var l=this,n=t.find("iframe");n.css({height:a.height}).on("load",function(){var o=n.contents(),r=n.prop("contentWindow"),c=o.find("head"),s=e([""].join("")),u=o.find("body");c.append(s),u.attr("contenteditable","true").css({"min-height":a.height}).html(i.value||""),y.apply(l,[r,n,i,a]),g.call(l,r,t,a)})},u=function(t){var i=e("#LAY_layedit_"+t),a=i.prop("contentWindow");return[a,i]},d=function(t){return 8==l.ie&&(t=t.replace(/<.+>/g,function(t){return t.toLowerCase()})),t},y=function(t,a,n,o){var r=t.document,c=e(r.body);c.on("keydown",function(t){var e=t.keyCode;if(13===e){var a=m(r),l=p(a),n=l.parentNode;if("pre"===n.tagName.toLowerCase()){if(t.shiftKey)return;return i.msg("请暂时用shift+enter"),!1}r.execCommand("formatBlock",!1,"

    ")}}),e(n).parents("form").on("submit",function(){var t=c.html();8==l.ie&&(t=t.replace(/<.+>/g,function(t){return t.toLowerCase()})),n.value=t}),c.on("paste",function(e){r.execCommand("formatBlock",!1,"

    "),setTimeout(function(){f.call(t,c),n.value=c.html()},100)})},f=function(t){var i=this;i.document;t.find("*[style]").each(function(){var t=this.style.textAlign;this.removeAttribute("style"),e(this).css({"text-align":t||""})}),t.find("table").addClass("layui-table"),t.find("script,link").remove()},m=function(t){return t.selection?t.selection.createRange():t.getSelection().getRangeAt(0)},p=function(t){return t.endContainer||t.parentElement().childNodes[0]},v=function(t,i,a){var l=this.document,n=document.createElement(t);for(var o in i)n.setAttribute(o,i[o]);if(n.removeAttribute("text"),l.selection){var r=a.text||i.text;if("a"===t&&!r)return;r&&(n.innerHTML=r),a.pasteHTML(e(n).prop("outerHTML")),a.select()}else{var r=a.toString()||i.text;if("a"===t&&!r)return;r&&(n.innerHTML=r),a.deleteContents(),a.insertNode(n)}},h=function(t,i){var a=this.document,l="layedit-tool-active",n=p(m(a)),o=function(e){return t.find(".layedit-tool-"+e)};i&&i[i.hasClass(l)?"removeClass":"addClass"](l),t.find(">i").removeClass(l),o("unlink").addClass(r),e(n).parents().each(function(){var t=this.tagName.toLowerCase(),e=this.style.textAlign;"b"!==t&&"strong"!==t||o("b").addClass(l),"i"!==t&&"em"!==t||o("i").addClass(l),"u"===t&&o("u").addClass(l),"strike"===t&&o("d").addClass(l),"p"===t&&("center"===e?o("center").addClass(l):"right"===e?o("right").addClass(l):o("left").addClass(l)),"a"===t&&(o("link").addClass(l),o("unlink").removeClass(r))})},g=function(t,a,l){var n=t.document,o=e(n.body),c={link:function(i){var a=p(i),l=e(a).parent();b.call(o,{href:l.attr("href"),target:l.attr("target")},function(e){var a=l[0];"A"===a.tagName?a.href=e.url:v.call(t,"a",{target:e.target,href:e.url,text:e.url},i)})},unlink:function(t){n.execCommand("unlink")},face:function(e){x.call(this,function(i){v.call(t,"img",{src:i.src,alt:i.alt},e)})},image:function(a){var n=this;layui.use("upload",function(o){var r=l.uploadImage||{};o.render({url:r.url,method:r.type,elem:e(n).find("input")[0],done:function(e){0==e.code?(e.data=e.data||{},v.call(t,"img",{src:e.data.src,alt:e.data.title},a)):i.msg(e.msg||"上传失败")}})})},code:function(e){k.call(o,function(i){v.call(t,"pre",{text:i.code,"lay-lang":i.lang},e)})},help:function(){i.open({type:2,title:"帮助",area:["600px","380px"],shadeClose:!0,shade:.1,skin:"layui-layer-msg",content:["http://www.layui.com/about/layedit/help.html","no"]})}},s=a.find(".layui-layedit-tool"),u=function(){var i=e(this),a=i.attr("layedit-event"),l=i.attr("lay-command");if(!i.hasClass(r)){o.focus();var u=m(n);u.commonAncestorContainer;l?(n.execCommand(l),/justifyLeft|justifyCenter|justifyRight/.test(l)&&n.execCommand("formatBlock",!1,"

    "),setTimeout(function(){o.focus()},10)):c[a]&&c[a].call(this,u),h.call(t,s,i)}},d=/image/;s.find(">i").on("mousedown",function(){var t=e(this),i=t.attr("layedit-event");d.test(i)||u.call(this)}).on("click",function(){var t=e(this),i=t.attr("layedit-event");d.test(i)&&u.call(this)}),o.on("click",function(){h.call(t,s),i.close(x.index)})},b=function(t,e){var l=this,n=i.open({type:1,id:"LAY_layedit_link",area:"350px",shade:.05,shadeClose:!0,moveType:1,title:"超链接",skin:"layui-layer-msg",content:['

      ','
    • ','','
      ','',"
      ","
    • ",'
    • ','','
      ','",'","
      ","
    • ",'
    • ','','',"
    • ","
    "].join(""),success:function(t,n){var o="submit(layedit-link-yes)";a.render("radio"),t.find(".layui-btn-primary").on("click",function(){i.close(n),l.focus()}),a.on(o,function(t){i.close(b.index),e&&e(t.field)})}});b.index=n},x=function(t){var a=function(){var t=["[微笑]","[嘻嘻]","[哈哈]","[可爱]","[可怜]","[挖鼻]","[吃惊]","[害羞]","[挤眼]","[闭嘴]","[鄙视]","[爱你]","[泪]","[偷笑]","[亲亲]","[生病]","[太开心]","[白眼]","[右哼哼]","[左哼哼]","[嘘]","[衰]","[委屈]","[吐]","[哈欠]","[抱抱]","[怒]","[疑问]","[馋嘴]","[拜拜]","[思考]","[汗]","[困]","[睡]","[钱]","[失望]","[酷]","[色]","[哼]","[鼓掌]","[晕]","[悲伤]","[抓狂]","[黑线]","[阴险]","[怒骂]","[互粉]","[心]","[伤心]","[猪头]","[熊猫]","[兔子]","[ok]","[耶]","[good]","[NO]","[赞]","[来]","[弱]","[草泥马]","[神马]","[囧]","[浮云]","[给力]","[围观]","[威武]","[奥特曼]","[礼物]","[钟]","[话筒]","[蜡烛]","[蛋糕]"],e={};return layui.each(t,function(t,i){e[i]=layui.cache.dir+"images/face/"+t+".gif"}),e}();return x.hide=x.hide||function(t){"face"!==e(t.target).attr("layedit-event")&&i.close(x.index)},x.index=i.tips(function(){var t=[];return layui.each(a,function(e,i){t.push('
  • '+e+'
  • ')}),'
      '+t.join("")+"
    "}(),this,{tips:1,time:0,skin:"layui-box layui-util-face",maxWidth:500,success:function(l,n){l.css({marginTop:-4,marginLeft:-10}).find(".layui-clear>li").on("click",function(){t&&t({src:a[this.title],alt:this.title}),i.close(n)}),e(document).off("click",x.hide).on("click",x.hide)}})},k=function(t){var e=this,l=i.open({type:1,id:"LAY_layedit_code",area:"550px",shade:.05,shadeClose:!0,moveType:1,title:"插入代码",skin:"layui-layer-msg",content:['
      ','
    • ','','
      ','","
      ","
    • ",'
    • ','','
      ','',"
      ","
    • ",'
    • ','','',"
    • ","
    "].join(""),success:function(l,n){var o="submit(layedit-code-yes)";a.render("select"),l.find(".layui-btn-primary").on("click",function(){i.close(n),e.focus()}),a.on(o,function(e){i.close(k.index),t&&t(e.field)})}});k.index=l},C={html:'',strong:'',italic:'',underline:'',del:'',"|":'',left:'',center:'',right:'',link:'',unlink:'',face:'',image:'',code:'',help:''},w=new c;t(n,w)}); \ No newline at end of file diff --git a/main/webapp/static/lib/layui/lay/modules/layer.js b/main/webapp/static/lib/layui/lay/modules/layer.js new file mode 100644 index 0000000..18bf440 --- /dev/null +++ b/main/webapp/static/lib/layui/lay/modules/layer.js @@ -0,0 +1,2 @@ +/** layui-v2.2.6 MIT License By https://www.layui.com */ + ;!function(e,t){"use strict";var i,n,a=e.layui&&layui.define,o={getPath:function(){var e=document.currentScript?document.currentScript.src:function(){for(var e,t=document.scripts,i=t.length-1,n=i;n>0;n--)if("interactive"===t[n].readyState){e=t[n].src;break}return e||t[i].src}();return e.substring(0,e.lastIndexOf("/")+1)}(),config:{},end:{},minIndex:0,minLeft:[],btn:["确定","取消"],type:["dialog","page","iframe","loading","tips"],getStyle:function(t,i){var n=t.currentStyle?t.currentStyle:e.getComputedStyle(t,null);return n[n.getPropertyValue?"getPropertyValue":"getAttribute"](i)},link:function(t,i,n){if(r.path){var a=document.getElementsByTagName("head")[0],s=document.createElement("link");"string"==typeof i&&(n=i);var l=(n||t).replace(/\.|\//g,""),f="layuicss-"+l,c=0;s.rel="stylesheet",s.href=r.path+t,s.id=f,document.getElementById(f)||a.appendChild(s),"function"==typeof i&&!function u(){return++c>80?e.console&&console.error("layer.css: Invalid"):void(1989===parseInt(o.getStyle(document.getElementById(f),"width"))?i():setTimeout(u,100))}()}}},r={v:"3.1.1",ie:function(){var t=navigator.userAgent.toLowerCase();return!!(e.ActiveXObject||"ActiveXObject"in e)&&((t.match(/msie\s(\d+)/)||[])[1]||"11")}(),index:e.layer&&e.layer.v?1e5:0,path:o.getPath,config:function(e,t){return e=e||{},r.cache=o.config=i.extend({},o.config,e),r.path=o.config.path||r.path,"string"==typeof e.extend&&(e.extend=[e.extend]),o.config.path&&r.ready(),e.extend?(a?layui.addcss("modules/layer/"+e.extend):o.link("theme/"+e.extend),this):this},ready:function(e){var t="layer",i="",n=(a?"modules/layer/":"theme/")+"default/layer.css?v="+r.v+i;return a?layui.addcss(n,e,t):o.link(n,e,t),this},alert:function(e,t,n){var a="function"==typeof t;return a&&(n=t),r.open(i.extend({content:e,yes:n},a?{}:t))},confirm:function(e,t,n,a){var s="function"==typeof t;return s&&(a=n,n=t),r.open(i.extend({content:e,btn:o.btn,yes:n,btn2:a},s?{}:t))},msg:function(e,n,a){var s="function"==typeof n,f=o.config.skin,c=(f?f+" "+f+"-msg":"")||"layui-layer-msg",u=l.anim.length-1;return s&&(a=n),r.open(i.extend({content:e,time:3e3,shade:!1,skin:c,title:!1,closeBtn:!1,btn:!1,resize:!1,end:a},s&&!o.config.skin?{skin:c+" layui-layer-hui",anim:u}:function(){return n=n||{},(n.icon===-1||n.icon===t&&!o.config.skin)&&(n.skin=c+" "+(n.skin||"layui-layer-hui")),n}()))},load:function(e,t){return r.open(i.extend({type:3,icon:e||0,resize:!1,shade:.01},t))},tips:function(e,t,n){return r.open(i.extend({type:4,content:[e,t],closeBtn:!1,time:3e3,shade:!1,resize:!1,fixed:!1,maxWidth:210},n))}},s=function(e){var t=this;t.index=++r.index,t.config=i.extend({},t.config,o.config,e),document.body?t.creat():setTimeout(function(){t.creat()},30)};s.pt=s.prototype;var l=["layui-layer",".layui-layer-title",".layui-layer-main",".layui-layer-dialog","layui-layer-iframe","layui-layer-content","layui-layer-btn","layui-layer-close"];l.anim=["layer-anim-00","layer-anim-01","layer-anim-02","layer-anim-03","layer-anim-04","layer-anim-05","layer-anim-06"],s.pt.config={type:0,shade:.3,fixed:!0,move:l[1],title:"信息",offset:"auto",area:"auto",closeBtn:1,time:0,zIndex:19891014,maxWidth:360,anim:0,isOutAnim:!0,icon:-1,moveType:1,resize:!0,scrollbar:!0,tips:2},s.pt.vessel=function(e,t){var n=this,a=n.index,r=n.config,s=r.zIndex+a,f="object"==typeof r.title,c=r.maxmin&&(1===r.type||2===r.type),u=r.title?'
    '+(f?r.title[0]:r.title)+"
    ":"";return r.zIndex=s,t([r.shade?'
    ':"",'
    '+(e&&2!=r.type?"":u)+'
    '+(0==r.type&&r.icon!==-1?'':"")+(1==r.type&&e?"":r.content||"")+'
    '+function(){var e=c?'':"";return r.closeBtn&&(e+=''),e}()+""+(r.btn?function(){var e="";"string"==typeof r.btn&&(r.btn=[r.btn]);for(var t=0,i=r.btn.length;t'+r.btn[t]+"";return'
    '+e+"
    "}():"")+(r.resize?'':"")+"
    "],u,i('
    ')),n},s.pt.creat=function(){var e=this,t=e.config,a=e.index,s=t.content,f="object"==typeof s,c=i("body");if(!t.id||!i("#"+t.id)[0]){switch("string"==typeof t.area&&(t.area="auto"===t.area?["",""]:[t.area,""]),t.shift&&(t.anim=t.shift),6==r.ie&&(t.fixed=!1),t.type){case 0:t.btn="btn"in t?t.btn:o.btn[0],r.closeAll("dialog");break;case 2:var s=t.content=f?t.content:[t.content||"http://layer.layui.com","auto"];t.content='';break;case 3:delete t.title,delete t.closeBtn,t.icon===-1&&0===t.icon,r.closeAll("loading");break;case 4:f||(t.content=[t.content,"body"]),t.follow=t.content[1],t.content=t.content[0]+'',delete t.title,t.tips="object"==typeof t.tips?t.tips:[t.tips,!0],t.tipsMore||r.closeAll("tips")}if(e.vessel(f,function(n,r,u){c.append(n[0]),f?function(){2==t.type||4==t.type?function(){i("body").append(n[1])}():function(){s.parents("."+l[0])[0]||(s.data("display",s.css("display")).show().addClass("layui-layer-wrap").wrap(n[1]),i("#"+l[0]+a).find("."+l[5]).before(r))}()}():c.append(n[1]),i(".layui-layer-move")[0]||c.append(o.moveElem=u),e.layero=i("#"+l[0]+a),t.scrollbar||l.html.css("overflow","hidden").attr("layer-full",a)}).auto(a),i("#layui-layer-shade"+e.index).css({"background-color":t.shade[1]||"#000",opacity:t.shade[0]||t.shade}),2==t.type&&6==r.ie&&e.layero.find("iframe").attr("src",s[0]),4==t.type?e.tips():e.offset(),t.fixed&&n.on("resize",function(){e.offset(),(/^\d+%$/.test(t.area[0])||/^\d+%$/.test(t.area[1]))&&e.auto(a),4==t.type&&e.tips()}),t.time<=0||setTimeout(function(){r.close(e.index)},t.time),e.move().callback(),l.anim[t.anim]){var u="layer-anim "+l.anim[t.anim];e.layero.addClass(u).one("webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend",function(){i(this).removeClass(u)})}t.isOutAnim&&e.layero.data("isOutAnim",!0)}},s.pt.auto=function(e){var t=this,a=t.config,o=i("#"+l[0]+e);""===a.area[0]&&a.maxWidth>0&&(r.ie&&r.ie<8&&a.btn&&o.width(o.innerWidth()),o.outerWidth()>a.maxWidth&&o.width(a.maxWidth));var s=[o.innerWidth(),o.innerHeight()],f=o.find(l[1]).outerHeight()||0,c=o.find("."+l[6]).outerHeight()||0,u=function(e){e=o.find(e),e.height(s[1]-f-c-2*(0|parseFloat(e.css("padding-top"))))};switch(a.type){case 2:u("iframe");break;default:""===a.area[1]?a.maxHeight>0&&o.outerHeight()>a.maxHeight?(s[1]=a.maxHeight,u("."+l[5])):a.fixed&&s[1]>=n.height()&&(s[1]=n.height(),u("."+l[5])):u("."+l[5])}return t},s.pt.offset=function(){var e=this,t=e.config,i=e.layero,a=[i.outerWidth(),i.outerHeight()],o="object"==typeof t.offset;e.offsetTop=(n.height()-a[1])/2,e.offsetLeft=(n.width()-a[0])/2,o?(e.offsetTop=t.offset[0],e.offsetLeft=t.offset[1]||e.offsetLeft):"auto"!==t.offset&&("t"===t.offset?e.offsetTop=0:"r"===t.offset?e.offsetLeft=n.width()-a[0]:"b"===t.offset?e.offsetTop=n.height()-a[1]:"l"===t.offset?e.offsetLeft=0:"lt"===t.offset?(e.offsetTop=0,e.offsetLeft=0):"lb"===t.offset?(e.offsetTop=n.height()-a[1],e.offsetLeft=0):"rt"===t.offset?(e.offsetTop=0,e.offsetLeft=n.width()-a[0]):"rb"===t.offset?(e.offsetTop=n.height()-a[1],e.offsetLeft=n.width()-a[0]):e.offsetTop=t.offset),t.fixed||(e.offsetTop=/%$/.test(e.offsetTop)?n.height()*parseFloat(e.offsetTop)/100:parseFloat(e.offsetTop),e.offsetLeft=/%$/.test(e.offsetLeft)?n.width()*parseFloat(e.offsetLeft)/100:parseFloat(e.offsetLeft),e.offsetTop+=n.scrollTop(),e.offsetLeft+=n.scrollLeft()),i.attr("minLeft")&&(e.offsetTop=n.height()-(i.find(l[1]).outerHeight()||0),e.offsetLeft=i.css("left")),i.css({top:e.offsetTop,left:e.offsetLeft})},s.pt.tips=function(){var e=this,t=e.config,a=e.layero,o=[a.outerWidth(),a.outerHeight()],r=i(t.follow);r[0]||(r=i("body"));var s={width:r.outerWidth(),height:r.outerHeight(),top:r.offset().top,left:r.offset().left},f=a.find(".layui-layer-TipsG"),c=t.tips[0];t.tips[1]||f.remove(),s.autoLeft=function(){s.left+o[0]-n.width()>0?(s.tipLeft=s.left+s.width-o[0],f.css({right:12,left:"auto"})):s.tipLeft=s.left},s.where=[function(){s.autoLeft(),s.tipTop=s.top-o[1]-10,f.removeClass("layui-layer-TipsB").addClass("layui-layer-TipsT").css("border-right-color",t.tips[1])},function(){s.tipLeft=s.left+s.width+10,s.tipTop=s.top,f.removeClass("layui-layer-TipsL").addClass("layui-layer-TipsR").css("border-bottom-color",t.tips[1])},function(){s.autoLeft(),s.tipTop=s.top+s.height+10,f.removeClass("layui-layer-TipsT").addClass("layui-layer-TipsB").css("border-right-color",t.tips[1])},function(){s.tipLeft=s.left-o[0]-10,s.tipTop=s.top,f.removeClass("layui-layer-TipsR").addClass("layui-layer-TipsL").css("border-bottom-color",t.tips[1])}],s.where[c-1](),1===c?s.top-(n.scrollTop()+o[1]+16)<0&&s.where[2]():2===c?n.width()-(s.left+s.width+o[0]+16)>0||s.where[3]():3===c?s.top-n.scrollTop()+s.height+o[1]+16-n.height()>0&&s.where[0]():4===c&&o[0]+16-s.left>0&&s.where[1](),a.find("."+l[5]).css({"background-color":t.tips[1],"padding-right":t.closeBtn?"30px":""}),a.css({left:s.tipLeft-(t.fixed?n.scrollLeft():0),top:s.tipTop-(t.fixed?n.scrollTop():0)})},s.pt.move=function(){var e=this,t=e.config,a=i(document),s=e.layero,l=s.find(t.move),f=s.find(".layui-layer-resize"),c={};return t.move&&l.css("cursor","move"),l.on("mousedown",function(e){e.preventDefault(),t.move&&(c.moveStart=!0,c.offset=[e.clientX-parseFloat(s.css("left")),e.clientY-parseFloat(s.css("top"))],o.moveElem.css("cursor","move").show())}),f.on("mousedown",function(e){e.preventDefault(),c.resizeStart=!0,c.offset=[e.clientX,e.clientY],c.area=[s.outerWidth(),s.outerHeight()],o.moveElem.css("cursor","se-resize").show()}),a.on("mousemove",function(i){if(c.moveStart){var a=i.clientX-c.offset[0],o=i.clientY-c.offset[1],l="fixed"===s.css("position");if(i.preventDefault(),c.stX=l?0:n.scrollLeft(),c.stY=l?0:n.scrollTop(),!t.moveOut){var f=n.width()-s.outerWidth()+c.stX,u=n.height()-s.outerHeight()+c.stY;af&&(a=f),ou&&(o=u)}s.css({left:a,top:o})}if(t.resize&&c.resizeStart){var a=i.clientX-c.offset[0],o=i.clientY-c.offset[1];i.preventDefault(),r.style(e.index,{width:c.area[0]+a,height:c.area[1]+o}),c.isResize=!0,t.resizing&&t.resizing(s)}}).on("mouseup",function(e){c.moveStart&&(delete c.moveStart,o.moveElem.hide(),t.moveEnd&&t.moveEnd(s)),c.resizeStart&&(delete c.resizeStart,o.moveElem.hide())}),e},s.pt.callback=function(){function e(){var e=a.cancel&&a.cancel(t.index,n);e===!1||r.close(t.index)}var t=this,n=t.layero,a=t.config;t.openLayer(),a.success&&(2==a.type?n.find("iframe").on("load",function(){a.success(n,t.index)}):a.success(n,t.index)),6==r.ie&&t.IE6(n),n.find("."+l[6]).children("a").on("click",function(){var e=i(this).index();if(0===e)a.yes?a.yes(t.index,n):a.btn1?a.btn1(t.index,n):r.close(t.index);else{var o=a["btn"+(e+1)]&&a["btn"+(e+1)](t.index,n);o===!1||r.close(t.index)}}),n.find("."+l[7]).on("click",e),a.shadeClose&&i("#layui-layer-shade"+t.index).on("click",function(){r.close(t.index)}),n.find(".layui-layer-min").on("click",function(){var e=a.min&&a.min(n);e===!1||r.min(t.index,a)}),n.find(".layui-layer-max").on("click",function(){i(this).hasClass("layui-layer-maxmin")?(r.restore(t.index),a.restore&&a.restore(n)):(r.full(t.index,a),setTimeout(function(){a.full&&a.full(n)},100))}),a.end&&(o.end[t.index]=a.end)},o.reselect=function(){i.each(i("select"),function(e,t){var n=i(this);n.parents("."+l[0])[0]||1==n.attr("layer")&&i("."+l[0]).length<1&&n.removeAttr("layer").show(),n=null})},s.pt.IE6=function(e){i("select").each(function(e,t){var n=i(this);n.parents("."+l[0])[0]||"none"===n.css("display")||n.attr({layer:"1"}).hide(),n=null})},s.pt.openLayer=function(){var e=this;r.zIndex=e.config.zIndex,r.setTop=function(e){var t=function(){r.zIndex++,e.css("z-index",r.zIndex+1)};return r.zIndex=parseInt(e[0].style.zIndex),e.on("mousedown",t),r.zIndex}},o.record=function(e){var t=[e.width(),e.height(),e.position().top,e.position().left+parseFloat(e.css("margin-left"))];e.find(".layui-layer-max").addClass("layui-layer-maxmin"),e.attr({area:t})},o.rescollbar=function(e){l.html.attr("layer-full")==e&&(l.html[0].style.removeProperty?l.html[0].style.removeProperty("overflow"):l.html[0].style.removeAttribute("overflow"),l.html.removeAttr("layer-full"))},e.layer=r,r.getChildFrame=function(e,t){return t=t||i("."+l[4]).attr("times"),i("#"+l[0]+t).find("iframe").contents().find(e)},r.getFrameIndex=function(e){return i("#"+e).parents("."+l[4]).attr("times")},r.iframeAuto=function(e){if(e){var t=r.getChildFrame("html",e).outerHeight(),n=i("#"+l[0]+e),a=n.find(l[1]).outerHeight()||0,o=n.find("."+l[6]).outerHeight()||0;n.css({height:t+a+o}),n.find("iframe").css({height:t})}},r.iframeSrc=function(e,t){i("#"+l[0]+e).find("iframe").attr("src",t)},r.style=function(e,t,n){var a=i("#"+l[0]+e),r=a.find(".layui-layer-content"),s=a.attr("type"),f=a.find(l[1]).outerHeight()||0,c=a.find("."+l[6]).outerHeight()||0;a.attr("minLeft");s!==o.type[3]&&s!==o.type[4]&&(n||(parseFloat(t.width)<=260&&(t.width=260),parseFloat(t.height)-f-c<=64&&(t.height=64+f+c)),a.css(t),c=a.find("."+l[6]).outerHeight(),s===o.type[2]?a.find("iframe").css({height:parseFloat(t.height)-f-c}):r.css({height:parseFloat(t.height)-f-c-parseFloat(r.css("padding-top"))-parseFloat(r.css("padding-bottom"))}))},r.min=function(e,t){var a=i("#"+l[0]+e),s=a.find(l[1]).outerHeight()||0,f=a.attr("minLeft")||181*o.minIndex+"px",c=a.css("position");o.record(a),o.minLeft[0]&&(f=o.minLeft[0],o.minLeft.shift()),a.attr("position",c),r.style(e,{width:180,height:s,left:f,top:n.height()-s,position:"fixed",overflow:"hidden"},!0),a.find(".layui-layer-min").hide(),"page"===a.attr("type")&&a.find(l[4]).hide(),o.rescollbar(e),a.attr("minLeft")||o.minIndex++,a.attr("minLeft",f)},r.restore=function(e){var t=i("#"+l[0]+e),n=t.attr("area").split(",");t.attr("type");r.style(e,{width:parseFloat(n[0]),height:parseFloat(n[1]),top:parseFloat(n[2]),left:parseFloat(n[3]),position:t.attr("position"),overflow:"visible"},!0),t.find(".layui-layer-max").removeClass("layui-layer-maxmin"),t.find(".layui-layer-min").show(),"page"===t.attr("type")&&t.find(l[4]).show(),o.rescollbar(e)},r.full=function(e){var t,a=i("#"+l[0]+e);o.record(a),l.html.attr("layer-full")||l.html.css("overflow","hidden").attr("layer-full",e),clearTimeout(t),t=setTimeout(function(){var t="fixed"===a.css("position");r.style(e,{top:t?0:n.scrollTop(),left:t?0:n.scrollLeft(),width:n.width(),height:n.height()},!0),a.find(".layui-layer-min").hide()},100)},r.title=function(e,t){var n=i("#"+l[0]+(t||r.index)).find(l[1]);n.html(e)},r.close=function(e){var t=i("#"+l[0]+e),n=t.attr("type"),a="layer-anim-close";if(t[0]){var s="layui-layer-wrap",f=function(){if(n===o.type[1]&&"object"===t.attr("conType")){t.children(":not(."+l[5]+")").remove();for(var a=t.find("."+s),r=0;r<2;r++)a.unwrap();a.css("display",a.data("display")).removeClass(s)}else{if(n===o.type[2])try{var f=i("#"+l[4]+e)[0];f.contentWindow.document.write(""),f.contentWindow.close(),t.find("."+l[5])[0].removeChild(f)}catch(c){}t[0].innerHTML="",t.remove()}"function"==typeof o.end[e]&&o.end[e](),delete o.end[e]};t.data("isOutAnim")&&t.addClass("layer-anim "+a),i("#layui-layer-moves, #layui-layer-shade"+e).remove(),6==r.ie&&o.reselect(),o.rescollbar(e),t.attr("minLeft")&&(o.minIndex--,o.minLeft.push(t.attr("minLeft"))),r.ie&&r.ie<10||!t.data("isOutAnim")?f():setTimeout(function(){f()},200)}},r.closeAll=function(e){i.each(i("."+l[0]),function(){var t=i(this),n=e?t.attr("type")===e:1;n&&r.close(t.attr("times")),n=null})};var f=r.cache||{},c=function(e){return f.skin?" "+f.skin+" "+f.skin+"-"+e:""};r.prompt=function(e,t){var a="";if(e=e||{},"function"==typeof e&&(t=e),e.area){var o=e.area;a='style="width: '+o[0]+"; height: "+o[1]+';"',delete e.area}var s,l=2==e.formType?'":function(){return''}(),f=e.success;return delete e.success,r.open(i.extend({type:1,btn:["确定","取消"],content:l,skin:"layui-layer-prompt"+c("prompt"),maxWidth:n.width(),success:function(t){s=t.find(".layui-layer-input"),s.val(e.value||"").focus(),"function"==typeof f&&f(t)},resize:!1,yes:function(i){var n=s.val();""===n?s.focus():n.length>(e.maxlength||500)?r.tips("最多输入"+(e.maxlength||500)+"个字数",s,{tips:1}):t&&t(n,i,s)}},e))},r.tab=function(e){e=e||{};var t=e.tab||{},n="layui-this",a=e.success;return delete e.success,r.open(i.extend({type:1,skin:"layui-layer-tab"+c("tab"),resize:!1,title:function(){var e=t.length,i=1,a="";if(e>0)for(a=''+t[0].title+"";i"+t[i].title+"";return a}(),content:'
      '+function(){var e=t.length,i=1,a="";if(e>0)for(a='
    • '+(t[0].content||"no content")+"
    • ";i'+(t[i].content||"no content")+"";return a}()+"
    ",success:function(t){var o=t.find(".layui-layer-title").children(),r=t.find(".layui-layer-tabmain").children();o.on("mousedown",function(t){t.stopPropagation?t.stopPropagation():t.cancelBubble=!0;var a=i(this),o=a.index();a.addClass(n).siblings().removeClass(n),r.eq(o).show().siblings().hide(),"function"==typeof e.change&&e.change(o)}),"function"==typeof a&&a(t)}},e))},r.photos=function(t,n,a){function o(e,t,i){var n=new Image;return n.src=e,n.complete?t(n):(n.onload=function(){n.onload=null,t(n)},void(n.onerror=function(e){n.onerror=null,i(e)}))}var s={};if(t=t||{},t.photos){var l=t.photos.constructor===Object,f=l?t.photos:{},u=f.data||[],d=f.start||0;s.imgIndex=(0|d)+1,t.img=t.img||"img";var y=t.success;if(delete t.success,l){if(0===u.length)return r.msg("没有图片")}else{var p=i(t.photos),h=function(){u=[],p.find(t.img).each(function(e){var t=i(this);t.attr("layer-index",e),u.push({alt:t.attr("alt"),pid:t.attr("layer-pid"),src:t.attr("layer-src")||t.attr("src"),thumb:t.attr("src")})})};if(h(),0===u.length)return;if(n||p.on("click",t.img,function(){var e=i(this),n=e.attr("layer-index");r.photos(i.extend(t,{photos:{start:n,data:u,tab:t.tab},full:t.full}),!0),h()}),!n)return}s.imgprev=function(e){s.imgIndex--,s.imgIndex<1&&(s.imgIndex=u.length),s.tabimg(e)},s.imgnext=function(e,t){s.imgIndex++,s.imgIndex>u.length&&(s.imgIndex=1,t)||s.tabimg(e)},s.keyup=function(e){if(!s.end){var t=e.keyCode;e.preventDefault(),37===t?s.imgprev(!0):39===t?s.imgnext(!0):27===t&&r.close(s.index)}},s.tabimg=function(e){if(!(u.length<=1))return f.start=s.imgIndex-1,r.close(s.index),r.photos(t,!0,e)},s.event=function(){s.bigimg.hover(function(){s.imgsee.show()},function(){s.imgsee.hide()}),s.bigimg.find(".layui-layer-imgprev").on("click",function(e){e.preventDefault(),s.imgprev()}),s.bigimg.find(".layui-layer-imgnext").on("click",function(e){e.preventDefault(),s.imgnext()}),i(document).on("keyup",s.keyup)},s.loadi=r.load(1,{shade:!("shade"in t)&&.9,scrollbar:!1}),o(u[d].src,function(n){r.close(s.loadi),s.index=r.open(i.extend({type:1,id:"layui-layer-photos",area:function(){var a=[n.width,n.height],o=[i(e).width()-100,i(e).height()-100];if(!t.full&&(a[0]>o[0]||a[1]>o[1])){var r=[a[0]/o[0],a[1]/o[1]];r[0]>r[1]?(a[0]=a[0]/r[0],a[1]=a[1]/r[0]):r[0]'+(u[d].alt||
    '+(u.length>1?'':"")+'
    '+(u[d].alt||"")+""+s.imgIndex+"/"+u.length+"
    ",success:function(e,i){s.bigimg=e.find(".layui-layer-phimg"),s.imgsee=e.find(".layui-layer-imguide,.layui-layer-imgbar"),s.event(e),t.tab&&t.tab(u[d],e),"function"==typeof y&&y(e)},end:function(){s.end=!0,i(document).off("keyup",s.keyup)}},t))},function(){r.close(s.loadi),r.msg("当前图片地址异常
    是否继续查看下一张?",{time:3e4,btn:["下一张","不看了"],yes:function(){u.length>1&&s.imgnext(!0,!0)}})})}},o.run=function(t){i=t,n=i(e),l.html=i("html"),r.open=function(e){var t=new s(e);return t.index}},e.layui&&layui.define?(r.ready(),layui.define("jquery",function(t){r.path=layui.cache.dir,o.run(layui.$),e.layer=r,t("layer",r)})):"function"==typeof define&&define.amd?define(["jquery"],function(){return o.run(e.jQuery),r}):function(){o.run(e.jQuery),r.ready()}()}(window); \ No newline at end of file diff --git a/main/webapp/static/lib/layui/lay/modules/laypage.js b/main/webapp/static/lib/layui/lay/modules/laypage.js new file mode 100644 index 0000000..5b3b09b --- /dev/null +++ b/main/webapp/static/lib/layui/lay/modules/laypage.js @@ -0,0 +1,2 @@ +/** layui-v2.2.6 MIT License By https://www.layui.com */ + ;layui.define(function(e){"use strict";var a=document,t="getElementById",n="getElementsByTagName",i="laypage",r="layui-disabled",u=function(e){var a=this;a.config=e||{},a.config.index=++s.index,a.render(!0)};u.prototype.type=function(){var e=this.config;if("object"==typeof e.elem)return void 0===e.elem.length?2:3},u.prototype.view=function(){var e=this,a=e.config,t=a.groups="groups"in a?0|a.groups:5;a.layout="object"==typeof a.layout?a.layout:["prev","page","next"],a.count=0|a.count,a.curr=0|a.curr||1,a.limits="object"==typeof a.limits?a.limits:[10,20,30,40,50],a.limit=0|a.limit||10,a.pages=Math.ceil(a.count/a.limit)||1,a.curr>a.pages&&(a.curr=a.pages),t<0?t=1:t>a.pages&&(t=a.pages),a.prev="prev"in a?a.prev:"上一页",a.next="next"in a?a.next:"下一页";var n=a.pages>t?Math.ceil((a.curr+(t>1?1:0))/(t>0?t:1)):1,i={prev:function(){return a.prev?''+a.prev+"":""}(),page:function(){var e=[];if(a.count<1)return"";n>1&&a.first!==!1&&0!==t&&e.push(''+(a.first||1)+"");var i=Math.floor((t-1)/2),r=n>1?a.curr-i:1,u=n>1?function(){var e=a.curr+(t-i-1);return e>a.pages?a.pages:e}():t;for(u-r2&&e.push('');r<=u;r++)r===a.curr?e.push('"+r+""):e.push(''+r+"");return a.pages>t&&a.pages>u&&a.last!==!1&&(u+1…'),0!==t&&e.push(''+(a.last||a.pages)+"")),e.join("")}(),next:function(){return a.next?''+a.next+"":""}(),count:'共 '+a.count+" 条",limit:function(){var e=['"}(),skip:function(){return['到第','','页',""].join("")}()};return['
    ',function(){var e=[];return layui.each(a.layout,function(a,t){i[t]&&e.push(i[t])}),e.join("")}(),"
    "].join("")},u.prototype.jump=function(e,a){if(e){var t=this,i=t.config,r=e.children,u=e[n]("button")[0],l=e[n]("input")[0],p=e[n]("select")[0],c=function(){var e=0|l.value.replace(/\s|\D/g,"");e&&(i.curr=e,t.render())};if(a)return c();for(var o=0,y=r.length;oi.pages||(i.curr=e,t.render())});p&&s.on(p,"change",function(){var e=this.value;i.curr*e>i.count&&(i.curr=Math.ceil(i.count/e)),i.limit=e,t.render()}),u&&s.on(u,"click",function(){c()})}},u.prototype.skip=function(e){if(e){var a=this,t=e[n]("input")[0];t&&s.on(t,"keyup",function(t){var n=this.value,i=t.keyCode;/^(37|38|39|40)$/.test(i)||(/\D/.test(n)&&(this.value=n.replace(/\D/,"")),13===i&&a.jump(e,!0))})}},u.prototype.render=function(e){var n=this,i=n.config,r=n.type(),u=n.view();2===r?i.elem&&(i.elem.innerHTML=u):3===r?i.elem.html(u):a[t](i.elem)&&(a[t](i.elem).innerHTML=u),i.jump&&i.jump(i,e);var s=a[t]("layui-laypage-"+i.index);n.jump(s),i.hash&&!e&&(location.hash="!"+i.hash+"="+i.curr),n.skip(s)};var s={render:function(e){var a=new u(e);return a.index},index:layui.laypage?layui.laypage.index+1e4:0,on:function(e,a,t){return e.attachEvent?e.attachEvent("on"+a,function(a){a.target=a.srcElement,t.call(e,a)}):e.addEventListener(a,t,!1),this}};e(i,s)}); \ No newline at end of file diff --git a/main/webapp/static/lib/layui/lay/modules/laytpl.js b/main/webapp/static/lib/layui/lay/modules/laytpl.js new file mode 100644 index 0000000..e19885e --- /dev/null +++ b/main/webapp/static/lib/layui/lay/modules/laytpl.js @@ -0,0 +1,2 @@ +/** layui-v2.2.6 MIT License By https://www.layui.com */ + ;layui.define(function(e){"use strict";var r={open:"{{",close:"}}"},c={exp:function(e){return new RegExp(e,"g")},query:function(e,c,t){var o=["#([\\s\\S])+?","([^{#}])*?"][e||0];return n((c||"")+r.open+o+r.close+(t||""))},escape:function(e){return String(e||"").replace(/&(?!#?[a-zA-Z0-9]+;)/g,"&").replace(//g,">").replace(/'/g,"'").replace(/"/g,""")},error:function(e,r){var c="Laytpl Error:";return"object"==typeof console&&console.error(c+e+"\n"+(r||"")),c+e}},n=c.exp,t=function(e){this.tpl=e};t.pt=t.prototype,window.errors=0,t.pt.parse=function(e,t){var o=this,p=e,a=n("^"+r.open+"#",""),l=n(r.close+"$","");e=e.replace(/\s+|\r|\t|\n/g," ").replace(n(r.open+"#"),r.open+"# ").replace(n(r.close+"}"),"} "+r.close).replace(/\\/g,"\\\\").replace(n(r.open+"!(.+?)!"+r.close),function(e){return e=e.replace(n("^"+r.open+"!"),"").replace(n("!"+r.close),"").replace(n(r.open+"|"+r.close),function(e){return e.replace(/(.)/g,"\\$1")})}).replace(/(?="|')/g,"\\").replace(c.query(),function(e){return e=e.replace(a,"").replace(l,""),'";'+e.replace(/\\/g,"")+';view+="'}).replace(c.query(1),function(e){var c='"+(';return e.replace(/\s/g,"")===r.open+r.close?"":(e=e.replace(n(r.open+"|"+r.close),""),/^=/.test(e)&&(e=e.replace(/^=/,""),c='"+_escape_('),c+e.replace(/\\/g,"")+')+"')}),e='"use strict";var view = "'+e+'";return view;';try{return o.cache=e=new Function("d, _escape_",e),e(t,c.escape)}catch(u){return delete o.cache,c.error(u,p)}},t.pt.render=function(e,r){var n,t=this;return e?(n=t.cache?t.cache(e,c.escape):t.parse(t.tpl,e),r?void r(n):n):c.error("no data")};var o=function(e){return"string"!=typeof e?c.error("Template not found"):new t(e)};o.config=function(e){e=e||{};for(var c in e)r[c]=e[c]},o.v="1.2.0",e("laytpl",o)}); \ No newline at end of file diff --git a/main/webapp/static/lib/layui/lay/modules/mobile.js b/main/webapp/static/lib/layui/lay/modules/mobile.js new file mode 100644 index 0000000..1f292ac --- /dev/null +++ b/main/webapp/static/lib/layui/lay/modules/mobile.js @@ -0,0 +1,2 @@ +/** layui-v2.2.6 MIT License By https://www.layui.com */ + ;layui.define(function(i){i("layui.mobile",layui.v)});layui.define(function(e){"use strict";var r={open:"{{",close:"}}"},c={exp:function(e){return new RegExp(e,"g")},query:function(e,c,t){var o=["#([\\s\\S])+?","([^{#}])*?"][e||0];return n((c||"")+r.open+o+r.close+(t||""))},escape:function(e){return String(e||"").replace(/&(?!#?[a-zA-Z0-9]+;)/g,"&").replace(//g,">").replace(/'/g,"'").replace(/"/g,""")},error:function(e,r){var c="Laytpl Error:";return"object"==typeof console&&console.error(c+e+"\n"+(r||"")),c+e}},n=c.exp,t=function(e){this.tpl=e};t.pt=t.prototype,window.errors=0,t.pt.parse=function(e,t){var o=this,p=e,a=n("^"+r.open+"#",""),l=n(r.close+"$","");e=e.replace(/\s+|\r|\t|\n/g," ").replace(n(r.open+"#"),r.open+"# ").replace(n(r.close+"}"),"} "+r.close).replace(/\\/g,"\\\\").replace(n(r.open+"!(.+?)!"+r.close),function(e){return e=e.replace(n("^"+r.open+"!"),"").replace(n("!"+r.close),"").replace(n(r.open+"|"+r.close),function(e){return e.replace(/(.)/g,"\\$1")})}).replace(/(?="|')/g,"\\").replace(c.query(),function(e){return e=e.replace(a,"").replace(l,""),'";'+e.replace(/\\/g,"")+';view+="'}).replace(c.query(1),function(e){var c='"+(';return e.replace(/\s/g,"")===r.open+r.close?"":(e=e.replace(n(r.open+"|"+r.close),""),/^=/.test(e)&&(e=e.replace(/^=/,""),c='"+_escape_('),c+e.replace(/\\/g,"")+')+"')}),e='"use strict";var view = "'+e+'";return view;';try{return o.cache=e=new Function("d, _escape_",e),e(t,c.escape)}catch(u){return delete o.cache,c.error(u,p)}},t.pt.render=function(e,r){var n,t=this;return e?(n=t.cache?t.cache(e,c.escape):t.parse(t.tpl,e),r?void r(n):n):c.error("no data")};var o=function(e){return"string"!=typeof e?c.error("Template not found"):new t(e)};o.config=function(e){e=e||{};for(var c in e)r[c]=e[c]},o.v="1.2.0",e("laytpl",o)});layui.define(function(e){"use strict";var t=(window,document),i="querySelectorAll",n="getElementsByClassName",a=function(e){return t[i](e)},s={type:0,shade:!0,shadeClose:!0,fixed:!0,anim:"scale"},l={extend:function(e){var t=JSON.parse(JSON.stringify(s));for(var i in e)t[i]=e[i];return t},timer:{},end:{}};l.touch=function(e,t){e.addEventListener("click",function(e){t.call(this,e)},!1)};var o=0,r=["layui-m-layer"],d=function(e){var t=this;t.config=l.extend(e),t.view()};d.prototype.view=function(){var e=this,i=e.config,s=t.createElement("div");e.id=s.id=r[0]+o,s.setAttribute("class",r[0]+" "+r[0]+(i.type||0)),s.setAttribute("index",o);var l=function(){var e="object"==typeof i.title;return i.title?'

    '+(e?i.title[0]:i.title)+"

    ":""}(),d=function(){"string"==typeof i.btn&&(i.btn=[i.btn]);var e,t=(i.btn||[]).length;return 0!==t&&i.btn?(e=''+i.btn[0]+"",2===t&&(e=''+i.btn[1]+""+e),'
    '+e+"
    "):""}();if(i.fixed||(i.top=i.hasOwnProperty("top")?i.top:100,i.style=i.style||"",i.style+=" top:"+(t.body.scrollTop+i.top)+"px"),2===i.type&&(i.content='

    '+(i.content||"")+"

    "),i.skin&&(i.anim="up"),"msg"===i.skin&&(i.shade=!1),s.innerHTML=(i.shade?"
    ':"")+'
    "+l+'
    '+i.content+"
    "+d+"
    ",!i.type||2===i.type){var y=t[n](r[0]+i.type),u=y.length;u>=1&&c.close(y[0].getAttribute("index"))}document.body.appendChild(s);var m=e.elem=a("#"+e.id)[0];i.success&&i.success(m),e.index=o++,e.action(i,m)},d.prototype.action=function(e,t){var i=this;e.time&&(l.timer[i.index]=setTimeout(function(){c.close(i.index)},1e3*e.time));var a=function(){var t=this.getAttribute("type");0==t?(e.no&&e.no(),c.close(i.index)):e.yes?e.yes(i.index):c.close(i.index)};if(e.btn)for(var s=t[n]("layui-m-layerbtn")[0].children,o=s.length,r=0;r0&&e-1 in t)}function s(t){return A.call(t,function(t){return null!=t})}function u(t){return t.length>0?T.fn.concat.apply([],t):t}function c(t){return t.replace(/::/g,"/").replace(/([A-Z]+)([A-Z][a-z])/g,"$1_$2").replace(/([a-z\d])([A-Z])/g,"$1_$2").replace(/_/g,"-").toLowerCase()}function l(t){return t in F?F[t]:F[t]=new RegExp("(^|\\s)"+t+"(\\s|$)")}function f(t,e){return"number"!=typeof e||k[c(t)]?e:e+"px"}function h(t){var e,n;return $[t]||(e=L.createElement(t),L.body.appendChild(e),n=getComputedStyle(e,"").getPropertyValue("display"),e.parentNode.removeChild(e),"none"==n&&(n="block"),$[t]=n),$[t]}function p(t){return"children"in t?D.call(t.children):T.map(t.childNodes,function(t){if(1==t.nodeType)return t})}function d(t,e){var n,r=t?t.length:0;for(n=0;n]*>/,R=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,z=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,Z=/^(?:body|html)$/i,q=/([A-Z])/g,H=["val","css","html","text","data","width","height","offset"],I=["after","prepend","before","append"],V=L.createElement("table"),_=L.createElement("tr"),B={tr:L.createElement("tbody"),tbody:V,thead:V,tfoot:V,td:_,th:_,"*":L.createElement("div")},U=/complete|loaded|interactive/,X=/^[\w-]*$/,J={},W=J.toString,Y={},G=L.createElement("div"),K={tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},Q=Array.isArray||function(t){return t instanceof Array};return Y.matches=function(t,e){if(!e||!t||1!==t.nodeType)return!1;var n=t.matches||t.webkitMatchesSelector||t.mozMatchesSelector||t.oMatchesSelector||t.matchesSelector;if(n)return n.call(t,e);var r,i=t.parentNode,o=!i;return o&&(i=G).appendChild(t),r=~Y.qsa(i,e).indexOf(t),o&&G.removeChild(t),r},C=function(t){return t.replace(/-+(.)?/g,function(t,e){return e?e.toUpperCase():""})},N=function(t){return A.call(t,function(e,n){return t.indexOf(e)==n})},Y.fragment=function(t,e,n){var r,i,a;return R.test(t)&&(r=T(L.createElement(RegExp.$1))),r||(t.replace&&(t=t.replace(z,"<$1>")),e===E&&(e=M.test(t)&&RegExp.$1),e in B||(e="*"),a=B[e],a.innerHTML=""+t,r=T.each(D.call(a.childNodes),function(){a.removeChild(this)})),o(n)&&(i=T(r),T.each(n,function(t,e){H.indexOf(t)>-1?i[t](e):i.attr(t,e)})),r},Y.Z=function(t,e){return new d(t,e)},Y.isZ=function(t){return t instanceof Y.Z},Y.init=function(t,n){var r;if(!t)return Y.Z();if("string"==typeof t)if(t=t.trim(),"<"==t[0]&&M.test(t))r=Y.fragment(t,RegExp.$1,n),t=null;else{if(n!==E)return T(n).find(t);r=Y.qsa(L,t)}else{if(e(t))return T(L).ready(t);if(Y.isZ(t))return t;if(Q(t))r=s(t);else if(i(t))r=[t],t=null;else if(M.test(t))r=Y.fragment(t.trim(),RegExp.$1,n),t=null;else{if(n!==E)return T(n).find(t);r=Y.qsa(L,t)}}return Y.Z(r,t)},T=function(t,e){return Y.init(t,e)},T.extend=function(t){var e,n=D.call(arguments,1);return"boolean"==typeof t&&(e=t,t=n.shift()),n.forEach(function(n){m(t,n,e)}),t},Y.qsa=function(t,e){var n,r="#"==e[0],i=!r&&"."==e[0],o=r||i?e.slice(1):e,a=X.test(o);return t.getElementById&&a&&r?(n=t.getElementById(o))?[n]:[]:1!==t.nodeType&&9!==t.nodeType&&11!==t.nodeType?[]:D.call(a&&!r&&t.getElementsByClassName?i?t.getElementsByClassName(o):t.getElementsByTagName(e):t.querySelectorAll(e))},T.contains=L.documentElement.contains?function(t,e){return t!==e&&t.contains(e)}:function(t,e){for(;e&&(e=e.parentNode);)if(e===t)return!0;return!1},T.type=t,T.isFunction=e,T.isWindow=n,T.isArray=Q,T.isPlainObject=o,T.isEmptyObject=function(t){var e;for(e in t)return!1;return!0},T.isNumeric=function(t){var e=Number(t),n=typeof t;return null!=t&&"boolean"!=n&&("string"!=n||t.length)&&!isNaN(e)&&isFinite(e)||!1},T.inArray=function(t,e,n){return O.indexOf.call(e,t,n)},T.camelCase=C,T.trim=function(t){return null==t?"":String.prototype.trim.call(t)},T.uuid=0,T.support={},T.expr={},T.noop=function(){},T.map=function(t,e){var n,r,i,o=[];if(a(t))for(r=0;r=0?t:t+this.length]},toArray:function(){return this.get()},size:function(){return this.length},remove:function(){return this.each(function(){null!=this.parentNode&&this.parentNode.removeChild(this)})},each:function(t){return O.every.call(this,function(e,n){return t.call(e,n,e)!==!1}),this},filter:function(t){return e(t)?this.not(this.not(t)):T(A.call(this,function(e){return Y.matches(e,t)}))},add:function(t,e){return T(N(this.concat(T(t,e))))},is:function(t){return this.length>0&&Y.matches(this[0],t)},not:function(t){var n=[];if(e(t)&&t.call!==E)this.each(function(e){t.call(this,e)||n.push(this)});else{var r="string"==typeof t?this.filter(t):a(t)&&e(t.item)?D.call(t):T(t);this.forEach(function(t){r.indexOf(t)<0&&n.push(t)})}return T(n)},has:function(t){return this.filter(function(){return i(t)?T.contains(this,t):T(this).find(t).size()})},eq:function(t){return t===-1?this.slice(t):this.slice(t,+t+1)},first:function(){var t=this[0];return t&&!i(t)?t:T(t)},last:function(){var t=this[this.length-1];return t&&!i(t)?t:T(t)},find:function(t){var e,n=this;return e=t?"object"==typeof t?T(t).filter(function(){var t=this;return O.some.call(n,function(e){return T.contains(e,t)})}):1==this.length?T(Y.qsa(this[0],t)):this.map(function(){return Y.qsa(this,t)}):T()},closest:function(t,e){var n=[],i="object"==typeof t&&T(t);return this.each(function(o,a){for(;a&&!(i?i.indexOf(a)>=0:Y.matches(a,t));)a=a!==e&&!r(a)&&a.parentNode;a&&n.indexOf(a)<0&&n.push(a)}),T(n)},parents:function(t){for(var e=[],n=this;n.length>0;)n=T.map(n,function(t){if((t=t.parentNode)&&!r(t)&&e.indexOf(t)<0)return e.push(t),t});return v(e,t)},parent:function(t){return v(N(this.pluck("parentNode")),t)},children:function(t){return v(this.map(function(){return p(this)}),t)},contents:function(){return this.map(function(){return this.contentDocument||D.call(this.childNodes)})},siblings:function(t){return v(this.map(function(t,e){return A.call(p(e.parentNode),function(t){return t!==e})}),t)},empty:function(){return this.each(function(){this.innerHTML=""})},pluck:function(t){return T.map(this,function(e){return e[t]})},show:function(){return this.each(function(){"none"==this.style.display&&(this.style.display=""),"none"==getComputedStyle(this,"").getPropertyValue("display")&&(this.style.display=h(this.nodeName))})},replaceWith:function(t){return this.before(t).remove()},wrap:function(t){var n=e(t);if(this[0]&&!n)var r=T(t).get(0),i=r.parentNode||this.length>1;return this.each(function(e){T(this).wrapAll(n?t.call(this,e):i?r.cloneNode(!0):r)})},wrapAll:function(t){if(this[0]){T(this[0]).before(t=T(t));for(var e;(e=t.children()).length;)t=e.first();T(t).append(this)}return this},wrapInner:function(t){var n=e(t);return this.each(function(e){var r=T(this),i=r.contents(),o=n?t.call(this,e):t;i.length?i.wrapAll(o):r.append(o)})},unwrap:function(){return this.parent().each(function(){T(this).replaceWith(T(this).children())}),this},clone:function(){return this.map(function(){return this.cloneNode(!0)})},hide:function(){return this.css("display","none")},toggle:function(t){return this.each(function(){var e=T(this);(t===E?"none"==e.css("display"):t)?e.show():e.hide()})},prev:function(t){return T(this.pluck("previousElementSibling")).filter(t||"*")},next:function(t){return T(this.pluck("nextElementSibling")).filter(t||"*")},html:function(t){return 0 in arguments?this.each(function(e){var n=this.innerHTML;T(this).empty().append(g(this,t,e,n))}):0 in this?this[0].innerHTML:null},text:function(t){return 0 in arguments?this.each(function(e){var n=g(this,t,e,this.textContent);this.textContent=null==n?"":""+n}):0 in this?this.pluck("textContent").join(""):null},attr:function(t,e){var n;return"string"!=typeof t||1 in arguments?this.each(function(n){if(1===this.nodeType)if(i(t))for(j in t)y(this,j,t[j]);else y(this,t,g(this,e,n,this.getAttribute(t)))}):0 in this&&1==this[0].nodeType&&null!=(n=this[0].getAttribute(t))?n:E},removeAttr:function(t){return this.each(function(){1===this.nodeType&&t.split(" ").forEach(function(t){y(this,t)},this)})},prop:function(t,e){return t=K[t]||t,1 in arguments?this.each(function(n){this[t]=g(this,e,n,this[t])}):this[0]&&this[0][t]},removeProp:function(t){return t=K[t]||t,this.each(function(){delete this[t]})},data:function(t,e){var n="data-"+t.replace(q,"-$1").toLowerCase(),r=1 in arguments?this.attr(n,e):this.attr(n);return null!==r?b(r):E},val:function(t){return 0 in arguments?(null==t&&(t=""),this.each(function(e){this.value=g(this,t,e,this.value)})):this[0]&&(this[0].multiple?T(this[0]).find("option").filter(function(){return this.selected}).pluck("value"):this[0].value)},offset:function(t){if(t)return this.each(function(e){var n=T(this),r=g(this,t,e,n.offset()),i=n.offsetParent().offset(),o={top:r.top-i.top,left:r.left-i.left};"static"==n.css("position")&&(o.position="relative"),n.css(o)});if(!this.length)return null;if(L.documentElement!==this[0]&&!T.contains(L.documentElement,this[0]))return{top:0,left:0};var e=this[0].getBoundingClientRect();return{left:e.left+window.pageXOffset,top:e.top+window.pageYOffset,width:Math.round(e.width),height:Math.round(e.height)}},css:function(e,n){if(arguments.length<2){var r=this[0];if("string"==typeof e){if(!r)return;return r.style[C(e)]||getComputedStyle(r,"").getPropertyValue(e)}if(Q(e)){if(!r)return;var i={},o=getComputedStyle(r,"");return T.each(e,function(t,e){i[e]=r.style[C(e)]||o.getPropertyValue(e)}),i}}var a="";if("string"==t(e))n||0===n?a=c(e)+":"+f(e,n):this.each(function(){this.style.removeProperty(c(e))});else for(j in e)e[j]||0===e[j]?a+=c(j)+":"+f(j,e[j])+";":this.each(function(){this.style.removeProperty(c(j))});return this.each(function(){this.style.cssText+=";"+a})},index:function(t){return t?this.indexOf(T(t)[0]):this.parent().children().indexOf(this[0])},hasClass:function(t){return!!t&&O.some.call(this,function(t){return this.test(x(t))},l(t))},addClass:function(t){return t?this.each(function(e){if("className"in this){S=[];var n=x(this),r=g(this,t,e,n);r.split(/\s+/g).forEach(function(t){T(this).hasClass(t)||S.push(t)},this),S.length&&x(this,n+(n?" ":"")+S.join(" "))}}):this},removeClass:function(t){return this.each(function(e){if("className"in this){if(t===E)return x(this,"");S=x(this),g(this,t,e,S).split(/\s+/g).forEach(function(t){S=S.replace(l(t)," ")}),x(this,S.trim())}})},toggleClass:function(t,e){return t?this.each(function(n){var r=T(this),i=g(this,t,n,x(this));i.split(/\s+/g).forEach(function(t){(e===E?!r.hasClass(t):e)?r.addClass(t):r.removeClass(t)})}):this},scrollTop:function(t){if(this.length){var e="scrollTop"in this[0];return t===E?e?this[0].scrollTop:this[0].pageYOffset:this.each(e?function(){this.scrollTop=t}:function(){this.scrollTo(this.scrollX,t)})}},scrollLeft:function(t){if(this.length){var e="scrollLeft"in this[0];return t===E?e?this[0].scrollLeft:this[0].pageXOffset:this.each(e?function(){this.scrollLeft=t}:function(){this.scrollTo(t,this.scrollY)})}},position:function(){if(this.length){var t=this[0],e=this.offsetParent(),n=this.offset(),r=Z.test(e[0].nodeName)?{top:0,left:0}:e.offset();return n.top-=parseFloat(T(t).css("margin-top"))||0,n.left-=parseFloat(T(t).css("margin-left"))||0,r.top+=parseFloat(T(e[0]).css("border-top-width"))||0,r.left+=parseFloat(T(e[0]).css("border-left-width"))||0,{top:n.top-r.top,left:n.left-r.left}}},offsetParent:function(){return this.map(function(){for(var t=this.offsetParent||L.body;t&&!Z.test(t.nodeName)&&"static"==T(t).css("position");)t=t.offsetParent;return t})}},T.fn.detach=T.fn.remove,["width","height"].forEach(function(t){var e=t.replace(/./,function(t){return t[0].toUpperCase()});T.fn[t]=function(i){var o,a=this[0];return i===E?n(a)?a["inner"+e]:r(a)?a.documentElement["scroll"+e]:(o=this.offset())&&o[t]:this.each(function(e){a=T(this),a.css(t,g(this,i,e,a[t]()))})}}),I.forEach(function(e,n){var r=n%2;T.fn[e]=function(){var e,i,o=T.map(arguments,function(n){var r=[];return e=t(n),"array"==e?(n.forEach(function(t){return t.nodeType!==E?r.push(t):T.zepto.isZ(t)?r=r.concat(t.get()):void(r=r.concat(Y.fragment(t)))}),r):"object"==e||null==n?n:Y.fragment(n)}),a=this.length>1;return o.length<1?this:this.each(function(t,e){i=r?e:e.parentNode,e=0==n?e.nextSibling:1==n?e.firstChild:2==n?e:null;var s=T.contains(L.documentElement,i);o.forEach(function(t){if(a)t=t.cloneNode(!0);else if(!i)return T(t).remove();i.insertBefore(t,e),s&&w(t,function(t){if(!(null==t.nodeName||"SCRIPT"!==t.nodeName.toUpperCase()||t.type&&"text/javascript"!==t.type||t.src)){var e=t.ownerDocument?t.ownerDocument.defaultView:window;e.eval.call(e,t.innerHTML)}})})})},T.fn[r?e+"To":"insert"+(n?"Before":"After")]=function(t){return T(t)[e](this),this}}),Y.Z.prototype=d.prototype=T.fn,Y.uniq=N,Y.deserializeValue=b,T.zepto=Y,T}();!function(t){function e(t){return t._zid||(t._zid=h++)}function n(t,n,o,a){if(n=r(n),n.ns)var s=i(n.ns);return(v[e(t)]||[]).filter(function(t){return t&&(!n.e||t.e==n.e)&&(!n.ns||s.test(t.ns))&&(!o||e(t.fn)===e(o))&&(!a||t.sel==a)})}function r(t){var e=(""+t).split(".");return{e:e[0],ns:e.slice(1).sort().join(" ")}}function i(t){return new RegExp("(?:^| )"+t.replace(" "," .* ?")+"(?: |$)")}function o(t,e){return t.del&&!y&&t.e in x||!!e}function a(t){return b[t]||y&&x[t]||t}function s(n,i,s,u,l,h,p){var d=e(n),m=v[d]||(v[d]=[]);i.split(/\s/).forEach(function(e){if("ready"==e)return t(document).ready(s);var i=r(e);i.fn=s,i.sel=l,i.e in b&&(s=function(e){var n=e.relatedTarget;if(!n||n!==this&&!t.contains(this,n))return i.fn.apply(this,arguments)}),i.del=h;var d=h||s;i.proxy=function(t){if(t=c(t),!t.isImmediatePropagationStopped()){t.data=u;var e=d.apply(n,t._args==f?[t]:[t].concat(t._args));return e===!1&&(t.preventDefault(),t.stopPropagation()),e}},i.i=m.length,m.push(i),"addEventListener"in n&&n.addEventListener(a(i.e),i.proxy,o(i,p))})}function u(t,r,i,s,u){var c=e(t);(r||"").split(/\s/).forEach(function(e){n(t,e,i,s).forEach(function(e){delete v[c][e.i],"removeEventListener"in t&&t.removeEventListener(a(e.e),e.proxy,o(e,u))})})}function c(e,n){return!n&&e.isDefaultPrevented||(n||(n=e),t.each(T,function(t,r){var i=n[t];e[t]=function(){return this[r]=w,i&&i.apply(n,arguments)},e[r]=E}),e.timeStamp||(e.timeStamp=Date.now()),(n.defaultPrevented!==f?n.defaultPrevented:"returnValue"in n?n.returnValue===!1:n.getPreventDefault&&n.getPreventDefault())&&(e.isDefaultPrevented=w)),e}function l(t){var e,n={originalEvent:t};for(e in t)j.test(e)||t[e]===f||(n[e]=t[e]);return c(n,t)}var f,h=1,p=Array.prototype.slice,d=t.isFunction,m=function(t){return"string"==typeof t},v={},g={},y="onfocusin"in window,x={focus:"focusin",blur:"focusout"},b={mouseenter:"mouseover",mouseleave:"mouseout"};g.click=g.mousedown=g.mouseup=g.mousemove="MouseEvents",t.event={add:s,remove:u},t.proxy=function(n,r){var i=2 in arguments&&p.call(arguments,2);if(d(n)){var o=function(){return n.apply(r,i?i.concat(p.call(arguments)):arguments)};return o._zid=e(n),o}if(m(r))return i?(i.unshift(n[r],n),t.proxy.apply(null,i)):t.proxy(n[r],n);throw new TypeError("expected function")},t.fn.bind=function(t,e,n){return this.on(t,e,n)},t.fn.unbind=function(t,e){return this.off(t,e)},t.fn.one=function(t,e,n,r){return this.on(t,e,n,r,1)};var w=function(){return!0},E=function(){return!1},j=/^([A-Z]|returnValue$|layer[XY]$|webkitMovement[XY]$)/,T={preventDefault:"isDefaultPrevented",stopImmediatePropagation:"isImmediatePropagationStopped",stopPropagation:"isPropagationStopped"};t.fn.delegate=function(t,e,n){return this.on(e,t,n)},t.fn.undelegate=function(t,e,n){return this.off(e,t,n)},t.fn.live=function(e,n){return t(document.body).delegate(this.selector,e,n),this},t.fn.die=function(e,n){return t(document.body).undelegate(this.selector,e,n),this},t.fn.on=function(e,n,r,i,o){var a,c,h=this;return e&&!m(e)?(t.each(e,function(t,e){h.on(t,n,r,e,o)}),h):(m(n)||d(i)||i===!1||(i=r,r=n,n=f),i!==f&&r!==!1||(i=r,r=f),i===!1&&(i=E),h.each(function(f,h){o&&(a=function(t){return u(h,t.type,i),i.apply(this,arguments)}),n&&(c=function(e){var r,o=t(e.target).closest(n,h).get(0);if(o&&o!==h)return r=t.extend(l(e),{currentTarget:o,liveFired:h}),(a||i).apply(o,[r].concat(p.call(arguments,1)))}),s(h,e,i,r,n,c||a)}))},t.fn.off=function(e,n,r){var i=this;return e&&!m(e)?(t.each(e,function(t,e){i.off(t,n,e)}),i):(m(n)||d(r)||r===!1||(r=n,n=f),r===!1&&(r=E),i.each(function(){u(this,e,r,n)}))},t.fn.trigger=function(e,n){return e=m(e)||t.isPlainObject(e)?t.Event(e):c(e),e._args=n,this.each(function(){e.type in x&&"function"==typeof this[e.type]?this[e.type]():"dispatchEvent"in this?this.dispatchEvent(e):t(this).triggerHandler(e,n)})},t.fn.triggerHandler=function(e,r){var i,o;return this.each(function(a,s){i=l(m(e)?t.Event(e):e),i._args=r,i.target=s,t.each(n(s,e.type||e),function(t,e){if(o=e.proxy(i),i.isImmediatePropagationStopped())return!1})}),o},"focusin focusout focus blur load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select keydown keypress keyup error".split(" ").forEach(function(e){t.fn[e]=function(t){return 0 in arguments?this.bind(e,t):this.trigger(e)}}),t.Event=function(t,e){m(t)||(e=t,t=e.type);var n=document.createEvent(g[t]||"Events"),r=!0;if(e)for(var i in e)"bubbles"==i?r=!!e[i]:n[i]=e[i];return n.initEvent(t,r,!0),c(n)}}(e),function(t){function e(e,n,r){var i=t.Event(n);return t(e).trigger(i,r),!i.isDefaultPrevented()}function n(t,n,r,i){if(t.global)return e(n||x,r,i)}function r(e){e.global&&0===t.active++&&n(e,null,"ajaxStart")}function i(e){e.global&&!--t.active&&n(e,null,"ajaxStop")}function o(t,e){var r=e.context;return e.beforeSend.call(r,t,e)!==!1&&n(e,r,"ajaxBeforeSend",[t,e])!==!1&&void n(e,r,"ajaxSend",[t,e])}function a(t,e,r,i){var o=r.context,a="success";r.success.call(o,t,a,e),i&&i.resolveWith(o,[t,a,e]),n(r,o,"ajaxSuccess",[e,r,t]),u(a,e,r)}function s(t,e,r,i,o){var a=i.context;i.error.call(a,r,e,t),o&&o.rejectWith(a,[r,e,t]),n(i,a,"ajaxError",[r,i,t||e]),u(e,r,i)}function u(t,e,r){var o=r.context;r.complete.call(o,e,t),n(r,o,"ajaxComplete",[e,r]),i(r)}function c(t,e,n){if(n.dataFilter==l)return t;var r=n.context;return n.dataFilter.call(r,t,e)}function l(){}function f(t){return t&&(t=t.split(";",2)[0]),t&&(t==T?"html":t==j?"json":w.test(t)?"script":E.test(t)&&"xml")||"text"}function h(t,e){return""==e?t:(t+"&"+e).replace(/[&?]{1,2}/,"?")}function p(e){e.processData&&e.data&&"string"!=t.type(e.data)&&(e.data=t.param(e.data,e.traditional)),!e.data||e.type&&"GET"!=e.type.toUpperCase()&&"jsonp"!=e.dataType||(e.url=h(e.url,e.data),e.data=void 0)}function d(e,n,r,i){return t.isFunction(n)&&(i=r,r=n,n=void 0),t.isFunction(r)||(i=r,r=void 0),{url:e,data:n,success:r,dataType:i}}function m(e,n,r,i){var o,a=t.isArray(n),s=t.isPlainObject(n);t.each(n,function(n,u){o=t.type(u),i&&(n=r?i:i+"["+(s||"object"==o||"array"==o?n:"")+"]"),!i&&a?e.add(u.name,u.value):"array"==o||!r&&"object"==o?m(e,u,r,n):e.add(n,u)})}var v,g,y=+new Date,x=window.document,b=/)<[^<]*)*<\/script>/gi,w=/^(?:text|application)\/javascript/i,E=/^(?:text|application)\/xml/i,j="application/json",T="text/html",S=/^\s*$/,C=x.createElement("a");C.href=window.location.href,t.active=0,t.ajaxJSONP=function(e,n){if(!("type"in e))return t.ajax(e);var r,i,u=e.jsonpCallback,c=(t.isFunction(u)?u():u)||"Zepto"+y++,l=x.createElement("script"),f=window[c],h=function(e){t(l).triggerHandler("error",e||"abort")},p={abort:h};return n&&n.promise(p),t(l).on("load error",function(o,u){clearTimeout(i),t(l).off().remove(),"error"!=o.type&&r?a(r[0],p,e,n):s(null,u||"error",p,e,n),window[c]=f,r&&t.isFunction(f)&&f(r[0]),f=r=void 0}),o(p,e)===!1?(h("abort"),p):(window[c]=function(){r=arguments},l.src=e.url.replace(/\?(.+)=\?/,"?$1="+c),x.head.appendChild(l),e.timeout>0&&(i=setTimeout(function(){h("timeout")},e.timeout)),p)},t.ajaxSettings={type:"GET",beforeSend:l,success:l,error:l,complete:l,context:null,global:!0,xhr:function(){return new window.XMLHttpRequest},accepts:{script:"text/javascript, application/javascript, application/x-javascript",json:j,xml:"application/xml, text/xml",html:T,text:"text/plain"},crossDomain:!1,timeout:0,processData:!0,cache:!0,dataFilter:l},t.ajax=function(e){var n,i,u=t.extend({},e||{}),d=t.Deferred&&t.Deferred();for(v in t.ajaxSettings)void 0===u[v]&&(u[v]=t.ajaxSettings[v]);r(u),u.crossDomain||(n=x.createElement("a"),n.href=u.url,n.href=n.href,u.crossDomain=C.protocol+"//"+C.host!=n.protocol+"//"+n.host),u.url||(u.url=window.location.toString()),(i=u.url.indexOf("#"))>-1&&(u.url=u.url.slice(0,i)),p(u);var m=u.dataType,y=/\?.+=\?/.test(u.url);if(y&&(m="jsonp"),u.cache!==!1&&(e&&e.cache===!0||"script"!=m&&"jsonp"!=m)||(u.url=h(u.url,"_="+Date.now())),"jsonp"==m)return y||(u.url=h(u.url,u.jsonp?u.jsonp+"=?":u.jsonp===!1?"":"callback=?")),t.ajaxJSONP(u,d);var b,w=u.accepts[m],E={},j=function(t,e){E[t.toLowerCase()]=[t,e]},T=/^([\w-]+:)\/\//.test(u.url)?RegExp.$1:window.location.protocol,N=u.xhr(),O=N.setRequestHeader;if(d&&d.promise(N),u.crossDomain||j("X-Requested-With","XMLHttpRequest"),j("Accept",w||"*/*"),(w=u.mimeType||w)&&(w.indexOf(",")>-1&&(w=w.split(",",2)[0]),N.overrideMimeType&&N.overrideMimeType(w)),(u.contentType||u.contentType!==!1&&u.data&&"GET"!=u.type.toUpperCase())&&j("Content-Type",u.contentType||"application/x-www-form-urlencoded"),u.headers)for(g in u.headers)j(g,u.headers[g]);if(N.setRequestHeader=j,N.onreadystatechange=function(){if(4==N.readyState){N.onreadystatechange=l,clearTimeout(b);var e,n=!1;if(N.status>=200&&N.status<300||304==N.status||0==N.status&&"file:"==T){if(m=m||f(u.mimeType||N.getResponseHeader("content-type")),"arraybuffer"==N.responseType||"blob"==N.responseType)e=N.response;else{e=N.responseText;try{e=c(e,m,u),"script"==m?(0,eval)(e):"xml"==m?e=N.responseXML:"json"==m&&(e=S.test(e)?null:t.parseJSON(e))}catch(r){n=r}if(n)return s(n,"parsererror",N,u,d)}a(e,N,u,d)}else s(N.statusText||null,N.status?"error":"abort",N,u,d)}},o(N,u)===!1)return N.abort(),s(null,"abort",N,u,d),N;var P=!("async"in u)||u.async;if(N.open(u.type,u.url,P,u.username,u.password),u.xhrFields)for(g in u.xhrFields)N[g]=u.xhrFields[g];for(g in E)O.apply(N,E[g]);return u.timeout>0&&(b=setTimeout(function(){N.onreadystatechange=l,N.abort(),s(null,"timeout",N,u,d)},u.timeout)),N.send(u.data?u.data:null),N},t.get=function(){return t.ajax(d.apply(null,arguments))},t.post=function(){var e=d.apply(null,arguments);return e.type="POST",t.ajax(e)},t.getJSON=function(){var e=d.apply(null,arguments);return e.dataType="json",t.ajax(e)},t.fn.load=function(e,n,r){if(!this.length)return this;var i,o=this,a=e.split(/\s/),s=d(e,n,r),u=s.success;return a.length>1&&(s.url=a[0],i=a[1]),s.success=function(e){o.html(i?t("
    ").html(e.replace(b,"")).find(i):e),u&&u.apply(o,arguments)},t.ajax(s),this};var N=encodeURIComponent;t.param=function(e,n){var r=[];return r.add=function(e,n){t.isFunction(n)&&(n=n()),null==n&&(n=""),this.push(N(e)+"="+N(n))},m(r,e,n),r.join("&").replace(/%20/g,"+")}}(e),function(t){t.fn.serializeArray=function(){var e,n,r=[],i=function(t){return t.forEach?t.forEach(i):void r.push({name:e,value:t})};return this[0]&&t.each(this[0].elements,function(r,o){n=o.type,e=o.name,e&&"fieldset"!=o.nodeName.toLowerCase()&&!o.disabled&&"submit"!=n&&"reset"!=n&&"button"!=n&&"file"!=n&&("radio"!=n&&"checkbox"!=n||o.checked)&&i(t(o).val())}),r},t.fn.serialize=function(){var t=[];return this.serializeArray().forEach(function(e){t.push(encodeURIComponent(e.name)+"="+encodeURIComponent(e.value))}),t.join("&")},t.fn.submit=function(e){if(0 in arguments)this.bind("submit",e);else if(this.length){var n=t.Event("submit");this.eq(0).trigger(n),n.isDefaultPrevented()||this.get(0).submit()}return this}}(e),function(){try{getComputedStyle(void 0)}catch(t){var e=getComputedStyle;window.getComputedStyle=function(t,n){try{return e(t,n)}catch(r){return null}}}}(),t("zepto",e)});layui.define(["layer-mobile","zepto"],function(e){"use strict";var t=layui.zepto,a=layui["layer-mobile"],i=(layui.device(),"layui-upload-enter"),n="layui-upload-iframe",r={icon:2,shift:6},o={file:"文件",video:"视频",audio:"音频"};a.msg=function(e){return a.open({content:e||"",skin:"msg",time:2})};var s=function(e){this.options=e};s.prototype.init=function(){var e=this,a=e.options,r=t("body"),s=t(a.elem||".layui-upload-file"),u=t('');return t("#"+n)[0]||r.append(u),s.each(function(r,s){s=t(s);var u='
    ',l=s.attr("lay-type")||a.type;a.unwrap||(u='
    '+u+''+(s.attr("lay-title")||a.title||"上传"+(o[l]||"图片"))+"
    "),u=t(u),a.unwrap||u.on("dragover",function(e){e.preventDefault(),t(this).addClass(i)}).on("dragleave",function(){t(this).removeClass(i)}).on("drop",function(){t(this).removeClass(i)}),s.parent("form").attr("target")===n&&(a.unwrap?s.unwrap():(s.parent().next().remove(),s.unwrap().unwrap())),s.wrap(u),s.off("change").on("change",function(){e.action(this,l)})})},s.prototype.action=function(e,i){var o=this,s=o.options,u=e.value,l=t(e),p=l.attr("lay-ext")||s.ext||"";if(u){switch(i){case"file":if(p&&!RegExp("\\w\\.("+p+")$","i").test(escape(u)))return a.msg("不支持该文件格式",r),e.value="";break;case"video":if(!RegExp("\\w\\.("+(p||"avi|mp4|wma|rmvb|rm|flash|3gp|flv")+")$","i").test(escape(u)))return a.msg("不支持该视频格式",r),e.value="";break;case"audio":if(!RegExp("\\w\\.("+(p||"mp3|wav|mid")+")$","i").test(escape(u)))return a.msg("不支持该音频格式",r),e.value="";break;default:if(!RegExp("\\w\\.("+(p||"jpg|png|gif|bmp|jpeg")+")$","i").test(escape(u)))return a.msg("不支持该图片格式",r),e.value=""}s.before&&s.before(e),l.parent().submit();var c=t("#"+n),f=setInterval(function(){var t;try{t=c.contents().find("body").text()}catch(i){a.msg("上传接口存在跨域",r),clearInterval(f)}if(t){clearInterval(f),c.contents().find("body").html("");try{t=JSON.parse(t)}catch(i){return t={},a.msg("请对上传接口返回JSON字符",r)}"function"==typeof s.success&&s.success(t,e)}},30);e.value=""}},e("upload-mobile",function(e){var t=new s(e=e||{});t.init()})});layui.define(function(i){i("layim-mobile",layui.v)});layui["layui.mobile"]||layui.config({base:layui.cache.dir+"lay/modules/mobile/"}).extend({"layer-mobile":"layer-mobile",zepto:"zepto","upload-mobile":"upload-mobile","layim-mobile":"layim-mobile"}),layui.define(["layer-mobile","zepto","layim-mobile"],function(l){l("mobile",{layer:layui["layer-mobile"],layim:layui["layim-mobile"]})}); \ No newline at end of file diff --git a/main/webapp/static/lib/layui/lay/modules/table.js b/main/webapp/static/lib/layui/lay/modules/table.js new file mode 100644 index 0000000..9e76e10 --- /dev/null +++ b/main/webapp/static/lib/layui/lay/modules/table.js @@ -0,0 +1,2 @@ +/** layui-v2.2.6 MIT License By https://www.layui.com */ + ;layui.define(["laytpl","laypage","layer","form"],function(e){"use strict";var t=layui.$,i=layui.laytpl,a=layui.laypage,l=layui.layer,n=layui.form,o=layui.hint(),r=layui.device(),d={config:{checkName:"LAY_CHECKED",indexName:"LAY_TABLE_INDEX"},cache:{},index:layui.table?layui.table.index+1e4:0,set:function(e){var i=this;return i.config=t.extend({},i.config,e),i},on:function(e,t){return layui.onevent.call(this,s,e,t)}},c=function(){var e=this,t=e.config,i=t.id;return i&&(c.config[i]=t),{reload:function(t){e.reload.call(e,t)},config:t}},s="table",u=".layui-table",h="layui-hide",f="layui-none",y="layui-table-view",p=".layui-table-header",m=".layui-table-body",v=".layui-table-main",g=".layui-table-fixed",x=".layui-table-fixed-l",b=".layui-table-fixed-r",k=".layui-table-tool",C=".layui-table-page",w=".layui-table-sort",N="layui-table-edit",T="layui-table-hover",F=function(e){var t='{{#if(item2.colspan){}} colspan="{{item2.colspan}}"{{#} if(item2.rowspan){}} rowspan="{{item2.rowspan}}"{{#}}}';return e=e||{},['',"","{{# layui.each(d.data.cols, function(i1, item1){ }}","","{{# layui.each(item1, function(i2, item2){ }}",'{{# if(item2.fixed && item2.fixed !== "right"){ left = true; } }}','{{# if(item2.fixed === "right"){ right = true; } }}',function(){return e.fixed&&"right"!==e.fixed?'{{# if(item2.fixed && item2.fixed !== "right"){ }}':"right"===e.fixed?'{{# if(item2.fixed === "right"){ }}':""}(),'",e.fixed?"{{# }; }}":"","{{# }); }}","","{{# }); }}","","
    ','
    1){ }}","group","{{# } else { }}","{{d.index}}-{{item2.field || i2}}",'{{# if(item2.type !== "normal"){ }}'," laytable-cell-{{ item2.type }}","{{# } }}","{{# } }}",'" {{#if(item2.align){}}align="{{item2.align}}"{{#}}}>','{{# if(item2.type === "checkbox"){ }}','',"{{# } else { }}",'{{item2.title||""}}',"{{# if(!(item2.colspan > 1) && item2.sort){ }}",'',"{{# } }}","{{# } }}","
    ","
    "].join("")},W=['',"","
    "].join(""),z=['
    ',"{{# if(d.data.toolbar){ }}",'
    ',"{{# } }}",'
    ',"{{# var left, right; }}",'
    ',F(),"
    ",'
    ',W,"
    ","{{# if(left){ }}",'
    ','
    ',F({fixed:!0}),"
    ",'
    ',W,"
    ","
    ","{{# }; }}","{{# if(right){ }}",'
    ','
    ',F({fixed:"right"}),'
    ',"
    ",'
    ',W,"
    ","
    ","{{# }; }}","
    ","{{# if(d.data.page){ }}",'
    ','
    ',"
    ","{{# } }}","","
    "].join(""),A=t(window),S=t(document),M=function(e){var i=this;i.index=++d.index,i.config=t.extend({},i.config,d.config,e),i.render()};M.prototype.config={limit:10,loading:!0,cellMinWidth:60,text:{none:"无数据"}},M.prototype.render=function(){var e=this,a=e.config;if(a.elem=t(a.elem),a.where=a.where||{},a.id=a.id||a.elem.attr("id"),a.request=t.extend({pageName:"page",limitName:"limit"},a.request),a.response=t.extend({statusName:"code",statusCode:0,msgName:"msg",dataName:"data",countName:"count"},a.response),"object"==typeof a.page&&(a.limit=a.page.limit||a.limit,a.limits=a.page.limits||a.limits,e.page=a.page.curr=a.page.curr||1,delete a.page.elem,delete a.page.jump),!a.elem[0])return e;e.setArea();var l=a.elem,n=l.next("."+y),o=e.elem=t(i(z).render({VIEW_CLASS:y,data:a,index:e.index}));if(a.index=e.index,n[0]&&n.remove(),l.after(o),e.layHeader=o.find(p),e.layMain=o.find(v),e.layBody=o.find(m),e.layFixed=o.find(g),e.layFixLeft=o.find(x),e.layFixRight=o.find(b),e.layTool=o.find(k),e.layPage=o.find(C),e.layTool.html(i(t(a.toolbar).html()||"").render(a)),a.height&&e.fullSize(),a.cols.length>1){var r=e.layFixed.find(p).find("th");r.height(e.layHeader.height()-1-parseFloat(r.css("padding-top"))-parseFloat(r.css("padding-bottom")))}e.pullData(e.page),e.events()},M.prototype.initOpts=function(e){var t=this,i=(t.config,{checkbox:48,space:15,numbers:40});e.checkbox&&(e.type="checkbox"),e.space&&(e.type="space"),e.type||(e.type="normal"),"normal"!==e.type&&(e.unresize=!0,e.width=e.width||i[e.type])},M.prototype.setArea=function(){var e=this,t=e.config,i=0,a=0,l=0,n=0,o=t.width||function(){var e=function(i){var a,l;i=i||t.elem.parent(),a=i.width();try{l="none"===i.css("display")}catch(n){}return!i[0]||a&&!l?a:e(i.parent())};return e()}();e.eachCols(function(){i++}),o-=function(){return"line"===t.skin||"nob"===t.skin?2:i+1}(),layui.each(t.cols,function(t,i){layui.each(i,function(t,l){var r;return l?(e.initOpts(l),r=l.width||0,void(l.colspan>1||(/\d+%$/.test(r)?l.width=r=Math.floor(parseFloat(r)/100*o):r||(l.width=r=0,a++),n+=r))):void i.splice(t,1)})}),e.autoColNums=a,o>n&&a&&(l=(o-n)/a),layui.each(t.cols,function(e,i){layui.each(i,function(e,i){var a=i.minWidth||t.cellMinWidth;i.colspan>1||0===i.width&&(i.width=Math.floor(l>=a?l:a))})}),t.height&&/^full-\d+$/.test(t.height)&&(e.fullHeightGap=t.height.split("-")[1],t.height=A.height()-e.fullHeightGap)},M.prototype.reload=function(e){var i=this;i.config.data&&i.config.data.constructor===Array&&delete i.config.data,i.config=t.extend({},i.config,e),i.render()},M.prototype.page=1,M.prototype.pullData=function(e,i){var a=this,n=a.config,o=n.request,r=n.response,d=function(){"object"==typeof n.initSort&&a.sort(n.initSort.field,n.initSort.type)};if(a.startTime=(new Date).getTime(),n.url){var c={};c[o.pageName]=e,c[o.limitName]=n.limit;var s=t.extend(c,n.where);n.contentType&&0==n.contentType.indexOf("application/json")&&(s=JSON.stringify(s)),t.ajax({type:n.method||"get",url:n.url,contentType:n.contentType,data:s,dataType:"json",headers:n.headers||{},success:function(t){t[r.statusName]!=r.statusCode?(a.renderForm(),a.layMain.html('
    '+(t[r.msgName]||"返回的数据状态异常")+"
    ")):(a.renderData(t,e,t[r.countName]),d(),n.time=(new Date).getTime()-a.startTime+" ms"),i&&l.close(i),"function"==typeof n.done&&n.done(t,e,t[r.countName])},error:function(e,t){a.layMain.html('
    数据接口请求异常
    '),a.renderForm(),i&&l.close(i)}})}else if(n.data&&n.data.constructor===Array){var u={},h=e*n.limit-n.limit;u[r.dataName]=n.data.concat().splice(h,n.limit),u[r.countName]=n.data.length,a.renderData(u,e,n.data.length),d(),"function"==typeof n.done&&n.done(u,e,u[r.countName])}},M.prototype.eachCols=function(e){var i=t.extend(!0,[],this.config.cols),a=[],l=0;layui.each(i,function(e,t){layui.each(t,function(t,n){if(n.colspan>1){var o=0;l++,n.CHILD_COLS=[],layui.each(i[e+1],function(e,t){t.PARENT_COL||o==n.colspan||(t.PARENT_COL=l,n.CHILD_COLS.push(t),o+=t.colspan>1?t.colspan:1)})}n.PARENT_COL||a.push(n)})});var n=function(t){layui.each(t||a,function(t,i){return i.CHILD_COLS?n(i.CHILD_COLS):void e(t,i)})};n()},M.prototype.renderData=function(e,n,o,r){var c=this,s=c.config,u=e[s.response.dataName]||[],y=[],p=[],m=[],v=function(){return!r&&c.sortKey?c.sort(c.sortKey.field,c.sortKey.sort,!0):(layui.each(u,function(e,a){var l=[],o=[],u=[],h=e+s.limit*(n-1)+1;0!==a.length&&(r||(a[d.config.indexName]=e),c.eachCols(function(e,n){var r=n.field||e,f=a[r];c.getColElem(c.layHeader,r);if(void 0!==f&&null!==f||(f=""),!(n.colspan>1)){var y=['",'
    '+function(){var e=t.extend(!0,{LAY_INDEX:h},a);return"checkbox"===n.type?'":"numbers"===n.type?h:n.toolbar?i(t(n.toolbar).html()||"").render(e):n.templet?function(){return"function"==typeof n.templet?n.templet(e):i(t(n.templet).html()||String(f)).render(e)}():f}(),"
    "].join("");l.push(y),n.fixed&&"right"!==n.fixed&&o.push(y),"right"===n.fixed&&u.push(y)}}),y.push(''+l.join("")+""),p.push(''+o.join("")+""),m.push(''+u.join("")+""))}),c.layBody.scrollTop(0),c.layMain.find("."+f).remove(),c.layMain.find("tbody").html(y.join("")),c.layFixLeft.find("tbody").html(p.join("")),c.layFixRight.find("tbody").html(m.join("")),c.renderForm(),c.syncCheckAll(),c.haveInit?c.scrollPatch():setTimeout(function(){c.scrollPatch()},50),c.haveInit=!0,void l.close(c.tipsIndex))};return c.key=s.id||s.index,d.cache[c.key]=u,c.layPage[0===u.length&&1==n?"addClass":"removeClass"](h),r?v():0===u.length?(c.renderForm(),c.layFixed.remove(),c.layMain.find("tbody").html(""),c.layMain.find("."+f).remove(),c.layMain.append('
    '+s.text.none+"
    ")):(v(),void(s.page&&(s.page=t.extend({elem:"layui-table-page"+s.index,count:o,limit:s.limit,limits:s.limits||[10,20,30,40,50,60,70,80,90],groups:3,layout:["prev","page","next","skip","count","limit"],prev:'',next:'',jump:function(e,t){t||(c.page=e.curr,s.limit=e.limit,c.pullData(e.curr,c.loading()))}},s.page),s.page.count=o,a.render(s.page))))},M.prototype.getColElem=function(e,t){var i=this,a=i.config;return e.eq(0).find(".laytable-cell-"+(a.index+"-"+t)+":eq(0)")},M.prototype.renderForm=function(e){n.render(e,"LAY-table-"+this.index)},M.prototype.sort=function(e,i,a,l){var n,r,c=this,u={},h=c.config,f=h.elem.attr("lay-filter"),y=d.cache[c.key];"string"==typeof e&&c.layHeader.find("th").each(function(i,a){var l=t(this),o=l.data("field");if(o===e)return e=l,n=o,!1});try{var n=n||e.data("field");if(c.sortKey&&!a&&n===c.sortKey.field&&i===c.sortKey.sort)return;var p=c.layHeader.find("th .laytable-cell-"+h.index+"-"+n).find(w);c.layHeader.find("th").find(w).removeAttr("lay-sort"),p.attr("lay-sort",i||null),c.layFixed.find("th")}catch(m){return o.error("Table modules: Did not match to field")}c.sortKey={field:n,sort:i},"asc"===i?r=layui.sort(y,n):"desc"===i?r=layui.sort(y,n,!0):(r=layui.sort(y,d.config.indexName),delete c.sortKey),u[h.response.dataName]=r,c.renderData(u,c.page,c.count,!0),l&&layui.event.call(e,s,"sort("+f+")",{field:n,type:i})},M.prototype.loading=function(){var e=this,t=e.config;if(t.loading&&t.url)return l.msg("数据请求中",{icon:16,offset:[e.elem.offset().top+e.elem.height()/2-35-A.scrollTop()+"px",e.elem.offset().left+e.elem.width()/2-90-A.scrollLeft()+"px"],time:-1,anim:-1,fixed:!1})},M.prototype.setCheckData=function(e,t){var i=this,a=i.config,l=d.cache[i.key];l[e]&&l[e].constructor!==Array&&(l[e][a.checkName]=t)},M.prototype.syncCheckAll=function(){var e=this,t=e.config,i=e.layHeader.find('input[name="layTableCheckbox"]'),a=function(i){return e.eachCols(function(e,a){"checkbox"===a.type&&(a[t.checkName]=i)}),i};i[0]&&(d.checkStatus(e.key).isAll?(i[0].checked||(i.prop("checked",!0),e.renderForm("checkbox")),a(!0)):(i[0].checked&&(i.prop("checked",!1),e.renderForm("checkbox")),a(!1)))},M.prototype.getCssRule=function(e,t){var i=this,a=i.elem.find("style")[0],l=a.sheet||a.styleSheet||{},n=l.cssRules||l.rules;layui.each(n,function(a,l){if(l.selectorText===".laytable-cell-"+i.index+"-"+e)return t(l),!0})},M.prototype.fullSize=function(){var e,t=this,i=t.config,a=i.height;t.fullHeightGap&&(a=A.height()-t.fullHeightGap,a<135&&(a=135),t.elem.css("height",a)),e=parseFloat(a)-parseFloat(t.layHeader.height())-1,i.toolbar&&(e-=t.layTool.outerHeight()),i.page&&(e=e-t.layPage.outerHeight()-1),t.layMain.css("height",e)},M.prototype.getScrollWidth=function(e){var t=0;return e?t=e.offsetWidth-e.clientWidth:(e=document.createElement("div"),e.style.width="100px",e.style.height="100px",e.style.overflowY="scroll",document.body.appendChild(e),t=e.offsetWidth-e.clientWidth,document.body.removeChild(e)),t},M.prototype.scrollPatch=function(){var e=this,i=e.layMain.children("table"),a=e.layMain.width()-e.layMain.prop("clientWidth"),l=e.layMain.height()-e.layMain.prop("clientHeight"),n=e.getScrollWidth(e.layMain[0]),o=i.outerWidth()-e.layMain.width();if(e.autoColNums&&o<5&&!e.scrollPatchWStatus){var r=e.layHeader.eq(0).find("thead th:last-child"),d=r.data("field");e.getCssRule(d,function(t){var i=t.style.width||r.outerWidth();t.style.width=parseFloat(i)-n-o+"px",e.layMain.height()-e.layMain.prop("clientHeight")>0&&(t.style.width=parseFloat(t.style.width)-1+"px"),e.scrollPatchWStatus=!0})}if(a&&l){if(!e.elem.find(".layui-table-patch")[0]){var c=t('
    ');c.find("div").css({width:a}),e.layHeader.eq(0).find("thead tr").append(c)}}else e.layHeader.eq(0).find(".layui-table-patch").remove();var s=e.layMain.height(),u=s-l;e.layFixed.find(m).css("height",i.height()>u?u:"auto"),e.layFixRight[o>0?"removeClass":"addClass"](h),e.layFixRight.css("right",a-1)},M.prototype.events=function(){var e,a=this,n=a.config,o=t("body"),c={},u=a.layHeader.find("th"),h=".layui-table-cell",f=n.elem.attr("lay-filter");u.on("mousemove",function(e){var i=t(this),a=i.offset().left,l=e.clientX-a;i.attr("colspan")>1||i.data("unresize")||c.resizeStart||(c.allowResize=i.width()-l<=10,o.css("cursor",c.allowResize?"col-resize":""))}).on("mouseleave",function(){t(this);c.resizeStart||o.css("cursor","")}).on("mousedown",function(e){var i=t(this);if(c.allowResize){var l=i.data("field");e.preventDefault(),c.resizeStart=!0,c.offset=[e.clientX,e.clientY],a.getCssRule(l,function(e){var t=e.style.width||i.outerWidth();c.rule=e,c.ruleWidth=parseFloat(t),c.minWidth=i.data("minwidth")||n.cellMinWidth})}}),S.on("mousemove",function(t){if(c.resizeStart){if(t.preventDefault(),c.rule){var i=c.ruleWidth+t.clientX-c.offset[0];i');d[0].value=e.data("content")||o.text(),e.find("."+N)[0]||e.append(d),d.focus()}else o.find(".layui-form-switch,.layui-form-checkbox")[0]||Math.round(o.prop("scrollWidth"))>Math.round(o.outerWidth())&&(a.tipsIndex=l.tips(['
    ',o.html(),"
    ",''].join(""),o[0],{tips:[3,""],time:-1,anim:-1,maxWidth:r.ios||r.android?300:600,isOutAnim:!1,skin:"layui-table-tips",success:function(e,t){e.find(".layui-table-tips-c").on("click",function(){l.close(t)})}}))}),a.layBody.on("click","*[lay-event]",function(){var e=t(this),l=e.parents("tr").eq(0).data("index"),n=a.layBody.find('tr[data-index="'+l+'"]'),o="layui-table-click",r=d.cache[a.key][l];layui.event.call(this,s,"tool("+f+")",{data:d.clearCacheKey(r),event:e.attr("lay-event"),tr:n,del:function(){d.cache[a.key][l]=[],n.remove(),a.scrollPatch()},update:function(e){e=e||{},layui.each(e,function(e,l){if(e in r){var o,d=n.children('td[data-field="'+e+'"]');r[e]=l,a.eachCols(function(t,i){i.field==e&&i.templet&&(o=i.templet)}),d.children(h).html(o?i(t(o).html()||l).render(r):l),d.data("content",l)}})}}),n.addClass(o).siblings("tr").removeClass(o)}),a.layMain.on("scroll",function(){var e=t(this),i=e.scrollLeft(),n=e.scrollTop();a.layHeader.scrollLeft(i),a.layFixed.find(m).scrollTop(n),l.close(a.tipsIndex)}),A.on("resize",function(){a.fullSize(),a.scrollPatch()})},d.init=function(e,i){i=i||{};var a=this,l=t(e?'table[lay-filter="'+e+'"]':u+"[lay-data]"),n="Table element property lay-data configuration item has a syntax error: ";return l.each(function(){var a=t(this),l=a.attr("lay-data");try{l=new Function("return "+l)()}catch(r){o.error(n+l)}var c=[],s=t.extend({elem:this,cols:[],data:[],skin:a.attr("lay-skin"),size:a.attr("lay-size"),even:"string"==typeof a.attr("lay-even")},d.config,i,l);e&&a.hide(),a.find("thead>tr").each(function(e){s.cols[e]=[],t(this).children().each(function(i){var a=t(this),l=a.attr("lay-data");try{l=new Function("return "+l)()}catch(r){return o.error(n+l)}var d=t.extend({title:a.text(),colspan:a.attr("colspan")||0,rowspan:a.attr("rowspan")||0},l);d.colspan<2&&c.push(d),s.cols[e].push(d)})}),a.find("tbody>tr").each(function(e){var i=t(this),a={};i.children("td").each(function(e,i){var l=t(this),n=l.data("field");if(n)return a[n]=l.html()}),layui.each(c,function(e,t){var l=i.children("td").eq(e);a[t.field]=l.html()}),s.data[e]=a}),d.render(s)}),a},d.checkStatus=function(e){var t=0,i=0,a=[],l=d.cache[e]||[];return layui.each(l,function(e,l){return l.constructor===Array?void i++:void(l[d.config.checkName]&&(t++,a.push(d.clearCacheKey(l))))}),{data:a,isAll:!!l.length&&t===l.length-i}},c.config={},d.reload=function(e,i){var a=c.config[e];return i=i||{},a?(i.data&&i.data.constructor===Array&&delete a.data,d.render(t.extend(!0,{},a,i))):o.error("The ID option was not found in the table instance")},d.render=function(e){var t=new M(e);return c.call(t)},d.clearCacheKey=function(e){return e=t.extend({},e),delete e[d.config.checkName],delete e[d.config.indexName],e},d.init(),e(s,d)}); \ No newline at end of file diff --git a/main/webapp/static/lib/layui/lay/modules/tree.js b/main/webapp/static/lib/layui/lay/modules/tree.js new file mode 100644 index 0000000..2c3dd22 --- /dev/null +++ b/main/webapp/static/lib/layui/lay/modules/tree.js @@ -0,0 +1,2 @@ +/** layui-v2.2.6 MIT License By https://www.layui.com */ + ;layui.define("jquery",function(e){"use strict";var o=layui.$,a=layui.hint(),i="layui-tree-enter",r=function(e){this.options=e},t={arrow:["",""],checkbox:["",""],radio:["",""],branch:["",""],leaf:""};r.prototype.init=function(e){var o=this;e.addClass("layui-box layui-tree"),o.options.skin&&e.addClass("layui-tree-skin-"+o.options.skin),o.tree(e),o.on(e)},r.prototype.tree=function(e,a){var i=this,r=i.options,n=a||r.nodes;layui.each(n,function(a,n){var l=n.children&&n.children.length>0,c=o('
      '),s=o(["
    • ",function(){return l?''+(n.spread?t.arrow[1]:t.arrow[0])+"":""}(),function(){return r.check?''+("checkbox"===r.check?t.checkbox[0]:"radio"===r.check?t.radio[0]:"")+"":""}(),function(){return'"+(''+(l?n.spread?t.branch[1]:t.branch[0]:t.leaf)+"")+(""+(n.name||"未命名")+"")}(),"
    • "].join(""));l&&(s.append(c),i.tree(c,n.children)),e.append(s),"function"==typeof r.click&&i.click(s,n),i.spread(s,n),r.drag&&i.drag(s,n)})},r.prototype.click=function(e,o){var a=this,i=a.options;e.children("a").on("click",function(e){layui.stope(e),i.click(o)})},r.prototype.spread=function(e,o){var a=this,i=(a.options,e.children(".layui-tree-spread")),r=e.children("ul"),n=e.children("a"),l=function(){e.data("spread")?(e.data("spread",null),r.removeClass("layui-show"),i.html(t.arrow[0]),n.find(".layui-icon").html(t.branch[0])):(e.data("spread",!0),r.addClass("layui-show"),i.html(t.arrow[1]),n.find(".layui-icon").html(t.branch[1]))};r[0]&&(i.on("click",l),n.on("dblclick",l))},r.prototype.on=function(e){var a=this,r=a.options,t="layui-tree-drag";e.find("i").on("selectstart",function(e){return!1}),r.drag&&o(document).on("mousemove",function(e){var i=a.move;if(i.from){var r=(i.to,o('
      '));e.preventDefault(),o("."+t)[0]||o("body").append(r);var n=o("."+t)[0]?o("."+t):r;n.addClass("layui-show").html(i.from.elem.children("a").html()),n.css({left:e.pageX+10,top:e.pageY+10})}}).on("mouseup",function(){var e=a.move;e.from&&(e.from.elem.children("a").removeClass(i),e.to&&e.to.elem.children("a").removeClass(i),a.move={},o("."+t).remove())})},r.prototype.move={},r.prototype.drag=function(e,a){var r=this,t=(r.options,e.children("a")),n=function(){var t=o(this),n=r.move;n.from&&(n.to={item:a,elem:e},t.addClass(i))};t.on("mousedown",function(){var o=r.move;o.from={item:a,elem:e}}),t.on("mouseenter",n).on("mousemove",n).on("mouseleave",function(){var e=o(this),a=r.move;a.from&&(delete a.to,e.removeClass(i))})},e("tree",function(e){var i=new r(e=e||{}),t=o(e.elem);return t[0]?void i.init(t):a.error("layui.tree 没有找到"+e.elem+"元素")})}); \ No newline at end of file diff --git a/main/webapp/static/lib/layui/lay/modules/upload.js b/main/webapp/static/lib/layui/lay/modules/upload.js new file mode 100644 index 0000000..795f613 --- /dev/null +++ b/main/webapp/static/lib/layui/lay/modules/upload.js @@ -0,0 +1,2 @@ +/** layui-v2.2.6 MIT License By https://www.layui.com */ + ;layui.define("layer",function(e){"use strict";var t=layui.$,i=layui.layer,n=layui.hint(),a=layui.device(),o={config:{},set:function(e){var i=this;return i.config=t.extend({},i.config,e),i},on:function(e,t){return layui.onevent.call(this,r,e,t)}},l=function(){var e=this;return{upload:function(t){e.upload.call(e,t)},config:e.config}},r="upload",u="layui-upload-file",c="layui-upload-form",f="layui-upload-iframe",s="layui-upload-choose",p=function(e){var i=this;i.config=t.extend({},i.config,o.config,e),i.render()};p.prototype.config={accept:"images",exts:"",auto:!0,bindAction:"",url:"",field:"file",method:"post",data:{},drag:!0,size:0,number:0,multiple:!1},p.prototype.render=function(e){var i=this,e=i.config;e.elem=t(e.elem),e.bindAction=t(e.bindAction),i.file(),i.events()},p.prototype.file=function(){var e=this,i=e.config,n=e.elemFile=t(['"].join("")),o=i.elem.next();(o.hasClass(u)||o.hasClass(c))&&o.remove(),a.ie&&a.ie<10&&i.elem.wrap('
      '),e.isFile()?(e.elemFile=i.elem,i.field=i.elem[0].name):i.elem.after(n),a.ie&&a.ie<10&&e.initIE()},p.prototype.initIE=function(){var e=this,i=e.config,n=t(''),a=t(['
      ',"
      "].join(""));t("#"+f)[0]||t("body").append(n),i.elem.next().hasClass(f)||(e.elemFile.wrap(a),i.elem.next("."+f).append(function(){var e=[];return layui.each(i.data,function(t,i){i="function"==typeof i?i():i,e.push('')}),e.join("")}()))},p.prototype.msg=function(e){return i.msg(e,{icon:2,shift:6})},p.prototype.isFile=function(){var e=this.config.elem[0];if(e)return"input"===e.tagName.toLocaleLowerCase()&&"file"===e.type},p.prototype.preview=function(e){var t=this;window.FileReader&&layui.each(t.chooseFiles,function(t,i){var n=new FileReader;n.readAsDataURL(i),n.onload=function(){e&&e(t,i,this.result)}})},p.prototype.upload=function(e,i){var n,o=this,l=o.config,r=o.elemFile[0],u=function(){var i=0,n=0,a=e||o.files||o.chooseFiles||r.files,u=function(){l.multiple&&i+n===o.fileLength&&"function"==typeof l.allDone&&l.allDone({total:o.fileLength,successful:i,aborted:n})};layui.each(a,function(e,a){var r=new FormData;r.append(l.field,a),layui.each(l.data,function(e,t){t="function"==typeof t?t():t,r.append(e,t)}),t.ajax({url:l.url,type:l.method,data:r,contentType:!1,processData:!1,dataType:"json",headers:l.headers||{},success:function(t){i++,d(e,t),u()},error:function(){n++,o.msg("请求上传接口出现异常"),m(e),u()}})})},c=function(){var e=t("#"+f);o.elemFile.parent().submit(),clearInterval(p.timer),p.timer=setInterval(function(){var t,i=e.contents().find("body");try{t=i.text()}catch(n){o.msg("获取上传后的响应信息出现异常"),clearInterval(p.timer),m()}t&&(clearInterval(p.timer),i.html(""),d(0,t))},30)},d=function(e,t){if(o.elemFile.next("."+s).remove(),r.value="","object"!=typeof t)try{t=JSON.parse(t)}catch(i){return t={},o.msg("请对上传接口返回有效JSON")}"function"==typeof l.done&&l.done(t,e||0,function(e){o.upload(e)})},m=function(e){l.auto&&(r.value=""),"function"==typeof l.error&&l.error(e||0,function(e){o.upload(e)})},h=l.exts,v=function(){var t=[];return layui.each(e||o.chooseFiles,function(e,i){t.push(i.name)}),t}(),g={preview:function(e){o.preview(e)},upload:function(e,t){var i={};i[e]=t,o.upload(i)},pushFile:function(){return o.files=o.files||{},layui.each(o.chooseFiles,function(e,t){o.files[e]=t}),o.files}},y=function(){return"choose"===i?l.choose&&l.choose(g):(l.before&&l.before(g),a.ie?a.ie>9?u():c():void u())};if(v=0===v.length?r.value.match(/[^\/\\]+\..+/g)||[]||"":v,0!==v.length){switch(l.accept){case"file":if(h&&!RegExp("\\w\\.("+h+")$","i").test(escape(v)))return o.msg("选择的文件中包含不支持的格式"),r.value="";break;case"video":if(!RegExp("\\w\\.("+(h||"avi|mp4|wma|rmvb|rm|flash|3gp|flv")+")$","i").test(escape(v)))return o.msg("选择的视频中包含不支持的格式"),r.value="";break;case"audio":if(!RegExp("\\w\\.("+(h||"mp3|wav|mid")+")$","i").test(escape(v)))return o.msg("选择的音频中包含不支持的格式"),r.value="";break;default:if(layui.each(v,function(e,t){RegExp("\\w\\.("+(h||"jpg|png|gif|bmp|jpeg$")+")","i").test(escape(t))||(n=!0)}),n)return o.msg("选择的图片中包含不支持的格式"),r.value=""}if(o.fileLength=function(){var t=0,i=e||o.files||o.chooseFiles||r.files;return layui.each(i,function(){t++}),t}(),l.number&&o.fileLength>l.number)return o.msg("同时最多只能上传的数量为:"+l.number);if(l.size>0&&!(a.ie&&a.ie<10)){var F;if(layui.each(o.chooseFiles,function(e,t){if(t.size>1024*l.size){var i=l.size/1024;i=i>=1?Math.floor(i)+(i%1>0?i.toFixed(1):0)+"MB":l.size+"KB",r.value="",F=i}}),F)return o.msg("文件不能超过"+F)}y()}},p.prototype.events=function(){var e=this,i=e.config,o=function(t){e.chooseFiles={},layui.each(t,function(t,i){var n=(new Date).getTime();e.chooseFiles[n+"-"+t]=i})},l=function(t,n){var a=e.elemFile,o=t.length>1?t.length+"个文件":(t[0]||{}).name||a[0].value.match(/[^\/\\]+\..+/g)||[]||"";a.next().hasClass(s)&&a.next().remove(),e.upload(null,"choose"),e.isFile()||i.choose||a.after(''+o+"")};i.elem.off("upload.start").on("upload.start",function(){var a=t(this),o=a.attr("lay-data");if(o)try{o=new Function("return "+o)(),e.config=t.extend({},i,o)}catch(l){n.error("Upload element property lay-data configuration item has a syntax error: "+o)}e.config.item=a,e.elemFile[0].click()}),a.ie&&a.ie<10||i.elem.off("upload.over").on("upload.over",function(){var e=t(this);e.attr("lay-over","")}).off("upload.leave").on("upload.leave",function(){var e=t(this);e.removeAttr("lay-over")}).off("upload.drop").on("upload.drop",function(n,a){var r=t(this),u=a.originalEvent.dataTransfer.files||[];r.removeAttr("lay-over"),o(u),i.auto?e.upload(u):l(u)}),e.elemFile.off("upload.change").on("upload.change",function(){var t=this.files||[];o(t),i.auto?e.upload():l(t)}),i.bindAction.off("upload.action").on("upload.action",function(){e.upload()}),i.elem.data("haveEvents")||(e.elemFile.on("change",function(){t(this).trigger("upload.change")}),i.elem.on("click",function(){e.isFile()||t(this).trigger("upload.start")}),i.drag&&i.elem.on("dragover",function(e){e.preventDefault(),t(this).trigger("upload.over")}).on("dragleave",function(e){t(this).trigger("upload.leave")}).on("drop",function(e){e.preventDefault(),t(this).trigger("upload.drop",e)}),i.bindAction.on("click",function(){t(this).trigger("upload.action")}),i.elem.data("haveEvents",!0))},o.render=function(e){var t=new p(e);return l.call(t)},e(r,o)}); \ No newline at end of file diff --git a/main/webapp/static/lib/layui/lay/modules/util.js b/main/webapp/static/lib/layui/lay/modules/util.js new file mode 100644 index 0000000..d492cd4 --- /dev/null +++ b/main/webapp/static/lib/layui/lay/modules/util.js @@ -0,0 +1,2 @@ +/** layui-v2.2.6 MIT License By https://www.layui.com */ + ;layui.define("jquery",function(e){"use strict";var t=layui.$,i={fixbar:function(e){var i,o,a="layui-fixbar",r="layui-fixbar-top",n=t(document),l=t("body");e=t.extend({showHeight:200},e),e.bar1=e.bar1===!0?"":e.bar1,e.bar2=e.bar2===!0?"":e.bar2,e.bgcolor=e.bgcolor?"background-color:"+e.bgcolor:"";var c=[e.bar1,e.bar2,""],g=t(['
        ',e.bar1?'
      • '+c[0]+"
      • ":"",e.bar2?'
      • '+c[1]+"
      • ":"",'
      • '+c[2]+"
      • ","
      "].join("")),s=g.find("."+r),u=function(){var t=n.scrollTop();t>=e.showHeight?i||(s.show(),i=1):i&&(s.hide(),i=0)};t("."+a)[0]||("object"==typeof e.css&&g.css(e.css),l.append(g),u(),g.find("li").on("click",function(){var i=t(this),o=i.attr("lay-type");"top"===o&&t("html,body").animate({scrollTop:0},200),e.click&&e.click.call(this,o)}),n.on("scroll",function(){clearTimeout(o),o=setTimeout(function(){u()},100)}))},countdown:function(e,t,i){var o=this,a="function"==typeof t,r=new Date(e).getTime(),n=new Date(!t||a?(new Date).getTime():t).getTime(),l=r-n,c=[Math.floor(l/864e5),Math.floor(l/36e5)%24,Math.floor(l/6e4)%60,Math.floor(l/1e3)%60];a&&(i=t);var g=setTimeout(function(){o.countdown(e,n+1e3,i)},1e3);return i&&i(l>0?c:[0,0,0,0],t,g),l<=0&&clearTimeout(g),g},timeAgo:function(e,t){var i=this,o=[[],[]],a=(new Date).getTime()-new Date(e).getTime();return a>6912e5?(a=new Date(e),o[0][0]=i.digit(a.getFullYear(),4),o[0][1]=i.digit(a.getMonth()+1),o[0][2]=i.digit(a.getDate()),t||(o[1][0]=i.digit(a.getHours()),o[1][1]=i.digit(a.getMinutes()),o[1][2]=i.digit(a.getSeconds())),o[0].join("-")+" "+o[1].join(":")):a>=864e5?(a/1e3/60/60/24|0)+"天前":a>=36e5?(a/1e3/60/60|0)+"小时前":a>=12e4?(a/1e3/60|0)+"分钟前":a<0?"未来":"刚刚"},digit:function(e,t){var i="";e=String(e),t=t||2;for(var o=e.length;o0;r--)if("interactive"===n[r].readyState){e=n[r].src;break}return e||n[o].src}();return e.substring(0,e.lastIndexOf("/")+1)}(),a=function(t){e.console&&console.error&&console.error("Layui hint: "+t)},i="undefined"!=typeof opera&&"[object Opera]"===opera.toString(),u={layer:"modules/layer",laydate:"modules/laydate",laypage:"modules/laypage",laytpl:"modules/laytpl",layim:"modules/layim",layedit:"modules/layedit",form:"modules/form",upload:"modules/upload",tree:"modules/tree",table:"modules/table",element:"modules/element",util:"modules/util",flow:"modules/flow",carousel:"modules/carousel",code:"modules/code",jquery:"modules/jquery",mobile:"modules/mobile","layui.all":"../layui.all"};o.prototype.cache=n,o.prototype.define=function(e,t){var o=this,r="function"==typeof e,a=function(){var e=function(e,t){layui[e]=t,n.status[e]=!0};return"function"==typeof t&&t(function(o,r){e(o,r),n.callback[o]=function(){t(e)}}),this};return r&&(t=e,e=[]),layui["layui.all"]||!layui["layui.all"]&&layui["layui.mobile"]?a.call(o):(o.use(e,a),o)},o.prototype.use=function(e,o,l){function s(e,t){var o="PLaySTATION 3"===navigator.platform?/^complete$/:/^(complete|loaded)$/;("load"===e.type||o.test((e.currentTarget||e.srcElement).readyState))&&(n.modules[f]=t,d.removeChild(v),function r(){return++m>1e3*n.timeout/4?a(f+" is not a valid module"):void(n.status[f]?c():setTimeout(r,4))}())}function c(){l.push(layui[f]),e.length>1?y.use(e.slice(1),o,l):"function"==typeof o&&o.apply(layui,l)}var y=this,p=n.dir=n.dir?n.dir:r,d=t.getElementsByTagName("head")[0];e="string"==typeof e?[e]:e,window.jQuery&&jQuery.fn.on&&(y.each(e,function(t,n){"jquery"===n&&e.splice(t,1)}),layui.jquery=layui.$=jQuery);var f=e[0],m=0;if(l=l||[],n.host=n.host||(p.match(/\/\/([\s\S]+?)\//)||["//"+location.host+"/"])[0],0===e.length||layui["layui.all"]&&u[f]||!layui["layui.all"]&&layui["layui.mobile"]&&u[f])return c(),y;if(n.modules[f])!function g(){return++m>1e3*n.timeout/4?a(f+" is not a valid module"):void("string"==typeof n.modules[f]&&n.status[f]?c():setTimeout(g,4))}();else{var v=t.createElement("script"),h=(u[f]?p+"lay/":/^\{\/\}/.test(y.modules[f])?"":n.base||"")+(y.modules[f]||f)+".js";h=h.replace(/^\{\/\}/,""),v.async=!0,v.charset="utf-8",v.src=h+function(){var e=n.version===!0?n.v||(new Date).getTime():n.version||"";return e?"?v="+e:""}(),d.appendChild(v),!v.attachEvent||v.attachEvent.toString&&v.attachEvent.toString().indexOf("[native code")<0||i?v.addEventListener("load",function(e){s(e,h)},!1):v.attachEvent("onreadystatechange",function(e){s(e,h)}),n.modules[f]=h}return y},o.prototype.getStyle=function(t,n){var o=t.currentStyle?t.currentStyle:e.getComputedStyle(t,null);return o[o.getPropertyValue?"getPropertyValue":"getAttribute"](n)},o.prototype.link=function(e,o,r){var i=this,u=t.createElement("link"),l=t.getElementsByTagName("head")[0];"string"==typeof o&&(r=o);var s=(r||e).replace(/\.|\//g,""),c=u.id="layuicss-"+s,y=0;return u.rel="stylesheet",u.href=e+(n.debug?"?v="+(new Date).getTime():""),u.media="all",t.getElementById(c)||l.appendChild(u),"function"!=typeof o?i:(function p(){return++y>1e3*n.timeout/100?a(e+" timeout"):void(1989===parseInt(i.getStyle(t.getElementById(c),"width"))?function(){o()}():setTimeout(p,100))}(),i)},n.callback={},o.prototype.factory=function(e){if(layui[e])return"function"==typeof n.callback[e]?n.callback[e]:null},o.prototype.addcss=function(e,t,o){return layui.link(n.dir+"css/"+e,t,o)},o.prototype.img=function(e,t,n){var o=new Image;return o.src=e,o.complete?t(o):(o.onload=function(){o.onload=null,t(o)},void(o.onerror=function(e){o.onerror=null,n(e)}))},o.prototype.config=function(e){e=e||{};for(var t in e)n[t]=e[t];return this},o.prototype.modules=function(){var e={};for(var t in u)e[t]=u[t];return e}(),o.prototype.extend=function(e){var t=this;e=e||{};for(var n in e)t[n]||t.modules[n]?a("模块名 "+n+" 已被占用"):t.modules[n]=e[n];return t},o.prototype.router=function(e){var t=this,e=e||location.hash,n={path:[],search:{},hash:(e.match(/[^#](#.*$)/)||[])[1]||""};return/^#\//.test(e)?(e=e.replace(/^#\//,""),n.href="/"+e,e=e.replace(/([^#])(#.*$)/,"$1").split("/")||[],t.each(e,function(e,t){/^\w+=/.test(t)?function(){t=t.split("="),n.search[t[0]]=t[1]}():n.path.push(t)}),n):n},o.prototype.data=function(t,n,o){if(t=t||"layui",o=o||localStorage,e.JSON&&e.JSON.parse){if(null===n)return delete o[t];n="object"==typeof n?n:{key:n};try{var r=JSON.parse(o[t])}catch(a){var r={}}return"value"in n&&(r[n.key]=n.value),n.remove&&delete r[n.key],o[t]=JSON.stringify(r),n.key?r[n.key]:r}},o.prototype.sessionData=function(e,t){return this.data(e,t,sessionStorage)},o.prototype.device=function(t){var n=navigator.userAgent.toLowerCase(),o=function(e){var t=new RegExp(e+"/([^\\s\\_\\-]+)");return e=(n.match(t)||[])[1],e||!1},r={os:function(){return/windows/.test(n)?"windows":/linux/.test(n)?"linux":/iphone|ipod|ipad|ios/.test(n)?"ios":/mac/.test(n)?"mac":void 0}(),ie:function(){return!!(e.ActiveXObject||"ActiveXObject"in e)&&((n.match(/msie\s(\d+)/)||[])[1]||"11")}(),weixin:o("micromessenger")};return t&&!r[t]&&(r[t]=o(t)),r.android=/android/.test(n),r.ios="ios"===r.os,r},o.prototype.hint=function(){return{error:a}},o.prototype.each=function(e,t){var n,o=this;if("function"!=typeof t)return o;if(e=e||[],e.constructor===Object){for(n in e)if(t.call(e[n],n,e[n]))break}else for(n=0;na?1:r/g,">").replace(/'/g,"'").replace(/"/g,""")},error:function(e,r){var c="Laytpl Error:";return"object"==typeof console&&console.error(c+e+"\n"+(r||"")),c+e}},n=c.exp,t=function(e){this.tpl=e};t.pt=t.prototype,window.errors=0,t.pt.parse=function(e,t){var o=this,p=e,a=n("^"+r.open+"#",""),l=n(r.close+"$","");e=e.replace(/\s+|\r|\t|\n/g," ").replace(n(r.open+"#"),r.open+"# ").replace(n(r.close+"}"),"} "+r.close).replace(/\\/g,"\\\\").replace(n(r.open+"!(.+?)!"+r.close),function(e){return e=e.replace(n("^"+r.open+"!"),"").replace(n("!"+r.close),"").replace(n(r.open+"|"+r.close),function(e){return e.replace(/(.)/g,"\\$1")})}).replace(/(?="|')/g,"\\").replace(c.query(),function(e){return e=e.replace(a,"").replace(l,""),'";'+e.replace(/\\/g,"")+';view+="'}).replace(c.query(1),function(e){var c='"+(';return e.replace(/\s/g,"")===r.open+r.close?"":(e=e.replace(n(r.open+"|"+r.close),""),/^=/.test(e)&&(e=e.replace(/^=/,""),c='"+_escape_('),c+e.replace(/\\/g,"")+')+"')}),e='"use strict";var view = "'+e+'";return view;';try{return o.cache=e=new Function("d, _escape_",e),e(t,c.escape)}catch(u){return delete o.cache,c.error(u,p)}},t.pt.render=function(e,r){var n,t=this;return e?(n=t.cache?t.cache(e,c.escape):t.parse(t.tpl,e),r?void r(n):n):c.error("no data")};var o=function(e){return"string"!=typeof e?c.error("Template not found"):new t(e)};o.config=function(e){e=e||{};for(var c in e)r[c]=e[c]},o.v="1.2.0",e("laytpl",o)});layui.define(function(e){"use strict";var a=document,t="getElementById",n="getElementsByTagName",i="laypage",r="layui-disabled",u=function(e){var a=this;a.config=e||{},a.config.index=++s.index,a.render(!0)};u.prototype.type=function(){var e=this.config;if("object"==typeof e.elem)return void 0===e.elem.length?2:3},u.prototype.view=function(){var e=this,a=e.config,t=a.groups="groups"in a?0|a.groups:5;a.layout="object"==typeof a.layout?a.layout:["prev","page","next"],a.count=0|a.count,a.curr=0|a.curr||1,a.limits="object"==typeof a.limits?a.limits:[10,20,30,40,50],a.limit=0|a.limit||10,a.pages=Math.ceil(a.count/a.limit)||1,a.curr>a.pages&&(a.curr=a.pages),t<0?t=1:t>a.pages&&(t=a.pages),a.prev="prev"in a?a.prev:"上一页",a.next="next"in a?a.next:"下一页";var n=a.pages>t?Math.ceil((a.curr+(t>1?1:0))/(t>0?t:1)):1,i={prev:function(){return a.prev?''+a.prev+"":""}(),page:function(){var e=[];if(a.count<1)return"";n>1&&a.first!==!1&&0!==t&&e.push(''+(a.first||1)+"");var i=Math.floor((t-1)/2),r=n>1?a.curr-i:1,u=n>1?function(){var e=a.curr+(t-i-1);return e>a.pages?a.pages:e}():t;for(u-r2&&e.push('');r<=u;r++)r===a.curr?e.push('"+r+""):e.push(''+r+"");return a.pages>t&&a.pages>u&&a.last!==!1&&(u+1…'),0!==t&&e.push(''+(a.last||a.pages)+"")),e.join("")}(),next:function(){return a.next?''+a.next+"":""}(),count:'共 '+a.count+" 条",limit:function(){var e=['"}(),skip:function(){return['到第','','页',""].join("")}()};return['
      ',function(){var e=[];return layui.each(a.layout,function(a,t){i[t]&&e.push(i[t])}),e.join("")}(),"
      "].join("")},u.prototype.jump=function(e,a){if(e){var t=this,i=t.config,r=e.children,u=e[n]("button")[0],l=e[n]("input")[0],p=e[n]("select")[0],c=function(){var e=0|l.value.replace(/\s|\D/g,"");e&&(i.curr=e,t.render())};if(a)return c();for(var o=0,y=r.length;oi.pages||(i.curr=e,t.render())});p&&s.on(p,"change",function(){var e=this.value;i.curr*e>i.count&&(i.curr=Math.ceil(i.count/e)),i.limit=e,t.render()}),u&&s.on(u,"click",function(){c()})}},u.prototype.skip=function(e){if(e){var a=this,t=e[n]("input")[0];t&&s.on(t,"keyup",function(t){var n=this.value,i=t.keyCode;/^(37|38|39|40)$/.test(i)||(/\D/.test(n)&&(this.value=n.replace(/\D/,"")),13===i&&a.jump(e,!0))})}},u.prototype.render=function(e){var n=this,i=n.config,r=n.type(),u=n.view();2===r?i.elem&&(i.elem.innerHTML=u):3===r?i.elem.html(u):a[t](i.elem)&&(a[t](i.elem).innerHTML=u),i.jump&&i.jump(i,e);var s=a[t]("layui-laypage-"+i.index);n.jump(s),i.hash&&!e&&(location.hash="!"+i.hash+"="+i.curr),n.skip(s)};var s={render:function(e){var a=new u(e);return a.index},index:layui.laypage?layui.laypage.index+1e4:0,on:function(e,a,t){return e.attachEvent?e.attachEvent("on"+a,function(a){a.target=a.srcElement,t.call(e,a)}):e.addEventListener(a,t,!1),this}};e(i,s)});!function(){"use strict";var e=window.layui&&layui.define,t={getPath:function(){var e=document.currentScript?document.currentScript.src:function(){for(var e,t=document.scripts,n=t.length-1,a=n;a>0;a--)if("interactive"===t[a].readyState){e=t[a].src;break}return e||t[n].src}();return e.substring(0,e.lastIndexOf("/")+1)}(),getStyle:function(e,t){var n=e.currentStyle?e.currentStyle:window.getComputedStyle(e,null);return n[n.getPropertyValue?"getPropertyValue":"getAttribute"](t)},link:function(e,a,i){if(n.path){var r=document.getElementsByTagName("head")[0],o=document.createElement("link");"string"==typeof a&&(i=a);var s=(i||e).replace(/\.|\//g,""),l="layuicss-"+s,d=0;o.rel="stylesheet",o.href=n.path+e,o.id=l,document.getElementById(l)||r.appendChild(o),"function"==typeof a&&!function c(){return++d>80?window.console&&console.error("laydate.css: Invalid"):void(1989===parseInt(t.getStyle(document.getElementById(l),"width"))?a():setTimeout(c,100))}()}}},n={v:"5.0.9",config:{},index:window.laydate&&window.laydate.v?1e5:0,path:t.getPath,set:function(e){var t=this;return t.config=w.extend({},t.config,e),t},ready:function(a){var i="laydate",r="",o=(e?"modules/laydate/":"theme/")+"default/laydate.css?v="+n.v+r;return e?layui.addcss(o,a,i):t.link(o,a,i),this}},a=function(){var e=this;return{hint:function(t){e.hint.call(e,t)},config:e.config}},i="laydate",r=".layui-laydate",o="layui-this",s="laydate-disabled",l="开始日期超出了结束日期
      建议重新选择",d=[100,2e5],c="layui-laydate-static",m="layui-laydate-list",u="laydate-selected",h="layui-laydate-hint",y="laydate-day-prev",f="laydate-day-next",p="layui-laydate-footer",g=".laydate-btns-confirm",v="laydate-time-text",D=".laydate-btns-time",T=function(e){var t=this;t.index=++n.index,t.config=w.extend({},t.config,n.config,e),n.ready(function(){t.init()})},w=function(e){return new C(e)},C=function(e){for(var t=0,n="object"==typeof e?[e]:(this.selector=e,document.querySelectorAll(e||null));t0)return n[0].getAttribute(e)}():n.each(function(n,a){a.setAttribute(e,t)})},C.prototype.removeAttr=function(e){return this.each(function(t,n){n.removeAttribute(e)})},C.prototype.html=function(e){return this.each(function(t,n){n.innerHTML=e})},C.prototype.val=function(e){return this.each(function(t,n){n.value=e})},C.prototype.append=function(e){return this.each(function(t,n){"object"==typeof e?n.appendChild(e):n.innerHTML=n.innerHTML+e})},C.prototype.remove=function(e){return this.each(function(t,n){e?n.removeChild(e):n.parentNode.removeChild(n)})},C.prototype.on=function(e,t){return this.each(function(n,a){a.attachEvent?a.attachEvent("on"+e,function(e){e.target=e.srcElement,t.call(a,e)}):a.addEventListener(e,t,!1)})},C.prototype.off=function(e,t){return this.each(function(n,a){a.detachEvent?a.detachEvent("on"+e,t):a.removeEventListener(e,t,!1)})},T.isLeapYear=function(e){return e%4===0&&e%100!==0||e%400===0},T.prototype.config={type:"date",range:!1,format:"yyyy-MM-dd",value:null,min:"1900-1-1",max:"2099-12-31",trigger:"focus",show:!1,showBottom:!0,btns:["clear","now","confirm"],lang:"cn",theme:"default",position:null,calendar:!1,mark:{},zIndex:null,done:null,change:null},T.prototype.lang=function(){var e=this,t=e.config,n={cn:{weeks:["日","一","二","三","四","五","六"],time:["时","分","秒"],timeTips:"选择时间",startTime:"开始时间",endTime:"结束时间",dateTips:"返回日期",month:["一","二","三","四","五","六","七","八","九","十","十一","十二"],tools:{confirm:"确定",clear:"清空",now:"现在"}},en:{weeks:["Su","Mo","Tu","We","Th","Fr","Sa"],time:["Hours","Minutes","Seconds"],timeTips:"Select Time",startTime:"Start Time",endTime:"End Time",dateTips:"Select Date",month:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],tools:{confirm:"Confirm",clear:"Clear",now:"Now"}}};return n[t.lang]||n.cn},T.prototype.init=function(){var e=this,t=e.config,n="yyyy|y|MM|M|dd|d|HH|H|mm|m|ss|s",a="static"===t.position,i={year:"yyyy",month:"yyyy-MM",date:"yyyy-MM-dd",time:"HH:mm:ss",datetime:"yyyy-MM-dd HH:mm:ss"};t.elem=w(t.elem),t.eventElem=w(t.eventElem),t.elem[0]&&(t.range===!0&&(t.range="-"),t.format===i.date&&(t.format=i[t.type]),e.format=t.format.match(new RegExp(n+"|.","g"))||[],e.EXP_IF="",e.EXP_SPLIT="",w.each(e.format,function(t,a){var i=new RegExp(n).test(a)?"\\d{"+function(){return new RegExp(n).test(e.format[0===t?t+1:t-1]||"")?/^yyyy|y$/.test(a)?4:a.length:/^yyyy$/.test(a)?"1,4":/^y$/.test(a)?"1,308":"1,2"}()+"}":"\\"+a;e.EXP_IF=e.EXP_IF+i,e.EXP_SPLIT=e.EXP_SPLIT+"("+i+")"}),e.EXP_IF=new RegExp("^"+(t.range?e.EXP_IF+"\\s\\"+t.range+"\\s"+e.EXP_IF:e.EXP_IF)+"$"),e.EXP_SPLIT=new RegExp("^"+e.EXP_SPLIT+"$",""),e.isInput(t.elem[0])||"focus"===t.trigger&&(t.trigger="click"),t.elem.attr("lay-key")||(t.elem.attr("lay-key",e.index),t.eventElem.attr("lay-key",e.index)),t.mark=w.extend({},t.calendar&&"cn"===t.lang?{"0-1-1":"元旦","0-2-14":"情人","0-3-8":"妇女","0-3-12":"植树","0-4-1":"愚人","0-5-1":"劳动","0-5-4":"青年","0-6-1":"儿童","0-9-10":"教师","0-9-18":"国耻","0-10-1":"国庆","0-12-25":"圣诞"}:{},t.mark),w.each(["min","max"],function(e,n){var a=[],i=[];if("number"==typeof t[n]){var r=t[n],o=(new Date).getTime(),s=864e5,l=new Date(r?r0)return!0;var a=w.elem("div",{"class":"layui-laydate-header"}),i=[function(){var e=w.elem("i",{"class":"layui-icon laydate-icon laydate-prev-y"});return e.innerHTML="",e}(),function(){var e=w.elem("i",{"class":"layui-icon laydate-icon laydate-prev-m"});return e.innerHTML="",e}(),function(){var e=w.elem("div",{"class":"laydate-set-ym"}),t=w.elem("span"),n=w.elem("span");return e.appendChild(t),e.appendChild(n),e}(),function(){var e=w.elem("i",{"class":"layui-icon laydate-icon laydate-next-m"});return e.innerHTML="",e}(),function(){var e=w.elem("i",{"class":"layui-icon laydate-icon laydate-next-y"});return e.innerHTML="",e}()],d=w.elem("div",{"class":"layui-laydate-content"}),c=w.elem("table"),m=w.elem("thead"),u=w.elem("tr");w.each(i,function(e,t){a.appendChild(t)}),m.appendChild(u),w.each(new Array(6),function(e){var t=c.insertRow(0);w.each(new Array(7),function(a){if(0===e){var i=w.elem("th");i.innerHTML=n.weeks[a],u.appendChild(i)}t.insertCell(a)})}),c.insertBefore(m,c.children[0]),d.appendChild(c),r[e]=w.elem("div",{"class":"layui-laydate-main laydate-main-list-"+e}),r[e].appendChild(a),r[e].appendChild(d),o.push(i),s.push(d),l.push(c)}),w(d).html(function(){var e=[],i=[];return"datetime"===t.type&&e.push(''+n.timeTips+""),w.each(t.btns,function(e,r){var o=n.tools[r]||"btn";t.range&&"now"===r||(a&&"clear"===r&&(o="cn"===t.lang?"重置":"Reset"),i.push(''+o+""))}),e.push('"),e.join("")}()),w.each(r,function(e,t){i.appendChild(t)}),t.showBottom&&i.appendChild(d),/^#/.test(t.theme)){var m=w.elem("style"),u=["#{{id}} .layui-laydate-header{background-color:{{theme}};}","#{{id}} .layui-this{background-color:{{theme}} !important;}"].join("").replace(/{{id}}/g,e.elemID).replace(/{{theme}}/g,t.theme);"styleSheet"in m?(m.setAttribute("type","text/css"),m.styleSheet.cssText=u):m.innerHTML=u,w(i).addClass("laydate-theme-molv"),i.appendChild(m)}e.remove(T.thisElemDate),a?t.elem.append(i):(document.body.appendChild(i),e.position()),e.checkDate().calendar(),e.changeEvent(),T.thisElemDate=e.elemID,"function"==typeof t.ready&&t.ready(w.extend({},t.dateTime,{month:t.dateTime.month+1}))},T.prototype.remove=function(e){var t=this,n=(t.config,w("#"+(e||t.elemID)));return n.hasClass(c)||t.checkDate(function(){n.remove()}),t},T.prototype.position=function(){var e=this,t=e.config,n=e.bindElem||t.elem[0],a=n.getBoundingClientRect(),i=e.elem.offsetWidth,r=e.elem.offsetHeight,o=function(e){return e=e?"scrollLeft":"scrollTop",document.body[e]|document.documentElement[e]},s=function(e){return document.documentElement[e?"clientWidth":"clientHeight"]},l=5,d=a.left,c=a.bottom;d+i+l>s("width")&&(d=s("width")-i-l),c+r+l>s()&&(c=a.top>r?a.top-r:s()-r,c-=2*l),t.position&&(e.elem.style.position=t.position),e.elem.style.left=d+("fixed"===t.position?0:o(1))+"px",e.elem.style.top=c+("fixed"===t.position?0:o())+"px"},T.prototype.hint=function(e){var t=this,n=(t.config,w.elem("div",{"class":h}));n.innerHTML=e||"",w(t.elem).find("."+h).remove(),t.elem.appendChild(n),clearTimeout(t.hinTimer),t.hinTimer=setTimeout(function(){w(t.elem).find("."+h).remove()},3e3)},T.prototype.getAsYM=function(e,t,n){return n?t--:t++,t<0&&(t=11,e--),t>11&&(t=0,e++),[e,t]},T.prototype.systemDate=function(e){var t=e||new Date;return{year:t.getFullYear(),month:t.getMonth(),date:t.getDate(),hours:e?e.getHours():0,minutes:e?e.getMinutes():0,seconds:e?e.getSeconds():0}},T.prototype.checkDate=function(e){var t,a,i=this,r=(new Date,i.config),o=r.dateTime=r.dateTime||i.systemDate(),s=i.bindElem||r.elem[0],l=(i.isInput(s)?"val":"html",i.isInput(s)?s.value:"static"===r.position?"":s.innerHTML),c=function(e){e.year>d[1]&&(e.year=d[1],a=!0),e.month>11&&(e.month=11,a=!0),e.hours>23&&(e.hours=0,a=!0),e.minutes>59&&(e.minutes=0,e.hours++,a=!0),e.seconds>59&&(e.seconds=0,e.minutes++,a=!0),t=n.getEndDate(e.month+1,e.year),e.date>t&&(e.date=t,a=!0)},m=function(e,t,n){var o=["startTime","endTime"];t=(t.match(i.EXP_SPLIT)||[]).slice(1),n=n||0,r.range&&(i[o[n]]=i[o[n]]||{}),w.each(i.format,function(s,l){var c=parseFloat(t[s]);t[s].length必须遵循下述格式:
      "+(r.range?r.format+" "+r.range+" "+r.format:r.format)+"
      已为你重置"),a=!0):l&&l.constructor===Date?r.dateTime=i.systemDate(l):(r.dateTime=i.systemDate(),delete i.startState,delete i.endState,delete i.startDate,delete i.endDate,delete i.startTime,delete i.endTime),c(o),a&&l&&i.setValue(r.range?i.endDate?i.parse():"":i.parse()),e&&e(),i)},T.prototype.mark=function(e,t){var n,a=this,i=a.config;return w.each(i.mark,function(e,a){var i=e.split("-");i[0]!=t[0]&&0!=i[0]||i[1]!=t[1]&&0!=i[1]||i[2]!=t[2]||(n=a||t[2])}),n&&e.html(''+n+""),a},T.prototype.limit=function(e,t,n,a){var i,r=this,o=r.config,l={},d=o[n>41?"endDate":"dateTime"],c=w.extend({},d,t||{});return w.each({now:c,min:o.min,max:o.max},function(e,t){l[e]=r.newDate(w.extend({year:t.year,month:t.month,date:t.date},function(){var e={};return w.each(a,function(n,a){e[a]=t[a]}),e}())).getTime()}),i=l.nowl.max,e&&e[i?"addClass":"removeClass"](s),i},T.prototype.calendar=function(e){var t,a,i,r=this,s=r.config,l=e||s.dateTime,c=new Date,m=r.lang(),u="date"!==s.type&&"datetime"!==s.type,h=e?1:0,y=w(r.table[h]).find("td"),f=w(r.elemHeader[h][2]).find("span");if(l.yeard[1]&&(l.year=d[1],r.hint("最高只能支持到公元"+d[1]+"年")),r.firstDate||(r.firstDate=w.extend({},l)),c.setFullYear(l.year,l.month,1),t=c.getDay(),a=n.getEndDate(l.month||12,l.year),i=n.getEndDate(l.month+1,l.year),w.each(y,function(e,n){var d=[l.year,l.month],c=0;n=w(n),n.removeAttr("class"),e=t&&e=n.firstDate.year&&(r.month=a.max.month,r.date=a.max.date),n.limit(w(i),r,t),M++}),w(u[f?0:1]).attr("lay-ym",M-8+"-"+T[1]).html(b+p+" - "+(M-1+p))}else if("month"===e)w.each(new Array(12),function(e){var i=w.elem("li",{"lay-ym":e}),s={year:T[0],month:e};e+1==T[1]&&w(i).addClass(o),i.innerHTML=r.month[e]+(f?"月":""),d.appendChild(i),T[0]=n.firstDate.year&&(s.date=a.max.date),n.limit(w(i),s,t)}),w(u[f?0:1]).attr("lay-ym",T[0]+"-"+T[1]).html(T[0]+p);else if("time"===e){var E=function(){w(d).find("ol").each(function(e,a){w(a).find("li").each(function(a,i){n.limit(w(i),[{hours:a},{hours:n[x].hours,minutes:a},{hours:n[x].hours,minutes:n[x].minutes,seconds:a}][e],t,[["hours"],["hours","minutes"],["hours","minutes","seconds"]][e])})}),a.range||n.limit(w(n.footer).find(g),n[x],0,["hours","minutes","seconds"])};a.range?n[x]||(n[x]={hours:0,minutes:0,seconds:0}):n[x]=i,w.each([24,60,60],function(e,t){var a=w.elem("li"),i=["

      "+r.time[e]+"

        "];w.each(new Array(t),function(t){i.push(""+w.digit(t,2)+"")}),a.innerHTML=i.join("")+"
      ",d.appendChild(a)}),E()}if(y&&h.removeChild(y),h.appendChild(d),"year"===e||"month"===e)w(n.elemMain[t]).addClass("laydate-ym-show"),w(d).find("li").on("click",function(){var r=0|w(this).attr("lay-ym");if(!w(this).hasClass(s)){if(0===t)i[e]=r,l&&(n.startDate[e]=r),n.limit(w(n.footer).find(g),null,0);else if(l)n.endDate[e]=r;else{var c="year"===e?n.getAsYM(r,T[1]-1,"sub"):n.getAsYM(T[0],r,"sub");w.extend(i,{year:c[0],month:c[1]})}"year"===a.type||"month"===a.type?(w(d).find("."+o).removeClass(o),w(this).addClass(o),"month"===a.type&&"year"===e&&(n.listYM[t][0]=r,l&&(n[["startDate","endDate"][t]].year=r),n.list("month",t))):(n.checkDate("limit").calendar(),n.closeList()),n.setBtnStatus(),a.range||n.done(null,"change"),w(n.footer).find(D).removeClass(s)}});else{var S=w.elem("span",{"class":v}),k=function(){w(d).find("ol").each(function(e){var t=this,a=w(t).find("li");t.scrollTop=30*(n[x][C[e]]-2),t.scrollTop<=0&&a.each(function(e,n){if(!w(this).hasClass(s))return t.scrollTop=30*(e-2),!0})})},H=w(c[2]).find("."+v);k(),S.innerHTML=a.range?[r.startTime,r.endTime][t]:r.timeTips,w(n.elemMain[t]).addClass("laydate-time-show"),H[0]&&H.remove(),c[2].appendChild(S),w(d).find("ol").each(function(e){var t=this;w(t).find("li").on("click",function(){var r=0|this.innerHTML;w(this).hasClass(s)||(a.range?n[x][C[e]]=r:i[C[e]]=r,w(t).find("."+o).removeClass(o),w(this).addClass(o),E(),k(),(n.endDate||"time"===a.type)&&n.done(null,"change"),n.setBtnStatus())})})}return n},T.prototype.listYM=[],T.prototype.closeList=function(){var e=this;e.config;w.each(e.elemCont,function(t,n){w(this).find("."+m).remove(),w(e.elemMain[t]).removeClass("laydate-ym-show laydate-time-show")}),w(e.elem).find("."+v).remove()},T.prototype.setBtnStatus=function(e,t,n){var a,i=this,r=i.config,o=w(i.footer).find(g),d=r.range&&"date"!==r.type&&"time"!==r.type;d&&(t=t||i.startDate,n=n||i.endDate,a=i.newDate(t).getTime()>i.newDate(n).getTime(),i.limit(null,t)||i.limit(null,n)?o.addClass(s):o[a?"addClass":"removeClass"](s),e&&a&&i.hint("string"==typeof e?l.replace(/日期/g,e):l))},T.prototype.parse=function(e,t){var n=this,a=n.config,i=t||(e?w.extend({},n.endDate,n.endTime):a.range?w.extend({},n.startDate,n.startTime):a.dateTime),r=n.format.concat();return w.each(r,function(e,t){/yyyy|y/.test(t)?r[e]=w.digit(i.year,t.length):/MM|M/.test(t)?r[e]=w.digit(i.month+1,t.length):/dd|d/.test(t)?r[e]=w.digit(i.date,t.length):/HH|H/.test(t)?r[e]=w.digit(i.hours,t.length):/mm|m/.test(t)?r[e]=w.digit(i.minutes,t.length):/ss|s/.test(t)&&(r[e]=w.digit(i.seconds,t.length))}),a.range&&!e?r.join("")+" "+a.range+" "+n.parse(1):r.join("")},T.prototype.newDate=function(e){return e=e||{},new Date(e.year||1,e.month||0,e.date||1,e.hours||0,e.minutes||0,e.seconds||0)},T.prototype.setValue=function(e){var t=this,n=t.config,a=t.bindElem||n.elem[0],i=t.isInput(a)?"val":"html";return"static"===n.position||w(a)[i](e||""),this},T.prototype.stampRange=function(){var e,t,n=this,a=n.config,i=w(n.elem).find("td");if(a.range&&!n.endDate&&w(n.footer).find(g).addClass(s),n.endDate)return e=n.newDate({year:n.startDate.year,month:n.startDate.month,date:n.startDate.date}).getTime(),t=n.newDate({year:n.endDate.year,month:n.endDate.month,date:n.endDate.date}).getTime(),e>t?n.hint(l):void w.each(i,function(a,i){var r=w(i).attr("lay-ymd").split("-"),s=n.newDate({year:r[0],month:r[1]-1,date:r[2]}).getTime();w(i).removeClass(u+" "+o),s!==e&&s!==t||w(i).addClass(w(i).hasClass(y)||w(i).hasClass(f)?u:o),s>e&&s0&&t-1 in e)}function r(e,t,n){if(pe.isFunction(t))return pe.grep(e,function(e,r){return!!t.call(e,r,e)!==n});if(t.nodeType)return pe.grep(e,function(e){return e===t!==n});if("string"==typeof t){if(Ce.test(t))return pe.filter(t,e,n);t=pe.filter(t,e)}return pe.grep(e,function(e){return pe.inArray(e,t)>-1!==n})}function i(e,t){do e=e[t];while(e&&1!==e.nodeType);return e}function o(e){var t={};return pe.each(e.match(De)||[],function(e,n){t[n]=!0}),t}function a(){re.addEventListener?(re.removeEventListener("DOMContentLoaded",s),e.removeEventListener("load",s)):(re.detachEvent("onreadystatechange",s),e.detachEvent("onload",s))}function s(){(re.addEventListener||"load"===e.event.type||"complete"===re.readyState)&&(a(),pe.ready())}function u(e,t,n){if(void 0===n&&1===e.nodeType){var r="data-"+t.replace(_e,"-$1").toLowerCase();if(n=e.getAttribute(r),"string"==typeof n){try{n="true"===n||"false"!==n&&("null"===n?null:+n+""===n?+n:qe.test(n)?pe.parseJSON(n):n)}catch(i){}pe.data(e,t,n)}else n=void 0}return n}function l(e){var t;for(t in e)if(("data"!==t||!pe.isEmptyObject(e[t]))&&"toJSON"!==t)return!1;return!0}function c(e,t,n,r){if(He(e)){var i,o,a=pe.expando,s=e.nodeType,u=s?pe.cache:e,l=s?e[a]:e[a]&&a;if(l&&u[l]&&(r||u[l].data)||void 0!==n||"string"!=typeof t)return l||(l=s?e[a]=ne.pop()||pe.guid++:a),u[l]||(u[l]=s?{}:{toJSON:pe.noop}),"object"!=typeof t&&"function"!=typeof t||(r?u[l]=pe.extend(u[l],t):u[l].data=pe.extend(u[l].data,t)),o=u[l],r||(o.data||(o.data={}),o=o.data),void 0!==n&&(o[pe.camelCase(t)]=n),"string"==typeof t?(i=o[t],null==i&&(i=o[pe.camelCase(t)])):i=o,i}}function f(e,t,n){if(He(e)){var r,i,o=e.nodeType,a=o?pe.cache:e,s=o?e[pe.expando]:pe.expando;if(a[s]){if(t&&(r=n?a[s]:a[s].data)){pe.isArray(t)?t=t.concat(pe.map(t,pe.camelCase)):t in r?t=[t]:(t=pe.camelCase(t),t=t in r?[t]:t.split(" ")),i=t.length;for(;i--;)delete r[t[i]];if(n?!l(r):!pe.isEmptyObject(r))return}(n||(delete a[s].data,l(a[s])))&&(o?pe.cleanData([e],!0):fe.deleteExpando||a!=a.window?delete a[s]:a[s]=void 0)}}}function d(e,t,n,r){var i,o=1,a=20,s=r?function(){return r.cur()}:function(){return pe.css(e,t,"")},u=s(),l=n&&n[3]||(pe.cssNumber[t]?"":"px"),c=(pe.cssNumber[t]||"px"!==l&&+u)&&Me.exec(pe.css(e,t));if(c&&c[3]!==l){l=l||c[3],n=n||[],c=+u||1;do o=o||".5",c/=o,pe.style(e,t,c+l);while(o!==(o=s()/u)&&1!==o&&--a)}return n&&(c=+c||+u||0,i=n[1]?c+(n[1]+1)*n[2]:+n[2],r&&(r.unit=l,r.start=c,r.end=i)),i}function p(e){var t=ze.split("|"),n=e.createDocumentFragment();if(n.createElement)for(;t.length;)n.createElement(t.pop());return n}function h(e,t){var n,r,i=0,o="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):void 0;if(!o)for(o=[],n=e.childNodes||e;null!=(r=n[i]);i++)!t||pe.nodeName(r,t)?o.push(r):pe.merge(o,h(r,t));return void 0===t||t&&pe.nodeName(e,t)?pe.merge([e],o):o}function g(e,t){for(var n,r=0;null!=(n=e[r]);r++)pe._data(n,"globalEval",!t||pe._data(t[r],"globalEval"))}function m(e){Be.test(e.type)&&(e.defaultChecked=e.checked)}function y(e,t,n,r,i){for(var o,a,s,u,l,c,f,d=e.length,y=p(t),v=[],x=0;x"!==f[1]||Ve.test(a)?0:u:u.firstChild,o=a&&a.childNodes.length;o--;)pe.nodeName(c=a.childNodes[o],"tbody")&&!c.childNodes.length&&a.removeChild(c);for(pe.merge(v,u.childNodes),u.textContent="";u.firstChild;)u.removeChild(u.firstChild);u=y.lastChild}else v.push(t.createTextNode(a));for(u&&y.removeChild(u),fe.appendChecked||pe.grep(h(v,"input"),m),x=0;a=v[x++];)if(r&&pe.inArray(a,r)>-1)i&&i.push(a);else if(s=pe.contains(a.ownerDocument,a),u=h(y.appendChild(a),"script"),s&&g(u),n)for(o=0;a=u[o++];)Ie.test(a.type||"")&&n.push(a);return u=null,y}function v(){return!0}function x(){return!1}function b(){try{return re.activeElement}catch(e){}}function w(e,t,n,r,i,o){var a,s;if("object"==typeof t){"string"!=typeof n&&(r=r||n,n=void 0);for(s in t)w(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),i===!1)i=x;else if(!i)return e;return 1===o&&(a=i,i=function(e){return pe().off(e),a.apply(this,arguments)},i.guid=a.guid||(a.guid=pe.guid++)),e.each(function(){pe.event.add(this,t,i,r,n)})}function T(e,t){return pe.nodeName(e,"table")&&pe.nodeName(11!==t.nodeType?t:t.firstChild,"tr")?e.getElementsByTagName("tbody")[0]||e.appendChild(e.ownerDocument.createElement("tbody")):e}function C(e){return e.type=(null!==pe.find.attr(e,"type"))+"/"+e.type,e}function E(e){var t=it.exec(e.type);return t?e.type=t[1]:e.removeAttribute("type"),e}function N(e,t){if(1===t.nodeType&&pe.hasData(e)){var n,r,i,o=pe._data(e),a=pe._data(t,o),s=o.events;if(s){delete a.handle,a.events={};for(n in s)for(r=0,i=s[n].length;r1&&"string"==typeof p&&!fe.checkClone&&rt.test(p))return e.each(function(i){var o=e.eq(i);g&&(t[0]=p.call(this,i,o.html())),S(o,t,n,r)});if(f&&(l=y(t,e[0].ownerDocument,!1,e,r),i=l.firstChild,1===l.childNodes.length&&(l=i),i||r)){for(s=pe.map(h(l,"script"),C),a=s.length;c")).appendTo(t.documentElement),t=(ut[0].contentWindow||ut[0].contentDocument).document,t.write(),t.close(),n=D(e,t),ut.detach()),lt[e]=n),n}function L(e,t){return{get:function(){return e()?void delete this.get:(this.get=t).apply(this,arguments)}}}function H(e){if(e in Et)return e;for(var t=e.charAt(0).toUpperCase()+e.slice(1),n=Ct.length;n--;)if(e=Ct[n]+t,e in Et)return e}function q(e,t){for(var n,r,i,o=[],a=0,s=e.length;a=0&&n=0},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},isPlainObject:function(e){var t;if(!e||"object"!==pe.type(e)||e.nodeType||pe.isWindow(e))return!1;try{if(e.constructor&&!ce.call(e,"constructor")&&!ce.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(n){return!1}if(!fe.ownFirst)for(t in e)return ce.call(e,t);for(t in e);return void 0===t||ce.call(e,t)},type:function(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?ue[le.call(e)]||"object":typeof e},globalEval:function(t){t&&pe.trim(t)&&(e.execScript||function(t){e.eval.call(e,t)})(t)},camelCase:function(e){return e.replace(ge,"ms-").replace(me,ye)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,t){var r,i=0;if(n(e))for(r=e.length;iT.cacheLength&&delete e[t.shift()],e[n+" "]=r}var t=[];return e}function r(e){return e[P]=!0,e}function i(e){var t=H.createElement("div");try{return!!e(t)}catch(n){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function o(e,t){for(var n=e.split("|"),r=n.length;r--;)T.attrHandle[n[r]]=t}function a(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&(~t.sourceIndex||V)-(~e.sourceIndex||V);if(r)return r;if(n)for(;n=n.nextSibling;)if(n===t)return-1;return e?1:-1}function s(e){return function(t){var n=t.nodeName.toLowerCase();return"input"===n&&t.type===e}}function u(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function l(e){return r(function(t){return t=+t,r(function(n,r){for(var i,o=e([],n.length,t),a=o.length;a--;)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}function c(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}function f(){}function d(e){for(var t=0,n=e.length,r="";t1?function(t,n,r){for(var i=e.length;i--;)if(!e[i](t,n,r))return!1;return!0}:e[0]}function g(e,n,r){for(var i=0,o=n.length;i-1&&(r[l]=!(a[l]=f))}}else x=m(x===a?x.splice(h,x.length):x),o?o(null,a,x,u):Q.apply(a,x)})}function v(e){for(var t,n,r,i=e.length,o=T.relative[e[0].type],a=o||T.relative[" "],s=o?1:0,u=p(function(e){return e===t},a,!0),l=p(function(e){return ee(t,e)>-1},a,!0),c=[function(e,n,r){var i=!o&&(r||n!==A)||((t=n).nodeType?u(e,n,r):l(e,n,r));return t=null,i}];s1&&h(c),s>1&&d(e.slice(0,s-1).concat({value:" "===e[s-2].type?"*":""})).replace(se,"$1"),n,s0,o=e.length>0,a=function(r,a,s,u,l){var c,f,d,p=0,h="0",g=r&&[],y=[],v=A,x=r||o&&T.find.TAG("*",l),b=W+=null==v?1:Math.random()||.1,w=x.length;for(l&&(A=a===H||a||l);h!==w&&null!=(c=x[h]);h++){if(o&&c){for(f=0,a||c.ownerDocument===H||(L(c),s=!_);d=e[f++];)if(d(c,a||H,s)){u.push(c);break}l&&(W=b)}i&&((c=!d&&c)&&p--,r&&g.push(c))}if(p+=h,i&&h!==p){for(f=0;d=n[f++];)d(g,y,a,s);if(r){if(p>0)for(;h--;)g[h]||y[h]||(y[h]=G.call(u));y=m(y)}Q.apply(u,y),l&&!r&&y.length>0&&p+n.length>1&&t.uniqueSort(u)}return l&&(W=b,A=v),g};return i?r(a):a}var b,w,T,C,E,N,k,S,A,D,j,L,H,q,_,F,M,O,R,P="sizzle"+1*new Date,B=e.document,W=0,I=0,$=n(),z=n(),X=n(),U=function(e,t){return e===t&&(j=!0),0},V=1<<31,Y={}.hasOwnProperty,J=[],G=J.pop,K=J.push,Q=J.push,Z=J.slice,ee=function(e,t){for(var n=0,r=e.length;n+~]|"+ne+")"+ne+"*"),ce=new RegExp("="+ne+"*([^\\]'\"]*?)"+ne+"*\\]","g"),fe=new RegExp(oe),de=new RegExp("^"+re+"$"),pe={ID:new RegExp("^#("+re+")"),CLASS:new RegExp("^\\.("+re+")"),TAG:new RegExp("^("+re+"|[*])"),ATTR:new RegExp("^"+ie),PSEUDO:new RegExp("^"+oe),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+ne+"*(even|odd|(([+-]|)(\\d*)n|)"+ne+"*(?:([+-]|)"+ne+"*(\\d+)|))"+ne+"*\\)|)","i"),bool:new RegExp("^(?:"+te+")$","i"),needsContext:new RegExp("^"+ne+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+ne+"*((?:-\\d)?\\d*)"+ne+"*\\)|)(?=[^-]|$)","i")},he=/^(?:input|select|textarea|button)$/i,ge=/^h\d$/i,me=/^[^{]+\{\s*\[native \w/,ye=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ve=/[+~]/,xe=/'|\\/g,be=new RegExp("\\\\([\\da-f]{1,6}"+ne+"?|("+ne+")|.)","ig"),we=function(e,t,n){var r="0x"+t-65536;return r!==r||n?t:r<0?String.fromCharCode(r+65536):String.fromCharCode(r>>10|55296,1023&r|56320)},Te=function(){L()};try{Q.apply(J=Z.call(B.childNodes),B.childNodes),J[B.childNodes.length].nodeType}catch(Ce){Q={apply:J.length?function(e,t){K.apply(e,Z.call(t))}:function(e,t){for(var n=e.length,r=0;e[n++]=t[r++];);e.length=n-1}}}w=t.support={},E=t.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return!!t&&"HTML"!==t.nodeName},L=t.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:B;return r!==H&&9===r.nodeType&&r.documentElement?(H=r,q=H.documentElement,_=!E(H),(n=H.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",Te,!1):n.attachEvent&&n.attachEvent("onunload",Te)),w.attributes=i(function(e){return e.className="i",!e.getAttribute("className")}),w.getElementsByTagName=i(function(e){return e.appendChild(H.createComment("")),!e.getElementsByTagName("*").length}),w.getElementsByClassName=me.test(H.getElementsByClassName),w.getById=i(function(e){return q.appendChild(e).id=P,!H.getElementsByName||!H.getElementsByName(P).length}),w.getById?(T.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&_){var n=t.getElementById(e);return n?[n]:[]}},T.filter.ID=function(e){var t=e.replace(be,we);return function(e){return e.getAttribute("id")===t}}):(delete T.find.ID,T.filter.ID=function(e){var t=e.replace(be,we);return function(e){var n="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return n&&n.value===t}}),T.find.TAG=w.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):w.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){for(;n=o[i++];)1===n.nodeType&&r.push(n);return r}return o},T.find.CLASS=w.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&_)return t.getElementsByClassName(e)},M=[],F=[],(w.qsa=me.test(H.querySelectorAll))&&(i(function(e){q.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&F.push("[*^$]="+ne+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||F.push("\\["+ne+"*(?:value|"+te+")"),e.querySelectorAll("[id~="+P+"-]").length||F.push("~="),e.querySelectorAll(":checked").length||F.push(":checked"),e.querySelectorAll("a#"+P+"+*").length||F.push(".#.+[+~]")}),i(function(e){var t=H.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&F.push("name"+ne+"*[*^$|!~]?="),e.querySelectorAll(":enabled").length||F.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),F.push(",.*:")})),(w.matchesSelector=me.test(O=q.matches||q.webkitMatchesSelector||q.mozMatchesSelector||q.oMatchesSelector||q.msMatchesSelector))&&i(function(e){w.disconnectedMatch=O.call(e,"div"),O.call(e,"[s!='']:x"),M.push("!=",oe)}),F=F.length&&new RegExp(F.join("|")),M=M.length&&new RegExp(M.join("|")),t=me.test(q.compareDocumentPosition),R=t||me.test(q.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)for(;t=t.parentNode;)if(t===e)return!0;return!1},U=t?function(e,t){if(e===t)return j=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n?n:(n=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1,1&n||!w.sortDetached&&t.compareDocumentPosition(e)===n?e===H||e.ownerDocument===B&&R(B,e)?-1:t===H||t.ownerDocument===B&&R(B,t)?1:D?ee(D,e)-ee(D,t):0:4&n?-1:1)}:function(e,t){if(e===t)return j=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,s=[e],u=[t];if(!i||!o)return e===H?-1:t===H?1:i?-1:o?1:D?ee(D,e)-ee(D,t):0;if(i===o)return a(e,t);for(n=e;n=n.parentNode;)s.unshift(n);for(n=t;n=n.parentNode;)u.unshift(n);for(;s[r]===u[r];)r++;return r?a(s[r],u[r]):s[r]===B?-1:u[r]===B?1:0},H):H},t.matches=function(e,n){return t(e,null,null,n)},t.matchesSelector=function(e,n){if((e.ownerDocument||e)!==H&&L(e),n=n.replace(ce,"='$1']"),w.matchesSelector&&_&&!X[n+" "]&&(!M||!M.test(n))&&(!F||!F.test(n)))try{var r=O.call(e,n);if(r||w.disconnectedMatch||e.document&&11!==e.document.nodeType)return r}catch(i){}return t(n,H,null,[e]).length>0},t.contains=function(e,t){return(e.ownerDocument||e)!==H&&L(e),R(e,t)},t.attr=function(e,t){(e.ownerDocument||e)!==H&&L(e);var n=T.attrHandle[t.toLowerCase()],r=n&&Y.call(T.attrHandle,t.toLowerCase())?n(e,t,!_):void 0;return void 0!==r?r:w.attributes||!_?e.getAttribute(t):(r=e.getAttributeNode(t))&&r.specified?r.value:null},t.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},t.uniqueSort=function(e){var t,n=[],r=0,i=0;if(j=!w.detectDuplicates,D=!w.sortStable&&e.slice(0),e.sort(U),j){for(;t=e[i++];)t===e[i]&&(r=n.push(i));for(;r--;)e.splice(n[r],1)}return D=null,e},C=t.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=C(e)}else if(3===i||4===i)return e.nodeValue}else for(;t=e[r++];)n+=C(t);return n},T=t.selectors={cacheLength:50,createPseudo:r,match:pe,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(be,we),e[3]=(e[3]||e[4]||e[5]||"").replace(be,we),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||t.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&t.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return pe.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&fe.test(n)&&(t=N(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(be,we).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=$[e+" "];return t||(t=new RegExp("(^|"+ne+")"+e+"("+ne+"|$)"))&&$(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(e,n,r){return function(i){var o=t.attr(i,e);return null==o?"!="===n:!n||(o+="","="===n?o===r:"!="===n?o!==r:"^="===n?r&&0===o.indexOf(r):"*="===n?r&&o.indexOf(r)>-1:"$="===n?r&&o.slice(-r.length)===r:"~="===n?(" "+o.replace(ae," ")+" ").indexOf(r)>-1:"|="===n&&(o===r||o.slice(0,r.length+1)===r+"-"))}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,u){var l,c,f,d,p,h,g=o!==a?"nextSibling":"previousSibling",m=t.parentNode,y=s&&t.nodeName.toLowerCase(),v=!u&&!s,x=!1;if(m){if(o){for(;g;){for(d=t;d=d[g];)if(s?d.nodeName.toLowerCase()===y:1===d.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?m.firstChild:m.lastChild],a&&v){for(d=m,f=d[P]||(d[P]={}),c=f[d.uniqueID]||(f[d.uniqueID]={}), +l=c[e]||[],p=l[0]===W&&l[1],x=p&&l[2],d=p&&m.childNodes[p];d=++p&&d&&d[g]||(x=p=0)||h.pop();)if(1===d.nodeType&&++x&&d===t){c[e]=[W,p,x];break}}else if(v&&(d=t,f=d[P]||(d[P]={}),c=f[d.uniqueID]||(f[d.uniqueID]={}),l=c[e]||[],p=l[0]===W&&l[1],x=p),x===!1)for(;(d=++p&&d&&d[g]||(x=p=0)||h.pop())&&((s?d.nodeName.toLowerCase()!==y:1!==d.nodeType)||!++x||(v&&(f=d[P]||(d[P]={}),c=f[d.uniqueID]||(f[d.uniqueID]={}),c[e]=[W,x]),d!==t)););return x-=i,x===r||x%r===0&&x/r>=0}}},PSEUDO:function(e,n){var i,o=T.pseudos[e]||T.setFilters[e.toLowerCase()]||t.error("unsupported pseudo: "+e);return o[P]?o(n):o.length>1?(i=[e,e,"",n],T.setFilters.hasOwnProperty(e.toLowerCase())?r(function(e,t){for(var r,i=o(e,n),a=i.length;a--;)r=ee(e,i[a]),e[r]=!(t[r]=i[a])}):function(e){return o(e,0,i)}):o}},pseudos:{not:r(function(e){var t=[],n=[],i=k(e.replace(se,"$1"));return i[P]?r(function(e,t,n,r){for(var o,a=i(e,null,r,[]),s=e.length;s--;)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,r,o){return t[0]=e,i(t,null,o,n),t[0]=null,!n.pop()}}),has:r(function(e){return function(n){return t(e,n).length>0}}),contains:r(function(e){return e=e.replace(be,we),function(t){return(t.textContent||t.innerText||C(t)).indexOf(e)>-1}}),lang:r(function(e){return de.test(e||"")||t.error("unsupported lang: "+e),e=e.replace(be,we).toLowerCase(),function(t){var n;do if(n=_?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return n=n.toLowerCase(),n===e||0===n.indexOf(e+"-");while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===q},focus:function(e){return e===H.activeElement&&(!H.hasFocus||H.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!T.pseudos.empty(e)},header:function(e){return ge.test(e.nodeName)},input:function(e){return he.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:l(function(){return[0]}),last:l(function(e,t){return[t-1]}),eq:l(function(e,t,n){return[n<0?n+t:n]}),even:l(function(e,t){for(var n=0;n=0;)e.push(r);return e}),gt:l(function(e,t,n){for(var r=n<0?n+t:n;++r2&&"ID"===(a=o[0]).type&&w.getById&&9===t.nodeType&&_&&T.relative[o[1].type]){if(t=(T.find.ID(a.matches[0].replace(be,we),t)||[])[0],!t)return n;l&&(t=t.parentNode),e=e.slice(o.shift().value.length)}for(i=pe.needsContext.test(e)?0:o.length;i--&&(a=o[i],!T.relative[s=a.type]);)if((u=T.find[s])&&(r=u(a.matches[0].replace(be,we),ve.test(o[0].type)&&c(t.parentNode)||t))){if(o.splice(i,1),e=r.length&&d(o),!e)return Q.apply(n,r),n;break}}return(l||k(e,f))(r,t,!_,n,!t||ve.test(e)&&c(t.parentNode)||t),n},w.sortStable=P.split("").sort(U).join("")===P,w.detectDuplicates=!!j,L(),w.sortDetached=i(function(e){return 1&e.compareDocumentPosition(H.createElement("div"))}),i(function(e){return e.innerHTML="","#"===e.firstChild.getAttribute("href")})||o("type|href|height|width",function(e,t,n){if(!n)return e.getAttribute(t,"type"===t.toLowerCase()?1:2)}),w.attributes&&i(function(e){return e.innerHTML="",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||o("value",function(e,t,n){if(!n&&"input"===e.nodeName.toLowerCase())return e.defaultValue}),i(function(e){return null==e.getAttribute("disabled")})||o(te,function(e,t,n){var r;if(!n)return e[t]===!0?t.toLowerCase():(r=e.getAttributeNode(t))&&r.specified?r.value:null}),t}(e);pe.find=ve,pe.expr=ve.selectors,pe.expr[":"]=pe.expr.pseudos,pe.uniqueSort=pe.unique=ve.uniqueSort,pe.text=ve.getText,pe.isXMLDoc=ve.isXML,pe.contains=ve.contains;var xe=function(e,t,n){for(var r=[],i=void 0!==n;(e=e[t])&&9!==e.nodeType;)if(1===e.nodeType){if(i&&pe(e).is(n))break;r.push(e)}return r},be=function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n},we=pe.expr.match.needsContext,Te=/^<([\w-]+)\s*\/?>(?:<\/\1>|)$/,Ce=/^.[^:#\[\.,]*$/;pe.filter=function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?pe.find.matchesSelector(r,e)?[r]:[]:pe.find.matches(e,pe.grep(t,function(e){return 1===e.nodeType}))},pe.fn.extend({find:function(e){var t,n=[],r=this,i=r.length;if("string"!=typeof e)return this.pushStack(pe(e).filter(function(){for(t=0;t1?pe.unique(n):n),n.selector=this.selector?this.selector+" "+e:e,n},filter:function(e){return this.pushStack(r(this,e||[],!1))},not:function(e){return this.pushStack(r(this,e||[],!0))},is:function(e){return!!r(this,"string"==typeof e&&we.test(e)?pe(e):e||[],!1).length}});var Ee,Ne=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,ke=pe.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||Ee,"string"==typeof e){if(r="<"===e.charAt(0)&&">"===e.charAt(e.length-1)&&e.length>=3?[null,e,null]:Ne.exec(e),!r||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof pe?t[0]:t,pe.merge(this,pe.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:re,!0)),Te.test(r[1])&&pe.isPlainObject(t))for(r in t)pe.isFunction(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}if(i=re.getElementById(r[2]),i&&i.parentNode){if(i.id!==r[2])return Ee.find(e);this.length=1,this[0]=i}return this.context=re,this.selector=e,this}return e.nodeType?(this.context=this[0]=e,this.length=1,this):pe.isFunction(e)?"undefined"!=typeof n.ready?n.ready(e):e(pe):(void 0!==e.selector&&(this.selector=e.selector,this.context=e.context),pe.makeArray(e,this))};ke.prototype=pe.fn,Ee=pe(re);var Se=/^(?:parents|prev(?:Until|All))/,Ae={children:!0,contents:!0,next:!0,prev:!0};pe.fn.extend({has:function(e){var t,n=pe(e,this),r=n.length;return this.filter(function(){for(t=0;t-1:1===n.nodeType&&pe.find.matchesSelector(n,e))){o.push(n);break}return this.pushStack(o.length>1?pe.uniqueSort(o):o)},index:function(e){return e?"string"==typeof e?pe.inArray(this[0],pe(e)):pe.inArray(e.jquery?e[0]:e,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(pe.uniqueSort(pe.merge(this.get(),pe(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),pe.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return xe(e,"parentNode")},parentsUntil:function(e,t,n){return xe(e,"parentNode",n)},next:function(e){return i(e,"nextSibling")},prev:function(e){return i(e,"previousSibling")},nextAll:function(e){return xe(e,"nextSibling")},prevAll:function(e){return xe(e,"previousSibling")},nextUntil:function(e,t,n){return xe(e,"nextSibling",n)},prevUntil:function(e,t,n){return xe(e,"previousSibling",n)},siblings:function(e){return be((e.parentNode||{}).firstChild,e)},children:function(e){return be(e.firstChild)},contents:function(e){return pe.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:pe.merge([],e.childNodes)}},function(e,t){pe.fn[e]=function(n,r){var i=pe.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(i=pe.filter(r,i)),this.length>1&&(Ae[e]||(i=pe.uniqueSort(i)),Se.test(e)&&(i=i.reverse())),this.pushStack(i)}});var De=/\S+/g;pe.Callbacks=function(e){e="string"==typeof e?o(e):pe.extend({},e);var t,n,r,i,a=[],s=[],u=-1,l=function(){for(i=e.once,r=t=!0;s.length;u=-1)for(n=s.shift();++u-1;)a.splice(n,1),n<=u&&u--}),this},has:function(e){return e?pe.inArray(e,a)>-1:a.length>0},empty:function(){return a&&(a=[]),this},disable:function(){return i=s=[],a=n="",this},disabled:function(){return!a},lock:function(){return i=!0,n||c.disable(),this},locked:function(){return!!i},fireWith:function(e,n){return i||(n=n||[],n=[e,n.slice?n.slice():n],s.push(n),t||l()),this},fire:function(){return c.fireWith(this,arguments),this},fired:function(){return!!r}};return c},pe.extend({Deferred:function(e){var t=[["resolve","done",pe.Callbacks("once memory"),"resolved"],["reject","fail",pe.Callbacks("once memory"),"rejected"],["notify","progress",pe.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return pe.Deferred(function(n){pe.each(t,function(t,o){var a=pe.isFunction(e[t])&&e[t];i[o[1]](function(){var e=a&&a.apply(this,arguments);e&&pe.isFunction(e.promise)?e.promise().progress(n.notify).done(n.resolve).fail(n.reject):n[o[0]+"With"](this===r?n.promise():this,a?[e]:arguments)})}),e=null}).promise()},promise:function(e){return null!=e?pe.extend(e,r):r}},i={};return r.pipe=r.then,pe.each(t,function(e,o){var a=o[2],s=o[3];r[o[1]]=a.add,s&&a.add(function(){n=s},t[1^e][2].disable,t[2][2].lock),i[o[0]]=function(){return i[o[0]+"With"](this===i?r:this,arguments),this},i[o[0]+"With"]=a.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t,n,r,i=0,o=ie.call(arguments),a=o.length,s=1!==a||e&&pe.isFunction(e.promise)?a:0,u=1===s?e:pe.Deferred(),l=function(e,n,r){return function(i){n[e]=this,r[e]=arguments.length>1?ie.call(arguments):i,r===t?u.notifyWith(n,r):--s||u.resolveWith(n,r)}};if(a>1)for(t=new Array(a),n=new Array(a),r=new Array(a);i0||(je.resolveWith(re,[pe]),pe.fn.triggerHandler&&(pe(re).triggerHandler("ready"),pe(re).off("ready"))))}}),pe.ready.promise=function(t){if(!je)if(je=pe.Deferred(),"complete"===re.readyState||"loading"!==re.readyState&&!re.documentElement.doScroll)e.setTimeout(pe.ready);else if(re.addEventListener)re.addEventListener("DOMContentLoaded",s),e.addEventListener("load",s);else{re.attachEvent("onreadystatechange",s),e.attachEvent("onload",s);var n=!1;try{n=null==e.frameElement&&re.documentElement}catch(r){}n&&n.doScroll&&!function i(){if(!pe.isReady){try{n.doScroll("left")}catch(t){return e.setTimeout(i,50)}a(),pe.ready()}}()}return je.promise(t)},pe.ready.promise();var Le;for(Le in pe(fe))break;fe.ownFirst="0"===Le,fe.inlineBlockNeedsLayout=!1,pe(function(){var e,t,n,r;n=re.getElementsByTagName("body")[0],n&&n.style&&(t=re.createElement("div"),r=re.createElement("div"),r.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",n.appendChild(r).appendChild(t),"undefined"!=typeof t.style.zoom&&(t.style.cssText="display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1",fe.inlineBlockNeedsLayout=e=3===t.offsetWidth,e&&(n.style.zoom=1)),n.removeChild(r))}),function(){var e=re.createElement("div");fe.deleteExpando=!0;try{delete e.test}catch(t){fe.deleteExpando=!1}e=null}();var He=function(e){var t=pe.noData[(e.nodeName+" ").toLowerCase()],n=+e.nodeType||1;return(1===n||9===n)&&(!t||t!==!0&&e.getAttribute("classid")===t)},qe=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,_e=/([A-Z])/g;pe.extend({cache:{},noData:{"applet ":!0,"embed ":!0,"object ":"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(e){return e=e.nodeType?pe.cache[e[pe.expando]]:e[pe.expando],!!e&&!l(e)},data:function(e,t,n){return c(e,t,n)},removeData:function(e,t){return f(e,t)},_data:function(e,t,n){return c(e,t,n,!0)},_removeData:function(e,t){return f(e,t,!0)}}),pe.fn.extend({data:function(e,t){var n,r,i,o=this[0],a=o&&o.attributes;if(void 0===e){if(this.length&&(i=pe.data(o),1===o.nodeType&&!pe._data(o,"parsedAttrs"))){for(n=a.length;n--;)a[n]&&(r=a[n].name,0===r.indexOf("data-")&&(r=pe.camelCase(r.slice(5)),u(o,r,i[r])));pe._data(o,"parsedAttrs",!0)}return i}return"object"==typeof e?this.each(function(){pe.data(this,e)}):arguments.length>1?this.each(function(){pe.data(this,e,t)}):o?u(o,e,pe.data(o,e)):void 0},removeData:function(e){return this.each(function(){pe.removeData(this,e)})}}),pe.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=pe._data(e,t),n&&(!r||pe.isArray(n)?r=pe._data(e,t,pe.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=pe.queue(e,t),r=n.length,i=n.shift(),o=pe._queueHooks(e,t),a=function(){pe.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return pe._data(e,n)||pe._data(e,n,{empty:pe.Callbacks("once memory").add(function(){pe._removeData(e,t+"queue"),pe._removeData(e,n)})})}}),pe.fn.extend({queue:function(e,t){var n=2;return"string"!=typeof e&&(t=e,e="fx",n--),arguments.length
      a",fe.leadingWhitespace=3===e.firstChild.nodeType,fe.tbody=!e.getElementsByTagName("tbody").length,fe.htmlSerialize=!!e.getElementsByTagName("link").length,fe.html5Clone="<:nav>"!==re.createElement("nav").cloneNode(!0).outerHTML,n.type="checkbox",n.checked=!0,t.appendChild(n),fe.appendChecked=n.checked,e.innerHTML="",fe.noCloneChecked=!!e.cloneNode(!0).lastChild.defaultValue,t.appendChild(e),n=re.createElement("input"),n.setAttribute("type","radio"),n.setAttribute("checked","checked"),n.setAttribute("name","t"),e.appendChild(n),fe.checkClone=e.cloneNode(!0).cloneNode(!0).lastChild.checked,fe.noCloneEvent=!!e.addEventListener,e[pe.expando]=1,fe.attributes=!e.getAttribute(pe.expando)}();var Xe={option:[1,""],legend:[1,"
      ","
      "],area:[1,"",""],param:[1,"",""],thead:[1,"","
      "],tr:[2,"","
      "],col:[2,"","
      "],td:[3,"","
      "],_default:fe.htmlSerialize?[0,"",""]:[1,"X
      ","
      "]};Xe.optgroup=Xe.option,Xe.tbody=Xe.tfoot=Xe.colgroup=Xe.caption=Xe.thead,Xe.th=Xe.td;var Ue=/<|&#?\w+;/,Ve=/-1&&(h=p.split("."),p=h.shift(),h.sort()),a=p.indexOf(":")<0&&"on"+p,t=t[pe.expando]?t:new pe.Event(p,"object"==typeof t&&t),t.isTrigger=i?2:3,t.namespace=h.join("."),t.rnamespace=t.namespace?new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,t.result=void 0,t.target||(t.target=r),n=null==n?[t]:pe.makeArray(n,[t]),l=pe.event.special[p]||{},i||!l.trigger||l.trigger.apply(r,n)!==!1)){if(!i&&!l.noBubble&&!pe.isWindow(r)){for(u=l.delegateType||p,Ke.test(u+p)||(s=s.parentNode);s;s=s.parentNode)d.push(s),c=s;c===(r.ownerDocument||re)&&d.push(c.defaultView||c.parentWindow||e)}for(f=0;(s=d[f++])&&!t.isPropagationStopped();)t.type=f>1?u:l.bindType||p,o=(pe._data(s,"events")||{})[t.type]&&pe._data(s,"handle"),o&&o.apply(s,n),o=a&&s[a],o&&o.apply&&He(s)&&(t.result=o.apply(s,n),t.result===!1&&t.preventDefault());if(t.type=p,!i&&!t.isDefaultPrevented()&&(!l._default||l._default.apply(d.pop(),n)===!1)&&He(r)&&a&&r[p]&&!pe.isWindow(r)){c=r[a],c&&(r[a]=null),pe.event.triggered=p;try{r[p]()}catch(g){}pe.event.triggered=void 0,c&&(r[a]=c)}return t.result}},dispatch:function(e){e=pe.event.fix(e);var t,n,r,i,o,a=[],s=ie.call(arguments),u=(pe._data(this,"events")||{})[e.type]||[],l=pe.event.special[e.type]||{};if(s[0]=e,e.delegateTarget=this,!l.preDispatch||l.preDispatch.call(this,e)!==!1){for(a=pe.event.handlers.call(this,e,u),t=0;(i=a[t++])&&!e.isPropagationStopped();)for(e.currentTarget=i.elem,n=0;(o=i.handlers[n++])&&!e.isImmediatePropagationStopped();)e.rnamespace&&!e.rnamespace.test(o.namespace)||(e.handleObj=o,e.data=o.data,r=((pe.event.special[o.origType]||{}).handle||o.handler).apply(i.elem,s),void 0!==r&&(e.result=r)===!1&&(e.preventDefault(),e.stopPropagation()));return l.postDispatch&&l.postDispatch.call(this,e),e.result}},handlers:function(e,t){var n,r,i,o,a=[],s=t.delegateCount,u=e.target;if(s&&u.nodeType&&("click"!==e.type||isNaN(e.button)||e.button<1))for(;u!=this;u=u.parentNode||this)if(1===u.nodeType&&(u.disabled!==!0||"click"!==e.type)){for(r=[],n=0;n-1:pe.find(i,this,null,[u]).length),r[i]&&r.push(o);r.length&&a.push({elem:u,handlers:r})}return s]","i"),tt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi,nt=/\s*$/g,at=p(re),st=at.appendChild(re.createElement("div"));pe.extend({htmlPrefilter:function(e){return e.replace(tt,"<$1>")},clone:function(e,t,n){var r,i,o,a,s,u=pe.contains(e.ownerDocument,e);if(fe.html5Clone||pe.isXMLDoc(e)||!et.test("<"+e.nodeName+">")?o=e.cloneNode(!0):(st.innerHTML=e.outerHTML,st.removeChild(o=st.firstChild)),!(fe.noCloneEvent&&fe.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||pe.isXMLDoc(e)))for(r=h(o),s=h(e),a=0;null!=(i=s[a]);++a)r[a]&&k(i,r[a]);if(t)if(n)for(s=s||h(e),r=r||h(o),a=0;null!=(i=s[a]);a++)N(i,r[a]);else N(e,o);return r=h(o,"script"),r.length>0&&g(r,!u&&h(e,"script")),r=s=i=null,o},cleanData:function(e,t){for(var n,r,i,o,a=0,s=pe.expando,u=pe.cache,l=fe.attributes,c=pe.event.special;null!=(n=e[a]);a++)if((t||He(n))&&(i=n[s],o=i&&u[i])){if(o.events)for(r in o.events)c[r]?pe.event.remove(n,r):pe.removeEvent(n,r,o.handle);u[i]&&(delete u[i],l||"undefined"==typeof n.removeAttribute?n[s]=void 0:n.removeAttribute(s),ne.push(i))}}}),pe.fn.extend({domManip:S,detach:function(e){return A(this,e,!0)},remove:function(e){return A(this,e)},text:function(e){return Pe(this,function(e){return void 0===e?pe.text(this):this.empty().append((this[0]&&this[0].ownerDocument||re).createTextNode(e))},null,e,arguments.length)},append:function(){return S(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=T(this,e);t.appendChild(e)}})},prepend:function(){return S(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=T(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return S(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return S(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},empty:function(){for(var e,t=0;null!=(e=this[t]);t++){for(1===e.nodeType&&pe.cleanData(h(e,!1));e.firstChild;)e.removeChild(e.firstChild);e.options&&pe.nodeName(e,"select")&&(e.options.length=0)}return this},clone:function(e,t){return e=null!=e&&e,t=null==t?e:t,this.map(function(){return pe.clone(this,e,t)})},html:function(e){return Pe(this,function(e){var t=this[0]||{},n=0,r=this.length;if(void 0===e)return 1===t.nodeType?t.innerHTML.replace(Ze,""):void 0;if("string"==typeof e&&!nt.test(e)&&(fe.htmlSerialize||!et.test(e))&&(fe.leadingWhitespace||!$e.test(e))&&!Xe[(We.exec(e)||["",""])[1].toLowerCase()]){e=pe.htmlPrefilter(e);try{for(;nt",t=l.getElementsByTagName("td"),t[0].style.cssText="margin:0;border:0;padding:0;display:none",o=0===t[0].offsetHeight,o&&(t[0].style.display="",t[1].style.display="none",o=0===t[0].offsetHeight)),f.removeChild(u)}var n,r,i,o,a,s,u=re.createElement("div"),l=re.createElement("div");l.style&&(l.style.cssText="float:left;opacity:.5",fe.opacity="0.5"===l.style.opacity,fe.cssFloat=!!l.style.cssFloat,l.style.backgroundClip="content-box",l.cloneNode(!0).style.backgroundClip="",fe.clearCloneStyle="content-box"===l.style.backgroundClip,u=re.createElement("div"),u.style.cssText="border:0;width:8px;height:0;top:0;left:-9999px;padding:0;margin-top:1px;position:absolute",l.innerHTML="",u.appendChild(l),fe.boxSizing=""===l.style.boxSizing||""===l.style.MozBoxSizing||""===l.style.WebkitBoxSizing,pe.extend(fe,{reliableHiddenOffsets:function(){return null==n&&t(),o},boxSizingReliable:function(){return null==n&&t(),i},pixelMarginRight:function(){return null==n&&t(),r},pixelPosition:function(){return null==n&&t(),n},reliableMarginRight:function(){return null==n&&t(),a},reliableMarginLeft:function(){return null==n&&t(),s}}))}();var ht,gt,mt=/^(top|right|bottom|left)$/;e.getComputedStyle?(ht=function(t){var n=t.ownerDocument.defaultView;return n&&n.opener||(n=e),n.getComputedStyle(t)},gt=function(e,t,n){var r,i,o,a,s=e.style;return n=n||ht(e),a=n?n.getPropertyValue(t)||n[t]:void 0,""!==a&&void 0!==a||pe.contains(e.ownerDocument,e)||(a=pe.style(e,t)),n&&!fe.pixelMarginRight()&&ft.test(a)&&ct.test(t)&&(r=s.width,i=s.minWidth,o=s.maxWidth,s.minWidth=s.maxWidth=s.width=a,a=n.width,s.width=r,s.minWidth=i,s.maxWidth=o),void 0===a?a:a+""}):pt.currentStyle&&(ht=function(e){return e.currentStyle},gt=function(e,t,n){var r,i,o,a,s=e.style;return n=n||ht(e),a=n?n[t]:void 0,null==a&&s&&s[t]&&(a=s[t]),ft.test(a)&&!mt.test(t)&&(r=s.left,i=e.runtimeStyle,o=i&&i.left,o&&(i.left=e.currentStyle.left),s.left="fontSize"===t?"1em":a,a=s.pixelLeft+"px",s.left=r,o&&(i.left=o)),void 0===a?a:a+""||"auto"});var yt=/alpha\([^)]*\)/i,vt=/opacity\s*=\s*([^)]*)/i,xt=/^(none|table(?!-c[ea]).+)/,bt=new RegExp("^("+Fe+")(.*)$","i"),wt={position:"absolute",visibility:"hidden",display:"block"},Tt={letterSpacing:"0",fontWeight:"400"},Ct=["Webkit","O","Moz","ms"],Et=re.createElement("div").style;pe.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=gt(e,"opacity");return""===n?"1":n}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":fe.cssFloat?"cssFloat":"styleFloat"},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var i,o,a,s=pe.camelCase(t),u=e.style;if(t=pe.cssProps[s]||(pe.cssProps[s]=H(s)||s),a=pe.cssHooks[t]||pe.cssHooks[s],void 0===n)return a&&"get"in a&&void 0!==(i=a.get(e,!1,r))?i:u[t];if(o=typeof n,"string"===o&&(i=Me.exec(n))&&i[1]&&(n=d(e,t,i),o="number"),null!=n&&n===n&&("number"===o&&(n+=i&&i[3]||(pe.cssNumber[s]?"":"px")),fe.clearCloneStyle||""!==n||0!==t.indexOf("background")||(u[t]="inherit"),!(a&&"set"in a&&void 0===(n=a.set(e,n,r)))))try{u[t]=n}catch(l){}}},css:function(e,t,n,r){var i,o,a,s=pe.camelCase(t);return t=pe.cssProps[s]||(pe.cssProps[s]=H(s)||s),a=pe.cssHooks[t]||pe.cssHooks[s],a&&"get"in a&&(o=a.get(e,!0,n)),void 0===o&&(o=gt(e,t,r)),"normal"===o&&t in Tt&&(o=Tt[t]),""===n||n?(i=parseFloat(o),n===!0||isFinite(i)?i||0:o):o}}),pe.each(["height","width"],function(e,t){pe.cssHooks[t]={get:function(e,n,r){if(n)return xt.test(pe.css(e,"display"))&&0===e.offsetWidth?dt(e,wt,function(){return M(e,t,r)}):M(e,t,r)},set:function(e,n,r){var i=r&&ht(e);return _(e,n,r?F(e,t,r,fe.boxSizing&&"border-box"===pe.css(e,"boxSizing",!1,i),i):0)}}}),fe.opacity||(pe.cssHooks.opacity={get:function(e,t){return vt.test((t&&e.currentStyle?e.currentStyle.filter:e.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":t?"1":""},set:function(e,t){var n=e.style,r=e.currentStyle,i=pe.isNumeric(t)?"alpha(opacity="+100*t+")":"",o=r&&r.filter||n.filter||"";n.zoom=1,(t>=1||""===t)&&""===pe.trim(o.replace(yt,""))&&n.removeAttribute&&(n.removeAttribute("filter"),""===t||r&&!r.filter)||(n.filter=yt.test(o)?o.replace(yt,i):o+" "+i)}}),pe.cssHooks.marginRight=L(fe.reliableMarginRight,function(e,t){if(t)return dt(e,{display:"inline-block"},gt,[e,"marginRight"])}),pe.cssHooks.marginLeft=L(fe.reliableMarginLeft,function(e,t){if(t)return(parseFloat(gt(e,"marginLeft"))||(pe.contains(e.ownerDocument,e)?e.getBoundingClientRect().left-dt(e,{marginLeft:0},function(){return e.getBoundingClientRect().left}):0))+"px"}),pe.each({margin:"",padding:"",border:"Width"},function(e,t){pe.cssHooks[e+t]={expand:function(n){for(var r=0,i={},o="string"==typeof n?n.split(" "):[n];r<4;r++)i[e+Oe[r]+t]=o[r]||o[r-2]||o[0];return i}},ct.test(e)||(pe.cssHooks[e+t].set=_)}),pe.fn.extend({css:function(e,t){return Pe(this,function(e,t,n){var r,i,o={},a=0;if(pe.isArray(t)){for(r=ht(e),i=t.length;a1)},show:function(){return q(this,!0)},hide:function(){return q(this)},toggle:function(e){return"boolean"==typeof e?e?this.show():this.hide():this.each(function(){Re(this)?pe(this).show():pe(this).hide()})}}),pe.Tween=O,O.prototype={constructor:O,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||pe.easing._default,this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(pe.cssNumber[n]?"":"px")},cur:function(){var e=O.propHooks[this.prop];return e&&e.get?e.get(this):O.propHooks._default.get(this)},run:function(e){var t,n=O.propHooks[this.prop];return this.options.duration?this.pos=t=pe.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):this.pos=t=e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):O.propHooks._default.set(this),this}},O.prototype.init.prototype=O.prototype,O.propHooks={_default:{get:function(e){var t;return 1!==e.elem.nodeType||null!=e.elem[e.prop]&&null==e.elem.style[e.prop]?e.elem[e.prop]:(t=pe.css(e.elem,e.prop,""),t&&"auto"!==t?t:0)},set:function(e){pe.fx.step[e.prop]?pe.fx.step[e.prop](e):1!==e.elem.nodeType||null==e.elem.style[pe.cssProps[e.prop]]&&!pe.cssHooks[e.prop]?e.elem[e.prop]=e.now:pe.style(e.elem,e.prop,e.now+e.unit)}}},O.propHooks.scrollTop=O.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},pe.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2},_default:"swing"},pe.fx=O.prototype.init,pe.fx.step={};var Nt,kt,St=/^(?:toggle|show|hide)$/,At=/queueHooks$/;pe.Animation=pe.extend($,{tweeners:{"*":[function(e,t){var n=this.createTween(e,t);return d(n.elem,e,Me.exec(t),n),n}]},tweener:function(e,t){pe.isFunction(e)?(t=e,e=["*"]):e=e.match(De);for(var n,r=0,i=e.length;r
      a",e=n.getElementsByTagName("a")[0],t.setAttribute("type","checkbox"),n.appendChild(t),e=n.getElementsByTagName("a")[0],e.style.cssText="top:1px",fe.getSetAttribute="t"!==n.className,fe.style=/top/.test(e.getAttribute("style")),fe.hrefNormalized="/a"===e.getAttribute("href"),fe.checkOn=!!t.value,fe.optSelected=i.selected,fe.enctype=!!re.createElement("form").enctype,r.disabled=!0,fe.optDisabled=!i.disabled,t=re.createElement("input"),t.setAttribute("value",""),fe.input=""===t.getAttribute("value"),t.value="t",t.setAttribute("type","radio"),fe.radioValue="t"===t.value}();var Dt=/\r/g,jt=/[\x20\t\r\n\f]+/g;pe.fn.extend({val:function(e){var t,n,r,i=this[0];{if(arguments.length)return r=pe.isFunction(e),this.each(function(n){var i;1===this.nodeType&&(i=r?e.call(this,n,pe(this).val()):e,null==i?i="":"number"==typeof i?i+="":pe.isArray(i)&&(i=pe.map(i,function(e){return null==e?"":e+""})),t=pe.valHooks[this.type]||pe.valHooks[this.nodeName.toLowerCase()],t&&"set"in t&&void 0!==t.set(this,i,"value")||(this.value=i))});if(i)return t=pe.valHooks[i.type]||pe.valHooks[i.nodeName.toLowerCase()],t&&"get"in t&&void 0!==(n=t.get(i,"value"))?n:(n=i.value,"string"==typeof n?n.replace(Dt,""):null==n?"":n)}}}),pe.extend({valHooks:{option:{get:function(e){var t=pe.find.attr(e,"value");return null!=t?t:pe.trim(pe.text(e)).replace(jt," ")}},select:{get:function(e){for(var t,n,r=e.options,i=e.selectedIndex,o="select-one"===e.type||i<0,a=o?null:[],s=o?i+1:r.length,u=i<0?s:o?i:0;u-1)try{r.selected=n=!0}catch(s){r.scrollHeight}else r.selected=!1;return n||(e.selectedIndex=-1),i}}}}),pe.each(["radio","checkbox"],function(){pe.valHooks[this]={set:function(e,t){if(pe.isArray(t))return e.checked=pe.inArray(pe(e).val(),t)>-1}},fe.checkOn||(pe.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})});var Lt,Ht,qt=pe.expr.attrHandle,_t=/^(?:checked|selected)$/i,Ft=fe.getSetAttribute,Mt=fe.input;pe.fn.extend({attr:function(e,t){return Pe(this,pe.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){pe.removeAttr(this,e)})}}),pe.extend({attr:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return"undefined"==typeof e.getAttribute?pe.prop(e,t,n):(1===o&&pe.isXMLDoc(e)||(t=t.toLowerCase(),i=pe.attrHooks[t]||(pe.expr.match.bool.test(t)?Ht:Lt)),void 0!==n?null===n?void pe.removeAttr(e,t):i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:(e.setAttribute(t,n+""),n):i&&"get"in i&&null!==(r=i.get(e,t))?r:(r=pe.find.attr(e,t),null==r?void 0:r))},attrHooks:{type:{set:function(e,t){if(!fe.radioValue&&"radio"===t&&pe.nodeName(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},removeAttr:function(e,t){var n,r,i=0,o=t&&t.match(De);if(o&&1===e.nodeType)for(;n=o[i++];)r=pe.propFix[n]||n,pe.expr.match.bool.test(n)?Mt&&Ft||!_t.test(n)?e[r]=!1:e[pe.camelCase("default-"+n)]=e[r]=!1:pe.attr(e,n,""),e.removeAttribute(Ft?n:r)}}),Ht={set:function(e,t,n){return t===!1?pe.removeAttr(e,n):Mt&&Ft||!_t.test(n)?e.setAttribute(!Ft&&pe.propFix[n]||n,n):e[pe.camelCase("default-"+n)]=e[n]=!0,n}},pe.each(pe.expr.match.bool.source.match(/\w+/g),function(e,t){var n=qt[t]||pe.find.attr;Mt&&Ft||!_t.test(t)?qt[t]=function(e,t,r){var i,o;return r||(o=qt[t],qt[t]=i,i=null!=n(e,t,r)?t.toLowerCase():null,qt[t]=o),i}:qt[t]=function(e,t,n){if(!n)return e[pe.camelCase("default-"+t)]?t.toLowerCase():null}}),Mt&&Ft||(pe.attrHooks.value={set:function(e,t,n){return pe.nodeName(e,"input")?void(e.defaultValue=t):Lt&&Lt.set(e,t,n)}}),Ft||(Lt={set:function(e,t,n){var r=e.getAttributeNode(n);if(r||e.setAttributeNode(r=e.ownerDocument.createAttribute(n)),r.value=t+="","value"===n||t===e.getAttribute(n))return t}},qt.id=qt.name=qt.coords=function(e,t,n){var r;if(!n)return(r=e.getAttributeNode(t))&&""!==r.value?r.value:null},pe.valHooks.button={get:function(e,t){var n=e.getAttributeNode(t);if(n&&n.specified)return n.value},set:Lt.set},pe.attrHooks.contenteditable={set:function(e,t,n){Lt.set(e,""!==t&&t,n)}},pe.each(["width","height"],function(e,t){pe.attrHooks[t]={set:function(e,n){if(""===n)return e.setAttribute(t,"auto"),n}}})),fe.style||(pe.attrHooks.style={get:function(e){return e.style.cssText||void 0},set:function(e,t){return e.style.cssText=t+""}});var Ot=/^(?:input|select|textarea|button|object)$/i,Rt=/^(?:a|area)$/i;pe.fn.extend({prop:function(e,t){return Pe(this,pe.prop,e,t,arguments.length>1)},removeProp:function(e){return e=pe.propFix[e]||e,this.each(function(){try{this[e]=void 0,delete this[e]}catch(t){}})}}),pe.extend({prop:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return 1===o&&pe.isXMLDoc(e)||(t=pe.propFix[t]||t,i=pe.propHooks[t]),void 0!==n?i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:e[t]=n:i&&"get"in i&&null!==(r=i.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){var t=pe.find.attr(e,"tabindex");return t?parseInt(t,10):Ot.test(e.nodeName)||Rt.test(e.nodeName)&&e.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),fe.hrefNormalized||pe.each(["href","src"],function(e,t){pe.propHooks[t]={get:function(e){return e.getAttribute(t,4)}}}),fe.optSelected||(pe.propHooks.selected={get:function(e){var t=e.parentNode;return t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex),null},set:function(e){var t=e.parentNode;t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex)}}),pe.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){pe.propFix[this.toLowerCase()]=this}),fe.enctype||(pe.propFix.enctype="encoding");var Pt=/[\t\r\n\f]/g;pe.fn.extend({addClass:function(e){var t,n,r,i,o,a,s,u=0;if(pe.isFunction(e))return this.each(function(t){pe(this).addClass(e.call(this,t,z(this)))});if("string"==typeof e&&e)for(t=e.match(De)||[];n=this[u++];)if(i=z(n),r=1===n.nodeType&&(" "+i+" ").replace(Pt," ")){for(a=0;o=t[a++];)r.indexOf(" "+o+" ")<0&&(r+=o+" ");s=pe.trim(r),i!==s&&pe.attr(n,"class",s)}return this},removeClass:function(e){var t,n,r,i,o,a,s,u=0;if(pe.isFunction(e))return this.each(function(t){pe(this).removeClass(e.call(this,t,z(this)))});if(!arguments.length)return this.attr("class","");if("string"==typeof e&&e)for(t=e.match(De)||[];n=this[u++];)if(i=z(n),r=1===n.nodeType&&(" "+i+" ").replace(Pt," ")){for(a=0;o=t[a++];)for(;r.indexOf(" "+o+" ")>-1;)r=r.replace(" "+o+" "," ");s=pe.trim(r),i!==s&&pe.attr(n,"class",s)}return this},toggleClass:function(e,t){var n=typeof e;return"boolean"==typeof t&&"string"===n?t?this.addClass(e):this.removeClass(e):pe.isFunction(e)?this.each(function(n){pe(this).toggleClass(e.call(this,n,z(this),t),t)}):this.each(function(){var t,r,i,o;if("string"===n)for(r=0,i=pe(this),o=e.match(De)||[];t=o[r++];)i.hasClass(t)?i.removeClass(t):i.addClass(t);else void 0!==e&&"boolean"!==n||(t=z(this),t&&pe._data(this,"__className__",t),pe.attr(this,"class",t||e===!1?"":pe._data(this,"__className__")||""))})},hasClass:function(e){var t,n,r=0;for(t=" "+e+" ";n=this[r++];)if(1===n.nodeType&&(" "+z(n)+" ").replace(Pt," ").indexOf(t)>-1)return!0;return!1}}),pe.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(e,t){pe.fn[t]=function(e,n){return arguments.length>0?this.on(t,null,e,n):this.trigger(t)}}),pe.fn.extend({hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}});var Bt=e.location,Wt=pe.now(),It=/\?/,$t=/(,)|(\[|{)|(}|])|"(?:[^"\\\r\n]|\\["\\\/bfnrt]|\\u[\da-fA-F]{4})*"\s*:?|true|false|null|-?(?!0\d)\d+(?:\.\d+|)(?:[eE][+-]?\d+|)/g;pe.parseJSON=function(t){if(e.JSON&&e.JSON.parse)return e.JSON.parse(t+"");var n,r=null,i=pe.trim(t+"");return i&&!pe.trim(i.replace($t,function(e,t,i,o){return n&&t&&(r=0),0===r?e:(n=i||t,r+=!o-!i,"")}))?Function("return "+i)():pe.error("Invalid JSON: "+t)},pe.parseXML=function(t){var n,r;if(!t||"string"!=typeof t)return null;try{e.DOMParser?(r=new e.DOMParser,n=r.parseFromString(t,"text/xml")):(n=new e.ActiveXObject("Microsoft.XMLDOM"),n.async="false",n.loadXML(t))}catch(i){n=void 0}return n&&n.documentElement&&!n.getElementsByTagName("parsererror").length||pe.error("Invalid XML: "+t),n};var zt=/#.*$/,Xt=/([?&])_=[^&]*/,Ut=/^(.*?):[ \t]*([^\r\n]*)\r?$/gm,Vt=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Yt=/^(?:GET|HEAD)$/,Jt=/^\/\//,Gt=/^([\w.+-]+:)(?:\/\/(?:[^\/?#]*@|)([^\/?#:]*)(?::(\d+)|)|)/,Kt={},Qt={},Zt="*/".concat("*"),en=Bt.href,tn=Gt.exec(en.toLowerCase())||[];pe.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:en,type:"GET",isLocal:Vt.test(tn[1]),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Zt,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":pe.parseJSON,"text xml":pe.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?V(V(e,pe.ajaxSettings),t):V(pe.ajaxSettings,e)},ajaxPrefilter:X(Kt),ajaxTransport:X(Qt),ajax:function(t,n){function r(t,n,r,i){var o,f,v,x,w,C=n;2!==b&&(b=2,u&&e.clearTimeout(u),c=void 0,s=i||"",T.readyState=t>0?4:0,o=t>=200&&t<300||304===t,r&&(x=Y(d,T,r)),x=J(d,x,T,o),o?(d.ifModified&&(w=T.getResponseHeader("Last-Modified"),w&&(pe.lastModified[a]=w),w=T.getResponseHeader("etag"),w&&(pe.etag[a]=w)),204===t||"HEAD"===d.type?C="nocontent":304===t?C="notmodified":(C=x.state,f=x.data,v=x.error,o=!v)):(v=C,!t&&C||(C="error",t<0&&(t=0))),T.status=t,T.statusText=(n||C)+"",o?g.resolveWith(p,[f,C,T]):g.rejectWith(p,[T,C,v]),T.statusCode(y),y=void 0,l&&h.trigger(o?"ajaxSuccess":"ajaxError",[T,d,o?f:v]),m.fireWith(p,[T,C]),l&&(h.trigger("ajaxComplete",[T,d]),--pe.active||pe.event.trigger("ajaxStop")))}"object"==typeof t&&(n=t,t=void 0),n=n||{};var i,o,a,s,u,l,c,f,d=pe.ajaxSetup({},n),p=d.context||d,h=d.context&&(p.nodeType||p.jquery)?pe(p):pe.event,g=pe.Deferred(),m=pe.Callbacks("once memory"),y=d.statusCode||{},v={},x={},b=0,w="canceled",T={readyState:0,getResponseHeader:function(e){var t;if(2===b){if(!f)for(f={};t=Ut.exec(s);)f[t[1].toLowerCase()]=t[2];t=f[e.toLowerCase()]}return null==t?null:t},getAllResponseHeaders:function(){return 2===b?s:null},setRequestHeader:function(e,t){var n=e.toLowerCase();return b||(e=x[n]=x[n]||e,v[e]=t),this},overrideMimeType:function(e){return b||(d.mimeType=e),this},statusCode:function(e){var t;if(e)if(b<2)for(t in e)y[t]=[y[t],e[t]];else T.always(e[T.status]);return this},abort:function(e){var t=e||w;return c&&c.abort(t),r(0,t),this}};if(g.promise(T).complete=m.add,T.success=T.done,T.error=T.fail,d.url=((t||d.url||en)+"").replace(zt,"").replace(Jt,tn[1]+"//"),d.type=n.method||n.type||d.method||d.type,d.dataTypes=pe.trim(d.dataType||"*").toLowerCase().match(De)||[""],null==d.crossDomain&&(i=Gt.exec(d.url.toLowerCase()),d.crossDomain=!(!i||i[1]===tn[1]&&i[2]===tn[2]&&(i[3]||("http:"===i[1]?"80":"443"))===(tn[3]||("http:"===tn[1]?"80":"443")))),d.data&&d.processData&&"string"!=typeof d.data&&(d.data=pe.param(d.data,d.traditional)),U(Kt,d,n,T),2===b)return T;l=pe.event&&d.global,l&&0===pe.active++&&pe.event.trigger("ajaxStart"),d.type=d.type.toUpperCase(),d.hasContent=!Yt.test(d.type),a=d.url,d.hasContent||(d.data&&(a=d.url+=(It.test(a)?"&":"?")+d.data,delete d.data),d.cache===!1&&(d.url=Xt.test(a)?a.replace(Xt,"$1_="+Wt++):a+(It.test(a)?"&":"?")+"_="+Wt++)),d.ifModified&&(pe.lastModified[a]&&T.setRequestHeader("If-Modified-Since",pe.lastModified[a]),pe.etag[a]&&T.setRequestHeader("If-None-Match",pe.etag[a])),(d.data&&d.hasContent&&d.contentType!==!1||n.contentType)&&T.setRequestHeader("Content-Type",d.contentType),T.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+("*"!==d.dataTypes[0]?", "+Zt+"; q=0.01":""):d.accepts["*"]);for(o in d.headers)T.setRequestHeader(o,d.headers[o]);if(d.beforeSend&&(d.beforeSend.call(p,T,d)===!1||2===b))return T.abort();w="abort";for(o in{success:1,error:1,complete:1})T[o](d[o]);if(c=U(Qt,d,n,T)){if(T.readyState=1,l&&h.trigger("ajaxSend",[T,d]),2===b)return T;d.async&&d.timeout>0&&(u=e.setTimeout(function(){T.abort("timeout")},d.timeout));try{b=1,c.send(v,r)}catch(C){if(!(b<2))throw C;r(-1,C)}}else r(-1,"No Transport");return T},getJSON:function(e,t,n){return pe.get(e,t,n,"json")},getScript:function(e,t){return pe.get(e,void 0,t,"script")}}),pe.each(["get","post"],function(e,t){pe[t]=function(e,n,r,i){return pe.isFunction(n)&&(i=i||r,r=n,n=void 0),pe.ajax(pe.extend({url:e,type:t,dataType:i,data:n,success:r},pe.isPlainObject(e)&&e))}}),pe._evalUrl=function(e){return pe.ajax({url:e,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,"throws":!0})},pe.fn.extend({wrapAll:function(e){if(pe.isFunction(e))return this.each(function(t){pe(this).wrapAll(e.call(this,t))});if(this[0]){var t=pe(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){for(var e=this;e.firstChild&&1===e.firstChild.nodeType;)e=e.firstChild;return e}).append(this)}return this},wrapInner:function(e){return pe.isFunction(e)?this.each(function(t){pe(this).wrapInner(e.call(this,t))}):this.each(function(){var t=pe(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=pe.isFunction(e);return this.each(function(n){pe(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){pe.nodeName(this,"body")||pe(this).replaceWith(this.childNodes)}).end()}}),pe.expr.filters.hidden=function(e){return fe.reliableHiddenOffsets()?e.offsetWidth<=0&&e.offsetHeight<=0&&!e.getClientRects().length:K(e)},pe.expr.filters.visible=function(e){return!pe.expr.filters.hidden(e)};var nn=/%20/g,rn=/\[\]$/,on=/\r?\n/g,an=/^(?:submit|button|image|reset|file)$/i,sn=/^(?:input|select|textarea|keygen)/i;pe.param=function(e,t){var n,r=[],i=function(e,t){t=pe.isFunction(t)?t():null==t?"":t,r[r.length]=encodeURIComponent(e)+"="+encodeURIComponent(t)};if(void 0===t&&(t=pe.ajaxSettings&&pe.ajaxSettings.traditional),pe.isArray(e)||e.jquery&&!pe.isPlainObject(e))pe.each(e,function(){i(this.name,this.value)});else for(n in e)Q(n,e[n],t,i);return r.join("&").replace(nn,"+")},pe.fn.extend({serialize:function(){return pe.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=pe.prop(this,"elements");return e?pe.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!pe(this).is(":disabled")&&sn.test(this.nodeName)&&!an.test(e)&&(this.checked||!Be.test(e))}).map(function(e,t){var n=pe(this).val();return null==n?null:pe.isArray(n)?pe.map(n,function(e){return{name:t.name,value:e.replace(on,"\r\n")}}):{name:t.name,value:n.replace(on,"\r\n")}}).get()}}),pe.ajaxSettings.xhr=void 0!==e.ActiveXObject?function(){return this.isLocal?ee():re.documentMode>8?Z():/^(get|post|head|put|delete|options)$/i.test(this.type)&&Z()||ee()}:Z;var un=0,ln={},cn=pe.ajaxSettings.xhr();e.attachEvent&&e.attachEvent("onunload",function(){for(var e in ln)ln[e](void 0,!0)}),fe.cors=!!cn&&"withCredentials"in cn,cn=fe.ajax=!!cn,cn&&pe.ajaxTransport(function(t){if(!t.crossDomain||fe.cors){var n;return{send:function(r,i){var o,a=t.xhr(),s=++un;if(a.open(t.type,t.url,t.async,t.username,t.password),t.xhrFields)for(o in t.xhrFields)a[o]=t.xhrFields[o];t.mimeType&&a.overrideMimeType&&a.overrideMimeType(t.mimeType),t.crossDomain||r["X-Requested-With"]||(r["X-Requested-With"]="XMLHttpRequest");for(o in r)void 0!==r[o]&&a.setRequestHeader(o,r[o]+"");a.send(t.hasContent&&t.data||null),n=function(e,r){var o,u,l;if(n&&(r||4===a.readyState))if(delete ln[s],n=void 0,a.onreadystatechange=pe.noop,r)4!==a.readyState&&a.abort();else{l={},o=a.status,"string"==typeof a.responseText&&(l.text=a.responseText);try{u=a.statusText}catch(c){u=""}o||!t.isLocal||t.crossDomain?1223===o&&(o=204):o=l.text?200:404}l&&i(o,u,l,a.getAllResponseHeaders())},t.async?4===a.readyState?e.setTimeout(n):a.onreadystatechange=ln[s]=n:n()},abort:function(){n&&n(void 0,!0)}}}}),pe.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(e){return pe.globalEval(e),e}}}),pe.ajaxPrefilter("script",function(e){void 0===e.cache&&(e.cache=!1),e.crossDomain&&(e.type="GET",e.global=!1)}),pe.ajaxTransport("script",function(e){if(e.crossDomain){var t,n=re.head||pe("head")[0]||re.documentElement;return{send:function(r,i){t=re.createElement("script"),t.async=!0,e.scriptCharset&&(t.charset=e.scriptCharset),t.src=e.url,t.onload=t.onreadystatechange=function(e,n){(n||!t.readyState||/loaded|complete/.test(t.readyState))&&(t.onload=t.onreadystatechange=null,t.parentNode&&t.parentNode.removeChild(t),t=null,n||i(200,"success"))},n.insertBefore(t,n.firstChild)},abort:function(){t&&t.onload(void 0,!0)}}}});var fn=[],dn=/(=)\?(?=&|$)|\?\?/;pe.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=fn.pop()||pe.expando+"_"+Wt++;return this[e]=!0,e}}),pe.ajaxPrefilter("json jsonp",function(t,n,r){var i,o,a,s=t.jsonp!==!1&&(dn.test(t.url)?"url":"string"==typeof t.data&&0===(t.contentType||"").indexOf("application/x-www-form-urlencoded")&&dn.test(t.data)&&"data");if(s||"jsonp"===t.dataTypes[0])return i=t.jsonpCallback=pe.isFunction(t.jsonpCallback)?t.jsonpCallback():t.jsonpCallback,s?t[s]=t[s].replace(dn,"$1"+i):t.jsonp!==!1&&(t.url+=(It.test(t.url)?"&":"?")+t.jsonp+"="+i),t.converters["script json"]=function(){return a||pe.error(i+" was not called"),a[0]},t.dataTypes[0]="json",o=e[i],e[i]=function(){a=arguments},r.always(function(){void 0===o?pe(e).removeProp(i):e[i]=o,t[i]&&(t.jsonpCallback=n.jsonpCallback,fn.push(i)),a&&pe.isFunction(o)&&o(a[0]),a=o=void 0}),"script"}),pe.parseHTML=function(e,t,n){if(!e||"string"!=typeof e)return null;"boolean"==typeof t&&(n=t,t=!1),t=t||re;var r=Te.exec(e),i=!n&&[];return r?[t.createElement(r[1])]:(r=y([e],t,i),i&&i.length&&pe(i).remove(),pe.merge([],r.childNodes))};var pn=pe.fn.load;return pe.fn.load=function(e,t,n){if("string"!=typeof e&&pn)return pn.apply(this,arguments);var r,i,o,a=this,s=e.indexOf(" ");return s>-1&&(r=pe.trim(e.slice(s,e.length)),e=e.slice(0,s)),pe.isFunction(t)?(n=t,t=void 0):t&&"object"==typeof t&&(i="POST"),a.length>0&&pe.ajax({url:e,type:i||"GET",dataType:"html",data:t}).done(function(e){o=arguments,a.html(r?pe("
      ").append(pe.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},pe.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){pe.fn[t]=function(e){return this.on(t,e)}}),pe.expr.filters.animated=function(e){return pe.grep(pe.timers,function(t){return e===t.elem}).length},pe.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l,c=pe.css(e,"position"),f=pe(e),d={};"static"===c&&(e.style.position="relative"),s=f.offset(),o=pe.css(e,"top"),u=pe.css(e,"left"),l=("absolute"===c||"fixed"===c)&&pe.inArray("auto",[o,u])>-1,l?(r=f.position(),a=r.top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),pe.isFunction(t)&&(t=t.call(e,n,pe.extend({},s))),null!=t.top&&(d.top=t.top-s.top+a),null!=t.left&&(d.left=t.left-s.left+i),"using"in t?t.using.call(e,d):f.css(d)}},pe.fn.extend({offset:function(e){if(arguments.length)return void 0===e?this:this.each(function(t){pe.offset.setOffset(this,e,t)});var t,n,r={top:0,left:0},i=this[0],o=i&&i.ownerDocument;if(o)return t=o.documentElement,pe.contains(t,i)?("undefined"!=typeof i.getBoundingClientRect&&(r=i.getBoundingClientRect()),n=te(o),{top:r.top+(n.pageYOffset||t.scrollTop)-(t.clientTop||0),left:r.left+(n.pageXOffset||t.scrollLeft)-(t.clientLeft||0)}):r},position:function(){if(this[0]){var e,t,n={top:0,left:0},r=this[0];return"fixed"===pe.css(r,"position")?t=r.getBoundingClientRect():(e=this.offsetParent(),t=this.offset(),pe.nodeName(e[0],"html")||(n=e.offset()),n.top+=pe.css(e[0],"borderTopWidth",!0),n.left+=pe.css(e[0],"borderLeftWidth",!0)),{top:t.top-n.top-pe.css(r,"marginTop",!0),left:t.left-n.left-pe.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){ +for(var e=this.offsetParent;e&&!pe.nodeName(e,"html")&&"static"===pe.css(e,"position");)e=e.offsetParent;return e||pt})}}),pe.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(e,t){var n=/Y/.test(t);pe.fn[e]=function(r){return Pe(this,function(e,r,i){var o=te(e);return void 0===i?o?t in o?o[t]:o.document.documentElement[r]:e[r]:void(o?o.scrollTo(n?pe(o).scrollLeft():i,n?i:pe(o).scrollTop()):e[r]=i)},e,r,arguments.length,null)}}),pe.each(["top","left"],function(e,t){pe.cssHooks[t]=L(fe.pixelPosition,function(e,n){if(n)return n=gt(e,t),ft.test(n)?pe(e).position()[t]+"px":n})}),pe.each({Height:"height",Width:"width"},function(e,t){pe.each({padding:"inner"+e,content:t,"":"outer"+e},function(n,r){pe.fn[r]=function(r,i){var o=arguments.length&&(n||"boolean"!=typeof r),a=n||(r===!0||i===!0?"margin":"border");return Pe(this,function(t,n,r){var i;return pe.isWindow(t)?t.document.documentElement["client"+e]:9===t.nodeType?(i=t.documentElement,Math.max(t.body["scroll"+e],i["scroll"+e],t.body["offset"+e],i["offset"+e],i["client"+e])):void 0===r?pe.css(t,n,a):pe.style(t,n,r,a)},t,o?r:void 0,o,null)}})}),pe.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)}}),pe.fn.size=function(){return this.length},pe.fn.andSelf=pe.fn.addBack,layui.define(function(e){layui.$=pe,e("jquery",pe)}),pe});!function(e,t){"use strict";var i,n,a=e.layui&&layui.define,o={getPath:function(){var e=document.currentScript?document.currentScript.src:function(){for(var e,t=document.scripts,i=t.length-1,n=i;n>0;n--)if("interactive"===t[n].readyState){e=t[n].src;break}return e||t[i].src}();return e.substring(0,e.lastIndexOf("/")+1)}(),config:{},end:{},minIndex:0,minLeft:[],btn:["确定","取消"],type:["dialog","page","iframe","loading","tips"],getStyle:function(t,i){var n=t.currentStyle?t.currentStyle:e.getComputedStyle(t,null);return n[n.getPropertyValue?"getPropertyValue":"getAttribute"](i)},link:function(t,i,n){if(r.path){var a=document.getElementsByTagName("head")[0],s=document.createElement("link");"string"==typeof i&&(n=i);var l=(n||t).replace(/\.|\//g,""),f="layuicss-"+l,c=0;s.rel="stylesheet",s.href=r.path+t,s.id=f,document.getElementById(f)||a.appendChild(s),"function"==typeof i&&!function u(){return++c>80?e.console&&console.error("layer.css: Invalid"):void(1989===parseInt(o.getStyle(document.getElementById(f),"width"))?i():setTimeout(u,100))}()}}},r={v:"3.1.1",ie:function(){var t=navigator.userAgent.toLowerCase();return!!(e.ActiveXObject||"ActiveXObject"in e)&&((t.match(/msie\s(\d+)/)||[])[1]||"11")}(),index:e.layer&&e.layer.v?1e5:0,path:o.getPath,config:function(e,t){return e=e||{},r.cache=o.config=i.extend({},o.config,e),r.path=o.config.path||r.path,"string"==typeof e.extend&&(e.extend=[e.extend]),o.config.path&&r.ready(),e.extend?(a?layui.addcss("modules/layer/"+e.extend):o.link("theme/"+e.extend),this):this},ready:function(e){var t="layer",i="",n=(a?"modules/layer/":"theme/")+"default/layer.css?v="+r.v+i;return a?layui.addcss(n,e,t):o.link(n,e,t),this},alert:function(e,t,n){var a="function"==typeof t;return a&&(n=t),r.open(i.extend({content:e,yes:n},a?{}:t))},confirm:function(e,t,n,a){var s="function"==typeof t;return s&&(a=n,n=t),r.open(i.extend({content:e,btn:o.btn,yes:n,btn2:a},s?{}:t))},msg:function(e,n,a){var s="function"==typeof n,f=o.config.skin,c=(f?f+" "+f+"-msg":"")||"layui-layer-msg",u=l.anim.length-1;return s&&(a=n),r.open(i.extend({content:e,time:3e3,shade:!1,skin:c,title:!1,closeBtn:!1,btn:!1,resize:!1,end:a},s&&!o.config.skin?{skin:c+" layui-layer-hui",anim:u}:function(){return n=n||{},(n.icon===-1||n.icon===t&&!o.config.skin)&&(n.skin=c+" "+(n.skin||"layui-layer-hui")),n}()))},load:function(e,t){return r.open(i.extend({type:3,icon:e||0,resize:!1,shade:.01},t))},tips:function(e,t,n){return r.open(i.extend({type:4,content:[e,t],closeBtn:!1,time:3e3,shade:!1,resize:!1,fixed:!1,maxWidth:210},n))}},s=function(e){var t=this;t.index=++r.index,t.config=i.extend({},t.config,o.config,e),document.body?t.creat():setTimeout(function(){t.creat()},30)};s.pt=s.prototype;var l=["layui-layer",".layui-layer-title",".layui-layer-main",".layui-layer-dialog","layui-layer-iframe","layui-layer-content","layui-layer-btn","layui-layer-close"];l.anim=["layer-anim-00","layer-anim-01","layer-anim-02","layer-anim-03","layer-anim-04","layer-anim-05","layer-anim-06"],s.pt.config={type:0,shade:.3,fixed:!0,move:l[1],title:"信息",offset:"auto",area:"auto",closeBtn:1,time:0,zIndex:19891014,maxWidth:360,anim:0,isOutAnim:!0,icon:-1,moveType:1,resize:!0,scrollbar:!0,tips:2},s.pt.vessel=function(e,t){var n=this,a=n.index,r=n.config,s=r.zIndex+a,f="object"==typeof r.title,c=r.maxmin&&(1===r.type||2===r.type),u=r.title?'
      '+(f?r.title[0]:r.title)+"
      ":"";return r.zIndex=s,t([r.shade?'
      ':"",'
      '+(e&&2!=r.type?"":u)+'
      '+(0==r.type&&r.icon!==-1?'':"")+(1==r.type&&e?"":r.content||"")+'
      '+function(){var e=c?'':"";return r.closeBtn&&(e+=''),e}()+""+(r.btn?function(){var e="";"string"==typeof r.btn&&(r.btn=[r.btn]);for(var t=0,i=r.btn.length;t'+r.btn[t]+"";return'
      '+e+"
      "}():"")+(r.resize?'':"")+"
      "],u,i('
      ')),n},s.pt.creat=function(){var e=this,t=e.config,a=e.index,s=t.content,f="object"==typeof s,c=i("body");if(!t.id||!i("#"+t.id)[0]){switch("string"==typeof t.area&&(t.area="auto"===t.area?["",""]:[t.area,""]),t.shift&&(t.anim=t.shift),6==r.ie&&(t.fixed=!1),t.type){case 0:t.btn="btn"in t?t.btn:o.btn[0],r.closeAll("dialog");break;case 2:var s=t.content=f?t.content:[t.content||"http://layer.layui.com","auto"];t.content='';break;case 3:delete t.title,delete t.closeBtn,t.icon===-1&&0===t.icon,r.closeAll("loading");break;case 4:f||(t.content=[t.content,"body"]),t.follow=t.content[1],t.content=t.content[0]+'',delete t.title,t.tips="object"==typeof t.tips?t.tips:[t.tips,!0],t.tipsMore||r.closeAll("tips")}if(e.vessel(f,function(n,r,u){c.append(n[0]),f?function(){2==t.type||4==t.type?function(){i("body").append(n[1])}():function(){s.parents("."+l[0])[0]||(s.data("display",s.css("display")).show().addClass("layui-layer-wrap").wrap(n[1]),i("#"+l[0]+a).find("."+l[5]).before(r))}()}():c.append(n[1]),i(".layui-layer-move")[0]||c.append(o.moveElem=u),e.layero=i("#"+l[0]+a),t.scrollbar||l.html.css("overflow","hidden").attr("layer-full",a)}).auto(a),i("#layui-layer-shade"+e.index).css({"background-color":t.shade[1]||"#000",opacity:t.shade[0]||t.shade}),2==t.type&&6==r.ie&&e.layero.find("iframe").attr("src",s[0]),4==t.type?e.tips():e.offset(),t.fixed&&n.on("resize",function(){e.offset(),(/^\d+%$/.test(t.area[0])||/^\d+%$/.test(t.area[1]))&&e.auto(a),4==t.type&&e.tips()}),t.time<=0||setTimeout(function(){r.close(e.index)},t.time),e.move().callback(),l.anim[t.anim]){var u="layer-anim "+l.anim[t.anim];e.layero.addClass(u).one("webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend",function(){i(this).removeClass(u)})}t.isOutAnim&&e.layero.data("isOutAnim",!0)}},s.pt.auto=function(e){var t=this,a=t.config,o=i("#"+l[0]+e);""===a.area[0]&&a.maxWidth>0&&(r.ie&&r.ie<8&&a.btn&&o.width(o.innerWidth()),o.outerWidth()>a.maxWidth&&o.width(a.maxWidth));var s=[o.innerWidth(),o.innerHeight()],f=o.find(l[1]).outerHeight()||0,c=o.find("."+l[6]).outerHeight()||0,u=function(e){e=o.find(e),e.height(s[1]-f-c-2*(0|parseFloat(e.css("padding-top"))))};switch(a.type){case 2:u("iframe");break;default:""===a.area[1]?a.maxHeight>0&&o.outerHeight()>a.maxHeight?(s[1]=a.maxHeight,u("."+l[5])):a.fixed&&s[1]>=n.height()&&(s[1]=n.height(),u("."+l[5])):u("."+l[5])}return t},s.pt.offset=function(){var e=this,t=e.config,i=e.layero,a=[i.outerWidth(),i.outerHeight()],o="object"==typeof t.offset;e.offsetTop=(n.height()-a[1])/2,e.offsetLeft=(n.width()-a[0])/2,o?(e.offsetTop=t.offset[0],e.offsetLeft=t.offset[1]||e.offsetLeft):"auto"!==t.offset&&("t"===t.offset?e.offsetTop=0:"r"===t.offset?e.offsetLeft=n.width()-a[0]:"b"===t.offset?e.offsetTop=n.height()-a[1]:"l"===t.offset?e.offsetLeft=0:"lt"===t.offset?(e.offsetTop=0,e.offsetLeft=0):"lb"===t.offset?(e.offsetTop=n.height()-a[1],e.offsetLeft=0):"rt"===t.offset?(e.offsetTop=0,e.offsetLeft=n.width()-a[0]):"rb"===t.offset?(e.offsetTop=n.height()-a[1],e.offsetLeft=n.width()-a[0]):e.offsetTop=t.offset),t.fixed||(e.offsetTop=/%$/.test(e.offsetTop)?n.height()*parseFloat(e.offsetTop)/100:parseFloat(e.offsetTop),e.offsetLeft=/%$/.test(e.offsetLeft)?n.width()*parseFloat(e.offsetLeft)/100:parseFloat(e.offsetLeft),e.offsetTop+=n.scrollTop(),e.offsetLeft+=n.scrollLeft()),i.attr("minLeft")&&(e.offsetTop=n.height()-(i.find(l[1]).outerHeight()||0),e.offsetLeft=i.css("left")),i.css({top:e.offsetTop,left:e.offsetLeft})},s.pt.tips=function(){var e=this,t=e.config,a=e.layero,o=[a.outerWidth(),a.outerHeight()],r=i(t.follow);r[0]||(r=i("body"));var s={width:r.outerWidth(),height:r.outerHeight(),top:r.offset().top,left:r.offset().left},f=a.find(".layui-layer-TipsG"),c=t.tips[0];t.tips[1]||f.remove(),s.autoLeft=function(){s.left+o[0]-n.width()>0?(s.tipLeft=s.left+s.width-o[0],f.css({right:12,left:"auto"})):s.tipLeft=s.left},s.where=[function(){s.autoLeft(),s.tipTop=s.top-o[1]-10,f.removeClass("layui-layer-TipsB").addClass("layui-layer-TipsT").css("border-right-color",t.tips[1])},function(){s.tipLeft=s.left+s.width+10,s.tipTop=s.top,f.removeClass("layui-layer-TipsL").addClass("layui-layer-TipsR").css("border-bottom-color",t.tips[1])},function(){s.autoLeft(),s.tipTop=s.top+s.height+10,f.removeClass("layui-layer-TipsT").addClass("layui-layer-TipsB").css("border-right-color",t.tips[1])},function(){s.tipLeft=s.left-o[0]-10,s.tipTop=s.top,f.removeClass("layui-layer-TipsR").addClass("layui-layer-TipsL").css("border-bottom-color",t.tips[1])}],s.where[c-1](),1===c?s.top-(n.scrollTop()+o[1]+16)<0&&s.where[2]():2===c?n.width()-(s.left+s.width+o[0]+16)>0||s.where[3]():3===c?s.top-n.scrollTop()+s.height+o[1]+16-n.height()>0&&s.where[0]():4===c&&o[0]+16-s.left>0&&s.where[1](),a.find("."+l[5]).css({"background-color":t.tips[1],"padding-right":t.closeBtn?"30px":""}),a.css({left:s.tipLeft-(t.fixed?n.scrollLeft():0),top:s.tipTop-(t.fixed?n.scrollTop():0)})},s.pt.move=function(){var e=this,t=e.config,a=i(document),s=e.layero,l=s.find(t.move),f=s.find(".layui-layer-resize"),c={};return t.move&&l.css("cursor","move"),l.on("mousedown",function(e){e.preventDefault(),t.move&&(c.moveStart=!0,c.offset=[e.clientX-parseFloat(s.css("left")),e.clientY-parseFloat(s.css("top"))],o.moveElem.css("cursor","move").show())}),f.on("mousedown",function(e){e.preventDefault(),c.resizeStart=!0,c.offset=[e.clientX,e.clientY],c.area=[s.outerWidth(),s.outerHeight()],o.moveElem.css("cursor","se-resize").show()}),a.on("mousemove",function(i){if(c.moveStart){var a=i.clientX-c.offset[0],o=i.clientY-c.offset[1],l="fixed"===s.css("position");if(i.preventDefault(),c.stX=l?0:n.scrollLeft(),c.stY=l?0:n.scrollTop(),!t.moveOut){var f=n.width()-s.outerWidth()+c.stX,u=n.height()-s.outerHeight()+c.stY;af&&(a=f),ou&&(o=u)}s.css({left:a,top:o})}if(t.resize&&c.resizeStart){var a=i.clientX-c.offset[0],o=i.clientY-c.offset[1];i.preventDefault(),r.style(e.index,{width:c.area[0]+a,height:c.area[1]+o}),c.isResize=!0,t.resizing&&t.resizing(s)}}).on("mouseup",function(e){c.moveStart&&(delete c.moveStart,o.moveElem.hide(),t.moveEnd&&t.moveEnd(s)),c.resizeStart&&(delete c.resizeStart,o.moveElem.hide())}),e},s.pt.callback=function(){function e(){var e=a.cancel&&a.cancel(t.index,n);e===!1||r.close(t.index)}var t=this,n=t.layero,a=t.config;t.openLayer(),a.success&&(2==a.type?n.find("iframe").on("load",function(){a.success(n,t.index)}):a.success(n,t.index)),6==r.ie&&t.IE6(n),n.find("."+l[6]).children("a").on("click",function(){var e=i(this).index();if(0===e)a.yes?a.yes(t.index,n):a.btn1?a.btn1(t.index,n):r.close(t.index);else{var o=a["btn"+(e+1)]&&a["btn"+(e+1)](t.index,n);o===!1||r.close(t.index)}}),n.find("."+l[7]).on("click",e),a.shadeClose&&i("#layui-layer-shade"+t.index).on("click",function(){r.close(t.index)}),n.find(".layui-layer-min").on("click",function(){var e=a.min&&a.min(n);e===!1||r.min(t.index,a)}),n.find(".layui-layer-max").on("click",function(){i(this).hasClass("layui-layer-maxmin")?(r.restore(t.index),a.restore&&a.restore(n)):(r.full(t.index,a),setTimeout(function(){a.full&&a.full(n)},100))}),a.end&&(o.end[t.index]=a.end)},o.reselect=function(){i.each(i("select"),function(e,t){var n=i(this);n.parents("."+l[0])[0]||1==n.attr("layer")&&i("."+l[0]).length<1&&n.removeAttr("layer").show(),n=null})},s.pt.IE6=function(e){i("select").each(function(e,t){var n=i(this);n.parents("."+l[0])[0]||"none"===n.css("display")||n.attr({layer:"1"}).hide(),n=null})},s.pt.openLayer=function(){var e=this;r.zIndex=e.config.zIndex,r.setTop=function(e){var t=function(){r.zIndex++,e.css("z-index",r.zIndex+1)};return r.zIndex=parseInt(e[0].style.zIndex),e.on("mousedown",t),r.zIndex}},o.record=function(e){var t=[e.width(),e.height(),e.position().top,e.position().left+parseFloat(e.css("margin-left"))];e.find(".layui-layer-max").addClass("layui-layer-maxmin"),e.attr({area:t})},o.rescollbar=function(e){l.html.attr("layer-full")==e&&(l.html[0].style.removeProperty?l.html[0].style.removeProperty("overflow"):l.html[0].style.removeAttribute("overflow"),l.html.removeAttr("layer-full"))},e.layer=r,r.getChildFrame=function(e,t){return t=t||i("."+l[4]).attr("times"),i("#"+l[0]+t).find("iframe").contents().find(e)},r.getFrameIndex=function(e){return i("#"+e).parents("."+l[4]).attr("times")},r.iframeAuto=function(e){if(e){var t=r.getChildFrame("html",e).outerHeight(),n=i("#"+l[0]+e),a=n.find(l[1]).outerHeight()||0,o=n.find("."+l[6]).outerHeight()||0;n.css({height:t+a+o}),n.find("iframe").css({height:t})}},r.iframeSrc=function(e,t){i("#"+l[0]+e).find("iframe").attr("src",t)},r.style=function(e,t,n){var a=i("#"+l[0]+e),r=a.find(".layui-layer-content"),s=a.attr("type"),f=a.find(l[1]).outerHeight()||0,c=a.find("."+l[6]).outerHeight()||0;a.attr("minLeft");s!==o.type[3]&&s!==o.type[4]&&(n||(parseFloat(t.width)<=260&&(t.width=260),parseFloat(t.height)-f-c<=64&&(t.height=64+f+c)),a.css(t),c=a.find("."+l[6]).outerHeight(),s===o.type[2]?a.find("iframe").css({height:parseFloat(t.height)-f-c}):r.css({height:parseFloat(t.height)-f-c-parseFloat(r.css("padding-top"))-parseFloat(r.css("padding-bottom"))}))},r.min=function(e,t){var a=i("#"+l[0]+e),s=a.find(l[1]).outerHeight()||0,f=a.attr("minLeft")||181*o.minIndex+"px",c=a.css("position");o.record(a),o.minLeft[0]&&(f=o.minLeft[0],o.minLeft.shift()),a.attr("position",c),r.style(e,{width:180,height:s,left:f,top:n.height()-s,position:"fixed",overflow:"hidden"},!0),a.find(".layui-layer-min").hide(),"page"===a.attr("type")&&a.find(l[4]).hide(),o.rescollbar(e),a.attr("minLeft")||o.minIndex++,a.attr("minLeft",f)},r.restore=function(e){var t=i("#"+l[0]+e),n=t.attr("area").split(",");t.attr("type");r.style(e,{width:parseFloat(n[0]),height:parseFloat(n[1]),top:parseFloat(n[2]),left:parseFloat(n[3]),position:t.attr("position"),overflow:"visible"},!0),t.find(".layui-layer-max").removeClass("layui-layer-maxmin"),t.find(".layui-layer-min").show(),"page"===t.attr("type")&&t.find(l[4]).show(),o.rescollbar(e)},r.full=function(e){var t,a=i("#"+l[0]+e);o.record(a),l.html.attr("layer-full")||l.html.css("overflow","hidden").attr("layer-full",e),clearTimeout(t),t=setTimeout(function(){var t="fixed"===a.css("position");r.style(e,{top:t?0:n.scrollTop(),left:t?0:n.scrollLeft(),width:n.width(),height:n.height()},!0),a.find(".layui-layer-min").hide()},100)},r.title=function(e,t){var n=i("#"+l[0]+(t||r.index)).find(l[1]);n.html(e)},r.close=function(e){var t=i("#"+l[0]+e),n=t.attr("type"),a="layer-anim-close";if(t[0]){var s="layui-layer-wrap",f=function(){if(n===o.type[1]&&"object"===t.attr("conType")){t.children(":not(."+l[5]+")").remove();for(var a=t.find("."+s),r=0;r<2;r++)a.unwrap();a.css("display",a.data("display")).removeClass(s)}else{if(n===o.type[2])try{var f=i("#"+l[4]+e)[0];f.contentWindow.document.write(""),f.contentWindow.close(),t.find("."+l[5])[0].removeChild(f)}catch(c){}t[0].innerHTML="",t.remove()}"function"==typeof o.end[e]&&o.end[e](),delete o.end[e]};t.data("isOutAnim")&&t.addClass("layer-anim "+a),i("#layui-layer-moves, #layui-layer-shade"+e).remove(),6==r.ie&&o.reselect(),o.rescollbar(e),t.attr("minLeft")&&(o.minIndex--,o.minLeft.push(t.attr("minLeft"))),r.ie&&r.ie<10||!t.data("isOutAnim")?f():setTimeout(function(){f()},200)}},r.closeAll=function(e){i.each(i("."+l[0]),function(){var t=i(this),n=e?t.attr("type")===e:1;n&&r.close(t.attr("times")),n=null})};var f=r.cache||{},c=function(e){return f.skin?" "+f.skin+" "+f.skin+"-"+e:""};r.prompt=function(e,t){var a="";if(e=e||{},"function"==typeof e&&(t=e),e.area){var o=e.area;a='style="width: '+o[0]+"; height: "+o[1]+';"',delete e.area}var s,l=2==e.formType?'":function(){return''}(),f=e.success;return delete e.success,r.open(i.extend({type:1,btn:["确定","取消"],content:l,skin:"layui-layer-prompt"+c("prompt"),maxWidth:n.width(),success:function(t){s=t.find(".layui-layer-input"),s.val(e.value||"").focus(),"function"==typeof f&&f(t)},resize:!1,yes:function(i){var n=s.val();""===n?s.focus():n.length>(e.maxlength||500)?r.tips("最多输入"+(e.maxlength||500)+"个字数",s,{tips:1}):t&&t(n,i,s)}},e))},r.tab=function(e){e=e||{};var t=e.tab||{},n="layui-this",a=e.success;return delete e.success,r.open(i.extend({type:1,skin:"layui-layer-tab"+c("tab"),resize:!1,title:function(){var e=t.length,i=1,a="";if(e>0)for(a=''+t[0].title+"";i"+t[i].title+"";return a}(),content:'
        '+function(){var e=t.length,i=1,a="";if(e>0)for(a='
      • '+(t[0].content||"no content")+"
      • ";i'+(t[i].content||"no content")+"";return a}()+"
      ",success:function(t){var o=t.find(".layui-layer-title").children(),r=t.find(".layui-layer-tabmain").children();o.on("mousedown",function(t){t.stopPropagation?t.stopPropagation():t.cancelBubble=!0;var a=i(this),o=a.index();a.addClass(n).siblings().removeClass(n),r.eq(o).show().siblings().hide(),"function"==typeof e.change&&e.change(o)}),"function"==typeof a&&a(t)}},e))},r.photos=function(t,n,a){function o(e,t,i){var n=new Image;return n.src=e,n.complete?t(n):(n.onload=function(){n.onload=null,t(n)},void(n.onerror=function(e){n.onerror=null,i(e)}))}var s={};if(t=t||{},t.photos){var l=t.photos.constructor===Object,f=l?t.photos:{},u=f.data||[],d=f.start||0;s.imgIndex=(0|d)+1,t.img=t.img||"img";var y=t.success;if(delete t.success,l){if(0===u.length)return r.msg("没有图片")}else{var p=i(t.photos),h=function(){u=[],p.find(t.img).each(function(e){var t=i(this);t.attr("layer-index",e),u.push({alt:t.attr("alt"),pid:t.attr("layer-pid"),src:t.attr("layer-src")||t.attr("src"),thumb:t.attr("src")})})};if(h(),0===u.length)return;if(n||p.on("click",t.img,function(){var e=i(this),n=e.attr("layer-index");r.photos(i.extend(t,{photos:{start:n,data:u,tab:t.tab},full:t.full}),!0),h()}),!n)return}s.imgprev=function(e){s.imgIndex--,s.imgIndex<1&&(s.imgIndex=u.length),s.tabimg(e)},s.imgnext=function(e,t){s.imgIndex++,s.imgIndex>u.length&&(s.imgIndex=1,t)||s.tabimg(e)},s.keyup=function(e){if(!s.end){var t=e.keyCode;e.preventDefault(),37===t?s.imgprev(!0):39===t?s.imgnext(!0):27===t&&r.close(s.index)}},s.tabimg=function(e){if(!(u.length<=1))return f.start=s.imgIndex-1,r.close(s.index),r.photos(t,!0,e)},s.event=function(){s.bigimg.hover(function(){s.imgsee.show()},function(){s.imgsee.hide()}),s.bigimg.find(".layui-layer-imgprev").on("click",function(e){e.preventDefault(),s.imgprev()}),s.bigimg.find(".layui-layer-imgnext").on("click",function(e){e.preventDefault(),s.imgnext()}),i(document).on("keyup",s.keyup)},s.loadi=r.load(1,{shade:!("shade"in t)&&.9,scrollbar:!1}),o(u[d].src,function(n){r.close(s.loadi),s.index=r.open(i.extend({type:1,id:"layui-layer-photos",area:function(){var a=[n.width,n.height],o=[i(e).width()-100,i(e).height()-100];if(!t.full&&(a[0]>o[0]||a[1]>o[1])){var r=[a[0]/o[0],a[1]/o[1]];r[0]>r[1]?(a[0]=a[0]/r[0],a[1]=a[1]/r[0]):r[0]'+(u[d].alt||
      '+(u.length>1?'':"")+'
      '+(u[d].alt||"")+""+s.imgIndex+"/"+u.length+"
      ",success:function(e,i){s.bigimg=e.find(".layui-layer-phimg"),s.imgsee=e.find(".layui-layer-imguide,.layui-layer-imgbar"),s.event(e),t.tab&&t.tab(u[d],e),"function"==typeof y&&y(e)},end:function(){s.end=!0,i(document).off("keyup",s.keyup)}},t))},function(){r.close(s.loadi),r.msg("当前图片地址异常
      是否继续查看下一张?",{time:3e4,btn:["下一张","不看了"],yes:function(){u.length>1&&s.imgnext(!0,!0)}})})}},o.run=function(t){i=t,n=i(e),l.html=i("html"),r.open=function(e){var t=new s(e);return t.index}},e.layui&&layui.define?(r.ready(),layui.define("jquery",function(t){r.path=layui.cache.dir,o.run(layui.$),e.layer=r,t("layer",r)})):"function"==typeof define&&define.amd?define(["jquery"],function(){return o.run(e.jQuery),r}):function(){o.run(e.jQuery),r.ready()}()}(window);layui.define("jquery",function(t){"use strict";var a=layui.$,i=(layui.hint(),layui.device()),e="element",l="layui-this",n="layui-show",s=function(){this.config={}};s.prototype.set=function(t){var i=this;return a.extend(!0,i.config,t),i},s.prototype.on=function(t,a){return layui.onevent.call(this,e,t,a)},s.prototype.tabAdd=function(t,i){var e=".layui-tab-title",l=a(".layui-tab[lay-filter="+t+"]"),n=l.children(e),s=n.children(".layui-tab-bar"),o=l.children(".layui-tab-content"),r='
    • "+(i.title||"unnaming")+"
    • ";return s[0]?s.before(r):n.append(r),o.append('
      '+(i.content||"")+"
      "),f.hideTabMore(!0),f.tabAuto(),this},s.prototype.tabDelete=function(t,i){var e=".layui-tab-title",l=a(".layui-tab[lay-filter="+t+"]"),n=l.children(e),s=n.find('>li[lay-id="'+i+'"]');return f.tabDelete(null,s),this},s.prototype.tabChange=function(t,i){var e=".layui-tab-title",l=a(".layui-tab[lay-filter="+t+"]"),n=l.children(e),s=n.find('>li[lay-id="'+i+'"]');return f.tabClick.call(s[0],null,null,s),this},s.prototype.tab=function(t){t=t||{},b.on("click",t.headerElem,function(i){var e=a(this).index();f.tabClick.call(this,i,e,null,t)})},s.prototype.progress=function(t,i){var e="layui-progress",l=a("."+e+"[lay-filter="+t+"]"),n=l.find("."+e+"-bar"),s=n.find("."+e+"-text");return n.css("width",i),s.text(i),this};var o=".layui-nav",r="layui-nav-item",c="layui-nav-bar",u="layui-nav-tree",d="layui-nav-child",y="layui-nav-more",h="layui-anim layui-anim-upbit",f={tabClick:function(t,i,s,o){o=o||{};var r=s||a(this),i=i||r.parent().children("li").index(r),c=o.headerElem?r.parent():r.parents(".layui-tab").eq(0),u=o.bodyElem?a(o.bodyElem):c.children(".layui-tab-content").children(".layui-tab-item"),d=r.find("a"),y=c.attr("lay-filter");"javascript:;"!==d.attr("href")&&"_blank"===d.attr("target")||(r.addClass(l).siblings().removeClass(l),u.eq(i).addClass(n).siblings().removeClass(n)),layui.event.call(this,e,"tab("+y+")",{elem:c,index:i})},tabDelete:function(t,i){var n=i||a(this).parent(),s=n.index(),o=n.parents(".layui-tab").eq(0),r=o.children(".layui-tab-content").children(".layui-tab-item"),c=o.attr("lay-filter");n.hasClass(l)&&(n.next()[0]?f.tabClick.call(n.next()[0],null,s+1):n.prev()[0]&&f.tabClick.call(n.prev()[0],null,s-1)),n.remove(),r.eq(s).remove(),setTimeout(function(){f.tabAuto()},50),layui.event.call(this,e,"tabDelete("+c+")",{elem:o,index:s})},tabAuto:function(){var t="layui-tab-more",e="layui-tab-bar",l="layui-tab-close",n=this;a(".layui-tab").each(function(){var s=a(this),o=s.children(".layui-tab-title"),r=(s.children(".layui-tab-content").children(".layui-tab-item"),'lay-stope="tabmore"'),c=a('');if(n===window&&8!=i.ie&&f.hideTabMore(!0),s.attr("lay-allowClose")&&o.find("li").each(function(){var t=a(this);if(!t.find("."+l)[0]){var i=a('');i.on("click",f.tabDelete),t.append(i)}}),"string"!=typeof s.attr("lay-unauto"))if(o.prop("scrollWidth")>o.outerWidth()+1){if(o.find("."+e)[0])return;o.append(c),s.attr("overflow",""),c.on("click",function(a){o[this.title?"removeClass":"addClass"](t),this.title=this.title?"":"收缩"})}else o.find("."+e).remove(),s.removeAttr("overflow")})},hideTabMore:function(t){var i=a(".layui-tab-title");t!==!0&&"tabmore"===a(t.target).attr("lay-stope")||(i.removeClass("layui-tab-more"),i.find(".layui-tab-bar").attr("title",""))},clickThis:function(){var t=a(this),i=t.parents(o),n=i.attr("lay-filter"),s=t.parent(),c=t.siblings("."+d),y="string"==typeof s.attr("lay-unselect");"javascript:;"!==t.attr("href")&&"_blank"===t.attr("target")||y||c[0]||(i.find("."+l).removeClass(l),s.addClass(l)),i.hasClass(u)&&(c.removeClass(h),c[0]&&(s["none"===c.css("display")?"addClass":"removeClass"](r+"ed"),"all"===i.attr("lay-shrink")&&s.siblings().removeClass(r+"ed"))),layui.event.call(this,e,"nav("+n+")",t)},collapse:function(){var t=a(this),i=t.find(".layui-colla-icon"),l=t.siblings(".layui-colla-content"),s=t.parents(".layui-collapse").eq(0),o=s.attr("lay-filter"),r="none"===l.css("display");if("string"==typeof s.attr("lay-accordion")){var c=s.children(".layui-colla-item").children("."+n);c.siblings(".layui-colla-title").children(".layui-colla-icon").html(""),c.removeClass(n)}l[r?"addClass":"removeClass"](n),i.html(r?"":""),layui.event.call(this,e,"collapse("+o+")",{title:t,content:l,show:r})}};s.prototype.init=function(t,e){var l=function(){return e?'[lay-filter="'+e+'"]':""}(),s={tab:function(){f.tabAuto.call({})},nav:function(){var t=200,e={},s={},p={},b=function(l,o,r){var c=a(this),f=c.find("."+d);o.hasClass(u)?l.css({top:c.position().top,height:c.children("a").outerHeight(),opacity:1}):(f.addClass(h),l.css({left:c.position().left+parseFloat(c.css("marginLeft")),top:c.position().top+c.height()-l.height()}),e[r]=setTimeout(function(){l.css({width:c.width(),opacity:1})},i.ie&&i.ie<10?0:t),clearTimeout(p[r]),"block"===f.css("display")&&clearTimeout(s[r]),s[r]=setTimeout(function(){f.addClass(n),c.find("."+y).addClass(y+"d")},300))};a(o+l).each(function(i){var l=a(this),o=a(''),h=l.find("."+r);l.find("."+c)[0]||(l.append(o),h.on("mouseenter",function(){b.call(this,o,l,i)}).on("mouseleave",function(){l.hasClass(u)||(clearTimeout(s[i]),s[i]=setTimeout(function(){l.find("."+d).removeClass(n),l.find("."+y).removeClass(y+"d")},300))}),l.on("mouseleave",function(){clearTimeout(e[i]),p[i]=setTimeout(function(){l.hasClass(u)?o.css({height:0,top:o.position().top+o.height()/2,opacity:0}):o.css({width:0,left:o.position().left+o.width()/2,opacity:0})},t)})),h.find("a").each(function(){var t=a(this),i=(t.parent(),t.siblings("."+d));i[0]&&!t.children("."+y)[0]&&t.append(''),t.off("click",f.clickThis).on("click",f.clickThis)})})},breadcrumb:function(){var t=".layui-breadcrumb";a(t+l).each(function(){var t=a(this),i="lay-separator",e=t.attr(i)||"/",l=t.find("a");l.next("span["+i+"]")[0]||(l.each(function(t){t!==l.length-1&&a(this).after(""+e+"")}),t.css("visibility","visible"))})},progress:function(){var t="layui-progress";a("."+t+l).each(function(){var i=a(this),e=i.find(".layui-progress-bar"),l=e.attr("lay-percent");e.css("width",function(){return/^.+\/.+$/.test(l)?100*new Function("return "+l)()+"%":l}()),i.attr("lay-showPercent")&&setTimeout(function(){e.html(''+l+"")},350)})},collapse:function(){var t="layui-collapse";a("."+t+l).each(function(){var t=a(this).find(".layui-colla-item");t.each(function(){var t=a(this),i=t.find(".layui-colla-title"),e=t.find(".layui-colla-content"),l="none"===e.css("display");i.find(".layui-colla-icon").remove(),i.append(''+(l?"":"")+""),i.off("click",f.collapse).on("click",f.collapse)})})}};return s[t]?s[t]():layui.each(s,function(t,a){a()})},s.prototype.render=s.prototype.init;var p=new s,b=a(document);p.render();var v=".layui-tab-title li";b.on("click",v,f.tabClick),b.on("click",f.hideTabMore),a(window).on("resize",f.tabAuto),t(e,p)});layui.define("layer",function(e){"use strict";var t=layui.$,i=layui.layer,n=layui.hint(),a=layui.device(),o={config:{},set:function(e){var i=this;return i.config=t.extend({},i.config,e),i},on:function(e,t){return layui.onevent.call(this,r,e,t)}},l=function(){var e=this;return{upload:function(t){e.upload.call(e,t)},config:e.config}},r="upload",u="layui-upload-file",c="layui-upload-form",f="layui-upload-iframe",s="layui-upload-choose",p=function(e){var i=this;i.config=t.extend({},i.config,o.config,e),i.render()};p.prototype.config={accept:"images",exts:"",auto:!0,bindAction:"",url:"",field:"file",method:"post",data:{},drag:!0,size:0,number:0,multiple:!1},p.prototype.render=function(e){var i=this,e=i.config;e.elem=t(e.elem),e.bindAction=t(e.bindAction),i.file(),i.events()},p.prototype.file=function(){var e=this,i=e.config,n=e.elemFile=t(['"].join("")),o=i.elem.next();(o.hasClass(u)||o.hasClass(c))&&o.remove(),a.ie&&a.ie<10&&i.elem.wrap('
      '),e.isFile()?(e.elemFile=i.elem,i.field=i.elem[0].name):i.elem.after(n),a.ie&&a.ie<10&&e.initIE()},p.prototype.initIE=function(){var e=this,i=e.config,n=t(''),a=t(['
      ',"
      "].join(""));t("#"+f)[0]||t("body").append(n),i.elem.next().hasClass(f)||(e.elemFile.wrap(a),i.elem.next("."+f).append(function(){var e=[];return layui.each(i.data,function(t,i){i="function"==typeof i?i():i,e.push('')}),e.join("")}()))},p.prototype.msg=function(e){return i.msg(e,{icon:2,shift:6})},p.prototype.isFile=function(){var e=this.config.elem[0];if(e)return"input"===e.tagName.toLocaleLowerCase()&&"file"===e.type},p.prototype.preview=function(e){var t=this;window.FileReader&&layui.each(t.chooseFiles,function(t,i){var n=new FileReader;n.readAsDataURL(i),n.onload=function(){e&&e(t,i,this.result)}})},p.prototype.upload=function(e,i){var n,o=this,l=o.config,r=o.elemFile[0],u=function(){var i=0,n=0,a=e||o.files||o.chooseFiles||r.files,u=function(){l.multiple&&i+n===o.fileLength&&"function"==typeof l.allDone&&l.allDone({total:o.fileLength,successful:i,aborted:n})};layui.each(a,function(e,a){var r=new FormData;r.append(l.field,a),layui.each(l.data,function(e,t){t="function"==typeof t?t():t,r.append(e,t)}),t.ajax({url:l.url,type:l.method,data:r,contentType:!1,processData:!1,dataType:"json",headers:l.headers||{},success:function(t){i++,d(e,t),u()},error:function(){n++,o.msg("请求上传接口出现异常"),m(e),u()}})})},c=function(){var e=t("#"+f);o.elemFile.parent().submit(),clearInterval(p.timer),p.timer=setInterval(function(){var t,i=e.contents().find("body");try{t=i.text()}catch(n){o.msg("获取上传后的响应信息出现异常"),clearInterval(p.timer),m()}t&&(clearInterval(p.timer),i.html(""),d(0,t))},30)},d=function(e,t){if(o.elemFile.next("."+s).remove(),r.value="","object"!=typeof t)try{t=JSON.parse(t)}catch(i){return t={},o.msg("请对上传接口返回有效JSON")}"function"==typeof l.done&&l.done(t,e||0,function(e){o.upload(e)})},m=function(e){l.auto&&(r.value=""),"function"==typeof l.error&&l.error(e||0,function(e){o.upload(e)})},h=l.exts,v=function(){var t=[];return layui.each(e||o.chooseFiles,function(e,i){t.push(i.name)}),t}(),g={preview:function(e){o.preview(e)},upload:function(e,t){var i={};i[e]=t,o.upload(i)},pushFile:function(){return o.files=o.files||{},layui.each(o.chooseFiles,function(e,t){o.files[e]=t}),o.files}},y=function(){return"choose"===i?l.choose&&l.choose(g):(l.before&&l.before(g),a.ie?a.ie>9?u():c():void u())};if(v=0===v.length?r.value.match(/[^\/\\]+\..+/g)||[]||"":v,0!==v.length){switch(l.accept){case"file":if(h&&!RegExp("\\w\\.("+h+")$","i").test(escape(v)))return o.msg("选择的文件中包含不支持的格式"),r.value="";break;case"video":if(!RegExp("\\w\\.("+(h||"avi|mp4|wma|rmvb|rm|flash|3gp|flv")+")$","i").test(escape(v)))return o.msg("选择的视频中包含不支持的格式"),r.value="";break;case"audio":if(!RegExp("\\w\\.("+(h||"mp3|wav|mid")+")$","i").test(escape(v)))return o.msg("选择的音频中包含不支持的格式"),r.value="";break;default:if(layui.each(v,function(e,t){RegExp("\\w\\.("+(h||"jpg|png|gif|bmp|jpeg$")+")","i").test(escape(t))||(n=!0)}),n)return o.msg("选择的图片中包含不支持的格式"),r.value=""}if(o.fileLength=function(){var t=0,i=e||o.files||o.chooseFiles||r.files;return layui.each(i,function(){t++}),t}(),l.number&&o.fileLength>l.number)return o.msg("同时最多只能上传的数量为:"+l.number);if(l.size>0&&!(a.ie&&a.ie<10)){var F;if(layui.each(o.chooseFiles,function(e,t){if(t.size>1024*l.size){var i=l.size/1024;i=i>=1?Math.floor(i)+(i%1>0?i.toFixed(1):0)+"MB":l.size+"KB",r.value="",F=i}}),F)return o.msg("文件不能超过"+F)}y()}},p.prototype.events=function(){var e=this,i=e.config,o=function(t){e.chooseFiles={},layui.each(t,function(t,i){var n=(new Date).getTime();e.chooseFiles[n+"-"+t]=i})},l=function(t,n){var a=e.elemFile,o=t.length>1?t.length+"个文件":(t[0]||{}).name||a[0].value.match(/[^\/\\]+\..+/g)||[]||"";a.next().hasClass(s)&&a.next().remove(),e.upload(null,"choose"),e.isFile()||i.choose||a.after(''+o+"")};i.elem.off("upload.start").on("upload.start",function(){var a=t(this),o=a.attr("lay-data");if(o)try{o=new Function("return "+o)(),e.config=t.extend({},i,o)}catch(l){n.error("Upload element property lay-data configuration item has a syntax error: "+o)}e.config.item=a,e.elemFile[0].click()}),a.ie&&a.ie<10||i.elem.off("upload.over").on("upload.over",function(){var e=t(this);e.attr("lay-over","")}).off("upload.leave").on("upload.leave",function(){var e=t(this);e.removeAttr("lay-over")}).off("upload.drop").on("upload.drop",function(n,a){var r=t(this),u=a.originalEvent.dataTransfer.files||[];r.removeAttr("lay-over"),o(u),i.auto?e.upload(u):l(u)}),e.elemFile.off("upload.change").on("upload.change",function(){var t=this.files||[];o(t),i.auto?e.upload():l(t)}),i.bindAction.off("upload.action").on("upload.action",function(){e.upload()}),i.elem.data("haveEvents")||(e.elemFile.on("change",function(){t(this).trigger("upload.change")}),i.elem.on("click",function(){e.isFile()||t(this).trigger("upload.start")}),i.drag&&i.elem.on("dragover",function(e){e.preventDefault(),t(this).trigger("upload.over")}).on("dragleave",function(e){t(this).trigger("upload.leave")}).on("drop",function(e){e.preventDefault(),t(this).trigger("upload.drop",e)}),i.bindAction.on("click",function(){t(this).trigger("upload.action")}),i.elem.data("haveEvents",!0))},o.render=function(e){var t=new p(e);return l.call(t)},e(r,o)});layui.define("layer",function(e){"use strict";var t=layui.$,i=layui.layer,a=layui.hint(),n=layui.device(),l="form",r=".layui-form",s="layui-this",o="layui-hide",u="layui-disabled",c=function(){this.config={verify:{required:[/[\S]+/,"必填项不能为空"],phone:[/^1\d{10}$/,"请输入正确的手机号"],email:[/^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/,"邮箱格式不正确"],url:[/(^#)|(^http(s*):\/\/[^\s]+\.[^\s]+)/,"链接格式不正确"],number:function(e){if(!e||isNaN(e))return"只能填写数字"},date:[/^(\d{4})[-\/](\d{1}|0\d{1}|1[0-2])([-\/](\d{1}|0\d{1}|[1-2][0-9]|3[0-1]))*$/,"日期格式不正确"],identity:[/(^\d{15}$)|(^\d{17}(x|X|\d)$)/,"请输入正确的身份证号"]}}};c.prototype.set=function(e){var i=this;return t.extend(!0,i.config,e),i},c.prototype.verify=function(e){var i=this;return t.extend(!0,i.config.verify,e),i},c.prototype.on=function(e,t){return layui.onevent.call(this,l,e,t)},c.prototype.render=function(e,i){var n=this,c=t(r+function(){return i?'[lay-filter="'+i+'"]':""}()),d={select:function(){var e,i="请选择",a="layui-form-select",n="layui-select-title",r="layui-select-none",d="",f=c.find("select"),y=function(i,l){t(i.target).parent().hasClass(n)&&!l||(t("."+a).removeClass(a+"ed "+a+"up"),e&&d&&e.val(d)),e=null},h=function(i,c,f){var h=t(this),p=i.find("."+n),m=p.find("input"),k=i.find("dl"),g=k.children("dd");if(!c){var x=function(){var e=i.offset().top+i.outerHeight()+5-v.scrollTop(),t=k.outerHeight();i.addClass(a+"ed"),g.removeClass(o),e+t>v.height()&&e>=t&&i.addClass(a+"up")},b=function(e){i.removeClass(a+"ed "+a+"up"),m.blur(),e||C(m.val(),function(e){e&&(d=k.find("."+s).html(),m&&m.val(d))})};p.on("click",function(e){i.hasClass(a+"ed")?b():(y(e,!0),x()),k.find("."+r).remove()}),p.find(".layui-edge").on("click",function(){m.focus()}),m.on("keyup",function(e){var t=e.keyCode;9===t&&x()}).on("keydown",function(e){var t=e.keyCode;9===t?b():13===t&&e.preventDefault()});var C=function(e,i,a){var n=0;layui.each(g,function(){var i=t(this),l=i.text(),r=l.indexOf(e)===-1;(""===e||"blur"===a?e!==l:r)&&n++,"keyup"===a&&i[r?"addClass":"removeClass"](o)});var l=n===g.length;return i(l),l},w=function(e){var t=this.value,i=e.keyCode;return 9!==i&&13!==i&&37!==i&&38!==i&&39!==i&&40!==i&&(C(t,function(e){e?k.find("."+r)[0]||k.append('

      无匹配项

      '):k.find("."+r).remove()},"keyup"),void(""===t&&k.find("."+r).remove()))};f&&m.on("keyup",w).on("blur",function(t){e=m,d=k.find("."+s).html(),setTimeout(function(){C(m.val(),function(e){d||m.val("")},"blur")},200)}),g.on("click",function(){var e=t(this),a=e.attr("lay-value"),n=h.attr("lay-filter");return!e.hasClass(u)&&(e.hasClass("layui-select-tips")?m.val(""):(m.val(e.text()),e.addClass(s)),e.siblings().removeClass(s),h.val(a).removeClass("layui-form-danger"),layui.event.call(this,l,"select("+n+")",{elem:h[0],value:a,othis:i}),b(!0),!1)}),i.find("dl>dt").on("click",function(e){return!1}),t(document).off("click",y).on("click",y)}};f.each(function(e,l){var r=t(this),o=r.next("."+a),c=this.disabled,d=l.value,f=t(l.options[l.selectedIndex]),y=l.options[0];if("string"==typeof r.attr("lay-ignore"))return r.show();var v="string"==typeof r.attr("lay-search"),p=y?y.value?i:y.innerHTML||i:i,m=t(['
      ','
      ','
      ','
      '+function(e){var t=[];return layui.each(e,function(e,a){0!==e||a.value?"optgroup"===a.tagName.toLowerCase()?t.push("
      "+a.label+"
      "):t.push('
      '+a.innerHTML+"
      "):t.push('
      '+(a.innerHTML||i)+"
      ")}),0===t.length&&t.push('
      没有选项
      '),t.join("")}(r.find("*"))+"
      ","
      "].join(""));o[0]&&o.remove(),r.after(m),h.call(this,m,c,v)})},checkbox:function(){var e={checkbox:["layui-form-checkbox","layui-form-checked","checkbox"],_switch:["layui-form-switch","layui-form-onswitch","switch"]},i=c.find("input[type=checkbox]"),a=function(e,i){var a=t(this);e.on("click",function(){var t=a.attr("lay-filter"),n=(a.attr("lay-text")||"").split("|");a[0].disabled||(a[0].checked?(a[0].checked=!1,e.removeClass(i[1]).find("em").text(n[1])):(a[0].checked=!0,e.addClass(i[1]).find("em").text(n[0])),layui.event.call(a[0],l,i[2]+"("+t+")",{elem:a[0],value:a[0].value,othis:e}))})};i.each(function(i,n){var l=t(this),r=l.attr("lay-skin"),s=(l.attr("lay-text")||"").split("|"),o=this.disabled;"switch"===r&&(r="_"+r);var c=e[r]||e.checkbox;if("string"==typeof l.attr("lay-ignore"))return l.show();var d=l.next("."+c[0]),f=t(['
      ',{_switch:""+((n.checked?s[0]:s[1])||"")+""}[r]||(n.title.replace(/\s/g,"")?""+n.title+"":"")+''+(r?"":"")+"","
      "].join(""));d[0]&&d.remove(),l.after(f),a.call(this,f,c)})},radio:function(){var e="layui-form-radio",i=["",""],a=c.find("input[type=radio]"),n=function(a){var n=t(this),s="layui-anim-scaleSpring";a.on("click",function(){var o=n[0].name,u=n.parents(r),c=n.attr("lay-filter"),d=u.find("input[name="+o.replace(/(\.|#|\[|\])/g,"\\$1")+"]");n[0].disabled||(layui.each(d,function(){var a=t(this).next("."+e);this.checked=!1,a.removeClass(e+"ed"),a.find(".layui-icon").removeClass(s).html(i[1])}),n[0].checked=!0,a.addClass(e+"ed"),a.find(".layui-icon").addClass(s).html(i[0]),layui.event.call(n[0],l,"radio("+c+")",{elem:n[0],value:n[0].value,othis:a}))})};a.each(function(a,l){var r=t(this),s=r.next("."+e),o=this.disabled;if("string"==typeof r.attr("lay-ignore"))return r.show();s[0]&&s.remove();var c=t(['
      ',''+i[l.checked?0:1]+"","
      "+function(){var e=l.title||"";return"string"==typeof r.next().attr("lay-radio")&&(e=r.next().html(),r.next().remove()),e}()+"
      ","
      "].join(""));r.after(c),n.call(this,c)})}};return e?d[e]?d[e]():a.error("不支持的"+e+"表单渲染"):layui.each(d,function(e,t){t()}),n};var d=function(){var e=t(this),a=f.config.verify,s=null,o="layui-form-danger",u={},c=e.parents(r),d=c.find("*[lay-verify]"),y=e.parents("form")[0],v=c.find("input,select,textarea"),h=e.attr("lay-filter");if(layui.each(d,function(e,l){var r=t(this),u=r.attr("lay-verify").split("|"),c=r.attr("lay-verType"),d=r.val();if(r.removeClass(o),layui.each(u,function(e,t){var u,f="",y="function"==typeof a[t];if(a[t]){var u=y?f=a[t](d,l):!a[t][0].test(d);if(f=f||a[t][1],u)return"tips"===c?i.tips(f,function(){return"string"==typeof r.attr("lay-ignore")||"select"!==l.tagName.toLowerCase()&&!/^checkbox|radio$/.test(l.type)?r:r.next()}(),{tips:1}):"alert"===c?i.alert(f,{title:"提示",shadeClose:!0}):i.msg(f,{icon:5,shift:6}),n.android||n.ios||l.focus(),r.addClass(o),s=!0}}),s)return s}),s)return!1;var p={};return layui.each(v,function(e,t){if(t.name=(t.name||"").replace(/^\s*|\s*&/,""),t.name){if(/^.*\[\]$/.test(t.name)){var i=t.name.match(/^(.*)\[\]$/g)[0];p[i]=0|p[i],t.name=t.name.replace(/^(.*)\[\]$/,"$1["+p[i]++ +"]")}/^checkbox|radio$/.test(t.type)&&!t.checked||(u[t.name]=t.value)}}),layui.event.call(this,l,"submit("+h+")",{elem:this,form:y,field:u})},f=new c,y=t(document),v=t(window);f.render(),y.on("reset",r,function(){var e=t(this).attr("lay-filter");setTimeout(function(){f.render(null,e)},50)}),y.on("submit",r,d).on("click","*[lay-submit]",d),e(l,f)});layui.define("jquery",function(e){"use strict";var o=layui.$,a=layui.hint(),i="layui-tree-enter",r=function(e){this.options=e},t={arrow:["",""],checkbox:["",""],radio:["",""],branch:["",""],leaf:""};r.prototype.init=function(e){var o=this;e.addClass("layui-box layui-tree"),o.options.skin&&e.addClass("layui-tree-skin-"+o.options.skin),o.tree(e),o.on(e)},r.prototype.tree=function(e,a){var i=this,r=i.options,n=a||r.nodes;layui.each(n,function(a,n){var l=n.children&&n.children.length>0,c=o('
        '),s=o(["
      • ",function(){return l?''+(n.spread?t.arrow[1]:t.arrow[0])+"":""}(),function(){return r.check?''+("checkbox"===r.check?t.checkbox[0]:"radio"===r.check?t.radio[0]:"")+"":""}(),function(){return'"+(''+(l?n.spread?t.branch[1]:t.branch[0]:t.leaf)+"")+(""+(n.name||"未命名")+"")}(),"
      • "].join(""));l&&(s.append(c),i.tree(c,n.children)),e.append(s),"function"==typeof r.click&&i.click(s,n),i.spread(s,n),r.drag&&i.drag(s,n)})},r.prototype.click=function(e,o){var a=this,i=a.options;e.children("a").on("click",function(e){layui.stope(e),i.click(o)})},r.prototype.spread=function(e,o){var a=this,i=(a.options,e.children(".layui-tree-spread")),r=e.children("ul"),n=e.children("a"),l=function(){e.data("spread")?(e.data("spread",null),r.removeClass("layui-show"),i.html(t.arrow[0]),n.find(".layui-icon").html(t.branch[0])):(e.data("spread",!0),r.addClass("layui-show"),i.html(t.arrow[1]),n.find(".layui-icon").html(t.branch[1]))};r[0]&&(i.on("click",l),n.on("dblclick",l))},r.prototype.on=function(e){var a=this,r=a.options,t="layui-tree-drag";e.find("i").on("selectstart",function(e){return!1}),r.drag&&o(document).on("mousemove",function(e){var i=a.move;if(i.from){var r=(i.to,o('
        '));e.preventDefault(),o("."+t)[0]||o("body").append(r);var n=o("."+t)[0]?o("."+t):r;n.addClass("layui-show").html(i.from.elem.children("a").html()),n.css({left:e.pageX+10,top:e.pageY+10})}}).on("mouseup",function(){var e=a.move;e.from&&(e.from.elem.children("a").removeClass(i),e.to&&e.to.elem.children("a").removeClass(i),a.move={},o("."+t).remove())})},r.prototype.move={},r.prototype.drag=function(e,a){var r=this,t=(r.options,e.children("a")),n=function(){var t=o(this),n=r.move;n.from&&(n.to={item:a,elem:e},t.addClass(i))};t.on("mousedown",function(){var o=r.move;o.from={item:a,elem:e}}),t.on("mouseenter",n).on("mousemove",n).on("mouseleave",function(){var e=o(this),a=r.move;a.from&&(delete a.to,e.removeClass(i))})},e("tree",function(e){var i=new r(e=e||{}),t=o(e.elem);return t[0]?void i.init(t):a.error("layui.tree 没有找到"+e.elem+"元素")})});layui.define(["laytpl","laypage","layer","form"],function(e){"use strict";var t=layui.$,i=layui.laytpl,a=layui.laypage,l=layui.layer,n=layui.form,o=layui.hint(),r=layui.device(),d={config:{checkName:"LAY_CHECKED",indexName:"LAY_TABLE_INDEX"},cache:{},index:layui.table?layui.table.index+1e4:0,set:function(e){var i=this;return i.config=t.extend({},i.config,e),i},on:function(e,t){return layui.onevent.call(this,s,e,t)}},c=function(){var e=this,t=e.config,i=t.id;return i&&(c.config[i]=t),{reload:function(t){e.reload.call(e,t)},config:t}},s="table",u=".layui-table",h="layui-hide",f="layui-none",y="layui-table-view",p=".layui-table-header",m=".layui-table-body",v=".layui-table-main",g=".layui-table-fixed",x=".layui-table-fixed-l",b=".layui-table-fixed-r",k=".layui-table-tool",C=".layui-table-page",w=".layui-table-sort",N="layui-table-edit",T="layui-table-hover",F=function(e){var t='{{#if(item2.colspan){}} colspan="{{item2.colspan}}"{{#} if(item2.rowspan){}} rowspan="{{item2.rowspan}}"{{#}}}';return e=e||{},['',"","{{# layui.each(d.data.cols, function(i1, item1){ }}","","{{# layui.each(item1, function(i2, item2){ }}",'{{# if(item2.fixed && item2.fixed !== "right"){ left = true; } }}','{{# if(item2.fixed === "right"){ right = true; } }}',function(){return e.fixed&&"right"!==e.fixed?'{{# if(item2.fixed && item2.fixed !== "right"){ }}':"right"===e.fixed?'{{# if(item2.fixed === "right"){ }}':""}(),'",e.fixed?"{{# }; }}":"","{{# }); }}","","{{# }); }}","","
        ','
        1){ }}","group","{{# } else { }}","{{d.index}}-{{item2.field || i2}}",'{{# if(item2.type !== "normal"){ }}'," laytable-cell-{{ item2.type }}","{{# } }}","{{# } }}",'" {{#if(item2.align){}}align="{{item2.align}}"{{#}}}>','{{# if(item2.type === "checkbox"){ }}','',"{{# } else { }}",'{{item2.title||""}}',"{{# if(!(item2.colspan > 1) && item2.sort){ }}",'',"{{# } }}","{{# } }}","
        ","
        "].join("")},W=['',"","
        "].join(""),z=['
        ',"{{# if(d.data.toolbar){ }}",'
        ',"{{# } }}",'
        ',"{{# var left, right; }}",'
        ',F(),"
        ",'
        ',W,"
        ","{{# if(left){ }}",'
        ','
        ',F({fixed:!0}),"
        ",'
        ',W,"
        ","
        ","{{# }; }}","{{# if(right){ }}",'
        ','
        ',F({fixed:"right"}),'
        ',"
        ",'
        ',W,"
        ","
        ","{{# }; }}","
        ","{{# if(d.data.page){ }}",'
        ','
        ',"
        ","{{# } }}","","
        "].join(""),A=t(window),S=t(document),M=function(e){var i=this;i.index=++d.index,i.config=t.extend({},i.config,d.config,e),i.render()};M.prototype.config={limit:10,loading:!0,cellMinWidth:60,text:{none:"无数据"}},M.prototype.render=function(){var e=this,a=e.config;if(a.elem=t(a.elem),a.where=a.where||{},a.id=a.id||a.elem.attr("id"),a.request=t.extend({pageName:"page",limitName:"limit"},a.request),a.response=t.extend({statusName:"code",statusCode:0,msgName:"msg",dataName:"data",countName:"count"},a.response),"object"==typeof a.page&&(a.limit=a.page.limit||a.limit,a.limits=a.page.limits||a.limits,e.page=a.page.curr=a.page.curr||1,delete a.page.elem,delete a.page.jump),!a.elem[0])return e;e.setArea();var l=a.elem,n=l.next("."+y),o=e.elem=t(i(z).render({VIEW_CLASS:y,data:a,index:e.index}));if(a.index=e.index,n[0]&&n.remove(),l.after(o),e.layHeader=o.find(p),e.layMain=o.find(v),e.layBody=o.find(m),e.layFixed=o.find(g),e.layFixLeft=o.find(x),e.layFixRight=o.find(b),e.layTool=o.find(k),e.layPage=o.find(C),e.layTool.html(i(t(a.toolbar).html()||"").render(a)),a.height&&e.fullSize(),a.cols.length>1){var r=e.layFixed.find(p).find("th");r.height(e.layHeader.height()-1-parseFloat(r.css("padding-top"))-parseFloat(r.css("padding-bottom")))}e.pullData(e.page),e.events()},M.prototype.initOpts=function(e){var t=this,i=(t.config,{checkbox:48,space:15,numbers:40});e.checkbox&&(e.type="checkbox"),e.space&&(e.type="space"),e.type||(e.type="normal"),"normal"!==e.type&&(e.unresize=!0,e.width=e.width||i[e.type])},M.prototype.setArea=function(){var e=this,t=e.config,i=0,a=0,l=0,n=0,o=t.width||function(){var e=function(i){var a,l;i=i||t.elem.parent(),a=i.width();try{l="none"===i.css("display")}catch(n){}return!i[0]||a&&!l?a:e(i.parent())};return e()}();e.eachCols(function(){i++}),o-=function(){return"line"===t.skin||"nob"===t.skin?2:i+1}(),layui.each(t.cols,function(t,i){layui.each(i,function(t,l){var r;return l?(e.initOpts(l),r=l.width||0,void(l.colspan>1||(/\d+%$/.test(r)?l.width=r=Math.floor(parseFloat(r)/100*o):r||(l.width=r=0,a++),n+=r))):void i.splice(t,1)})}),e.autoColNums=a,o>n&&a&&(l=(o-n)/a),layui.each(t.cols,function(e,i){layui.each(i,function(e,i){var a=i.minWidth||t.cellMinWidth;i.colspan>1||0===i.width&&(i.width=Math.floor(l>=a?l:a))})}),t.height&&/^full-\d+$/.test(t.height)&&(e.fullHeightGap=t.height.split("-")[1],t.height=A.height()-e.fullHeightGap)},M.prototype.reload=function(e){var i=this;i.config.data&&i.config.data.constructor===Array&&delete i.config.data,i.config=t.extend({},i.config,e),i.render()},M.prototype.page=1,M.prototype.pullData=function(e,i){var a=this,n=a.config,o=n.request,r=n.response,d=function(){"object"==typeof n.initSort&&a.sort(n.initSort.field,n.initSort.type)};if(a.startTime=(new Date).getTime(),n.url){var c={};c[o.pageName]=e,c[o.limitName]=n.limit;var s=t.extend(c,n.where);n.contentType&&0==n.contentType.indexOf("application/json")&&(s=JSON.stringify(s)),t.ajax({type:n.method||"get",url:n.url,contentType:n.contentType,data:s,dataType:"json",headers:n.headers||{},success:function(t){t[r.statusName]!=r.statusCode?(a.renderForm(),a.layMain.html('
        '+(t[r.msgName]||"返回的数据状态异常")+"
        ")):(a.renderData(t,e,t[r.countName]),d(),n.time=(new Date).getTime()-a.startTime+" ms"),i&&l.close(i),"function"==typeof n.done&&n.done(t,e,t[r.countName])},error:function(e,t){a.layMain.html('
        数据接口请求异常
        '),a.renderForm(),i&&l.close(i)}})}else if(n.data&&n.data.constructor===Array){var u={},h=e*n.limit-n.limit;u[r.dataName]=n.data.concat().splice(h,n.limit),u[r.countName]=n.data.length,a.renderData(u,e,n.data.length),d(),"function"==typeof n.done&&n.done(u,e,u[r.countName])}},M.prototype.eachCols=function(e){var i=t.extend(!0,[],this.config.cols),a=[],l=0;layui.each(i,function(e,t){layui.each(t,function(t,n){if(n.colspan>1){var o=0;l++,n.CHILD_COLS=[],layui.each(i[e+1],function(e,t){t.PARENT_COL||o==n.colspan||(t.PARENT_COL=l,n.CHILD_COLS.push(t),o+=t.colspan>1?t.colspan:1)})}n.PARENT_COL||a.push(n)})});var n=function(t){layui.each(t||a,function(t,i){return i.CHILD_COLS?n(i.CHILD_COLS):void e(t,i)})};n()},M.prototype.renderData=function(e,n,o,r){var c=this,s=c.config,u=e[s.response.dataName]||[],y=[],p=[],m=[],v=function(){return!r&&c.sortKey?c.sort(c.sortKey.field,c.sortKey.sort,!0):(layui.each(u,function(e,a){var l=[],o=[],u=[],h=e+s.limit*(n-1)+1;0!==a.length&&(r||(a[d.config.indexName]=e),c.eachCols(function(e,n){var r=n.field||e,f=a[r];c.getColElem(c.layHeader,r);if(void 0!==f&&null!==f||(f=""),!(n.colspan>1)){var y=['",'
        '+function(){var e=t.extend(!0,{LAY_INDEX:h},a);return"checkbox"===n.type?'":"numbers"===n.type?h:n.toolbar?i(t(n.toolbar).html()||"").render(e):n.templet?function(){return"function"==typeof n.templet?n.templet(e):i(t(n.templet).html()||String(f)).render(e)}():f}(),"
        "].join("");l.push(y),n.fixed&&"right"!==n.fixed&&o.push(y),"right"===n.fixed&&u.push(y)}}),y.push(''+l.join("")+""),p.push(''+o.join("")+""),m.push(''+u.join("")+""))}),c.layBody.scrollTop(0),c.layMain.find("."+f).remove(),c.layMain.find("tbody").html(y.join("")),c.layFixLeft.find("tbody").html(p.join("")),c.layFixRight.find("tbody").html(m.join("")),c.renderForm(),c.syncCheckAll(),c.haveInit?c.scrollPatch():setTimeout(function(){c.scrollPatch()},50),c.haveInit=!0,void l.close(c.tipsIndex))};return c.key=s.id||s.index,d.cache[c.key]=u,c.layPage[0===u.length&&1==n?"addClass":"removeClass"](h),r?v():0===u.length?(c.renderForm(),c.layFixed.remove(),c.layMain.find("tbody").html(""),c.layMain.find("."+f).remove(),c.layMain.append('
        '+s.text.none+"
        ")):(v(),void(s.page&&(s.page=t.extend({elem:"layui-table-page"+s.index,count:o,limit:s.limit,limits:s.limits||[10,20,30,40,50,60,70,80,90],groups:3,layout:["prev","page","next","skip","count","limit"],prev:'',next:'',jump:function(e,t){t||(c.page=e.curr,s.limit=e.limit,c.pullData(e.curr,c.loading()))}},s.page),s.page.count=o,a.render(s.page))))},M.prototype.getColElem=function(e,t){var i=this,a=i.config;return e.eq(0).find(".laytable-cell-"+(a.index+"-"+t)+":eq(0)")},M.prototype.renderForm=function(e){n.render(e,"LAY-table-"+this.index)},M.prototype.sort=function(e,i,a,l){var n,r,c=this,u={},h=c.config,f=h.elem.attr("lay-filter"),y=d.cache[c.key];"string"==typeof e&&c.layHeader.find("th").each(function(i,a){var l=t(this),o=l.data("field");if(o===e)return e=l,n=o,!1});try{var n=n||e.data("field");if(c.sortKey&&!a&&n===c.sortKey.field&&i===c.sortKey.sort)return;var p=c.layHeader.find("th .laytable-cell-"+h.index+"-"+n).find(w);c.layHeader.find("th").find(w).removeAttr("lay-sort"),p.attr("lay-sort",i||null),c.layFixed.find("th")}catch(m){return o.error("Table modules: Did not match to field")}c.sortKey={field:n,sort:i},"asc"===i?r=layui.sort(y,n):"desc"===i?r=layui.sort(y,n,!0):(r=layui.sort(y,d.config.indexName),delete c.sortKey),u[h.response.dataName]=r,c.renderData(u,c.page,c.count,!0),l&&layui.event.call(e,s,"sort("+f+")",{field:n,type:i})},M.prototype.loading=function(){var e=this,t=e.config;if(t.loading&&t.url)return l.msg("数据请求中",{icon:16,offset:[e.elem.offset().top+e.elem.height()/2-35-A.scrollTop()+"px",e.elem.offset().left+e.elem.width()/2-90-A.scrollLeft()+"px"],time:-1,anim:-1,fixed:!1})},M.prototype.setCheckData=function(e,t){var i=this,a=i.config,l=d.cache[i.key];l[e]&&l[e].constructor!==Array&&(l[e][a.checkName]=t)},M.prototype.syncCheckAll=function(){var e=this,t=e.config,i=e.layHeader.find('input[name="layTableCheckbox"]'),a=function(i){return e.eachCols(function(e,a){"checkbox"===a.type&&(a[t.checkName]=i)}),i};i[0]&&(d.checkStatus(e.key).isAll?(i[0].checked||(i.prop("checked",!0),e.renderForm("checkbox")),a(!0)):(i[0].checked&&(i.prop("checked",!1),e.renderForm("checkbox")),a(!1)))},M.prototype.getCssRule=function(e,t){var i=this,a=i.elem.find("style")[0],l=a.sheet||a.styleSheet||{},n=l.cssRules||l.rules;layui.each(n,function(a,l){if(l.selectorText===".laytable-cell-"+i.index+"-"+e)return t(l),!0})},M.prototype.fullSize=function(){var e,t=this,i=t.config,a=i.height;t.fullHeightGap&&(a=A.height()-t.fullHeightGap,a<135&&(a=135),t.elem.css("height",a)),e=parseFloat(a)-parseFloat(t.layHeader.height())-1,i.toolbar&&(e-=t.layTool.outerHeight()),i.page&&(e=e-t.layPage.outerHeight()-1),t.layMain.css("height",e)},M.prototype.getScrollWidth=function(e){var t=0;return e?t=e.offsetWidth-e.clientWidth:(e=document.createElement("div"),e.style.width="100px",e.style.height="100px",e.style.overflowY="scroll",document.body.appendChild(e),t=e.offsetWidth-e.clientWidth,document.body.removeChild(e)),t},M.prototype.scrollPatch=function(){var e=this,i=e.layMain.children("table"),a=e.layMain.width()-e.layMain.prop("clientWidth"),l=e.layMain.height()-e.layMain.prop("clientHeight"),n=e.getScrollWidth(e.layMain[0]),o=i.outerWidth()-e.layMain.width();if(e.autoColNums&&o<5&&!e.scrollPatchWStatus){var r=e.layHeader.eq(0).find("thead th:last-child"),d=r.data("field");e.getCssRule(d,function(t){var i=t.style.width||r.outerWidth();t.style.width=parseFloat(i)-n-o+"px",e.layMain.height()-e.layMain.prop("clientHeight")>0&&(t.style.width=parseFloat(t.style.width)-1+"px"),e.scrollPatchWStatus=!0})}if(a&&l){if(!e.elem.find(".layui-table-patch")[0]){var c=t('
        ');c.find("div").css({width:a}),e.layHeader.eq(0).find("thead tr").append(c)}}else e.layHeader.eq(0).find(".layui-table-patch").remove();var s=e.layMain.height(),u=s-l;e.layFixed.find(m).css("height",i.height()>u?u:"auto"),e.layFixRight[o>0?"removeClass":"addClass"](h),e.layFixRight.css("right",a-1)},M.prototype.events=function(){var e,a=this,n=a.config,o=t("body"),c={},u=a.layHeader.find("th"),h=".layui-table-cell",f=n.elem.attr("lay-filter");u.on("mousemove",function(e){var i=t(this),a=i.offset().left,l=e.clientX-a;i.attr("colspan")>1||i.data("unresize")||c.resizeStart||(c.allowResize=i.width()-l<=10,o.css("cursor",c.allowResize?"col-resize":""))}).on("mouseleave",function(){t(this);c.resizeStart||o.css("cursor","")}).on("mousedown",function(e){var i=t(this);if(c.allowResize){var l=i.data("field");e.preventDefault(),c.resizeStart=!0,c.offset=[e.clientX,e.clientY],a.getCssRule(l,function(e){var t=e.style.width||i.outerWidth();c.rule=e,c.ruleWidth=parseFloat(t),c.minWidth=i.data("minwidth")||n.cellMinWidth})}}),S.on("mousemove",function(t){if(c.resizeStart){if(t.preventDefault(),c.rule){var i=c.ruleWidth+t.clientX-c.offset[0];i');d[0].value=e.data("content")||o.text(),e.find("."+N)[0]||e.append(d),d.focus()}else o.find(".layui-form-switch,.layui-form-checkbox")[0]||Math.round(o.prop("scrollWidth"))>Math.round(o.outerWidth())&&(a.tipsIndex=l.tips(['
        ',o.html(),"
        ",''].join(""),o[0],{tips:[3,""],time:-1,anim:-1,maxWidth:r.ios||r.android?300:600,isOutAnim:!1,skin:"layui-table-tips",success:function(e,t){e.find(".layui-table-tips-c").on("click",function(){l.close(t)})}}))}),a.layBody.on("click","*[lay-event]",function(){var e=t(this),l=e.parents("tr").eq(0).data("index"),n=a.layBody.find('tr[data-index="'+l+'"]'),o="layui-table-click",r=d.cache[a.key][l];layui.event.call(this,s,"tool("+f+")",{data:d.clearCacheKey(r),event:e.attr("lay-event"),tr:n,del:function(){d.cache[a.key][l]=[],n.remove(),a.scrollPatch()},update:function(e){e=e||{},layui.each(e,function(e,l){if(e in r){var o,d=n.children('td[data-field="'+e+'"]');r[e]=l,a.eachCols(function(t,i){i.field==e&&i.templet&&(o=i.templet)}),d.children(h).html(o?i(t(o).html()||l).render(r):l),d.data("content",l)}})}}),n.addClass(o).siblings("tr").removeClass(o)}),a.layMain.on("scroll",function(){var e=t(this),i=e.scrollLeft(),n=e.scrollTop();a.layHeader.scrollLeft(i),a.layFixed.find(m).scrollTop(n),l.close(a.tipsIndex)}),A.on("resize",function(){a.fullSize(),a.scrollPatch()})},d.init=function(e,i){i=i||{};var a=this,l=t(e?'table[lay-filter="'+e+'"]':u+"[lay-data]"),n="Table element property lay-data configuration item has a syntax error: ";return l.each(function(){var a=t(this),l=a.attr("lay-data");try{l=new Function("return "+l)()}catch(r){o.error(n+l)}var c=[],s=t.extend({elem:this,cols:[],data:[],skin:a.attr("lay-skin"),size:a.attr("lay-size"),even:"string"==typeof a.attr("lay-even")},d.config,i,l);e&&a.hide(),a.find("thead>tr").each(function(e){s.cols[e]=[],t(this).children().each(function(i){var a=t(this),l=a.attr("lay-data");try{l=new Function("return "+l)()}catch(r){return o.error(n+l)}var d=t.extend({title:a.text(),colspan:a.attr("colspan")||0,rowspan:a.attr("rowspan")||0},l);d.colspan<2&&c.push(d),s.cols[e].push(d)})}),a.find("tbody>tr").each(function(e){var i=t(this),a={};i.children("td").each(function(e,i){var l=t(this),n=l.data("field");if(n)return a[n]=l.html()}),layui.each(c,function(e,t){var l=i.children("td").eq(e);a[t.field]=l.html()}),s.data[e]=a}),d.render(s)}),a},d.checkStatus=function(e){var t=0,i=0,a=[],l=d.cache[e]||[];return layui.each(l,function(e,l){return l.constructor===Array?void i++:void(l[d.config.checkName]&&(t++,a.push(d.clearCacheKey(l))))}),{data:a,isAll:!!l.length&&t===l.length-i}},c.config={},d.reload=function(e,i){var a=c.config[e];return i=i||{},a?(i.data&&i.data.constructor===Array&&delete a.data,d.render(t.extend(!0,{},a,i))):o.error("The ID option was not found in the table instance")},d.render=function(e){var t=new M(e);return c.call(t)},d.clearCacheKey=function(e){return e=t.extend({},e),delete e[d.config.checkName],delete e[d.config.indexName],e},d.init(),e(s,d)});layui.define("jquery",function(e){"use strict";var i=layui.$,n=(layui.hint(),layui.device(),{config:{},set:function(e){var n=this;return n.config=i.extend({},n.config,e),n},on:function(e,i){return layui.onevent.call(this,t,e,i)}}),t="carousel",a="layui-this",l=">*[carousel-item]>*",o="layui-carousel-left",r="layui-carousel-right",d="layui-carousel-prev",s="layui-carousel-next",u="layui-carousel-arrow",c="layui-carousel-ind",m=function(e){var t=this;t.config=i.extend({},t.config,n.config,e),t.render()};m.prototype.config={width:"600px",height:"280px",full:!1,arrow:"hover",indicator:"inside",autoplay:!0,interval:3e3,anim:"",trigger:"click",index:0},m.prototype.render=function(){var e=this,n=e.config;n.elem=i(n.elem),n.elem[0]&&(e.elemItem=n.elem.find(l),n.index<0&&(n.index=0),n.index>=e.elemItem.length&&(n.index=e.elemItem.length-1),n.interval<800&&(n.interval=800),n.full?n.elem.css({position:"fixed",width:"100%",height:"100%",zIndex:9999}):n.elem.css({width:n.width,height:n.height}),n.elem.attr("lay-anim",n.anim),e.elemItem.eq(n.index).addClass(a),e.elemItem.length<=1||(e.indicator(),e.arrow(),e.autoplay(),e.events()))},m.prototype.reload=function(e){var n=this;clearInterval(n.timer),n.config=i.extend({},n.config,e),n.render()},m.prototype.prevIndex=function(){var e=this,i=e.config,n=i.index-1;return n<0&&(n=e.elemItem.length-1),n},m.prototype.nextIndex=function(){var e=this,i=e.config,n=i.index+1;return n>=e.elemItem.length&&(n=0),n},m.prototype.addIndex=function(e){var i=this,n=i.config;e=e||1,n.index=n.index+e,n.index>=i.elemItem.length&&(n.index=0)},m.prototype.subIndex=function(e){var i=this,n=i.config;e=e||1,n.index=n.index-e,n.index<0&&(n.index=i.elemItem.length-1)},m.prototype.autoplay=function(){var e=this,i=e.config;i.autoplay&&(e.timer=setInterval(function(){e.slide()},i.interval))},m.prototype.arrow=function(){var e=this,n=e.config,t=i(['",'"].join(""));n.elem.attr("lay-arrow",n.arrow),n.elem.find("."+u)[0]&&n.elem.find("."+u).remove(),n.elem.append(t),t.on("click",function(){var n=i(this),t=n.attr("lay-type");e.slide(t)})},m.prototype.indicator=function(){var e=this,n=e.config,t=e.elemInd=i(['
          ',function(){var i=[];return layui.each(e.elemItem,function(e){i.push("")}),i.join("")}(),"
        "].join(""));n.elem.attr("lay-indicator",n.indicator),n.elem.find("."+c)[0]&&n.elem.find("."+c).remove(),n.elem.append(t),"updown"===n.anim&&t.css("margin-top",-(t.height()/2)),t.find("li").on("hover"===n.trigger?"mouseover":n.trigger,function(){var t=i(this),a=t.index();a>n.index?e.slide("add",a-n.index):a',e.bar1?'
      • '+c[0]+"
      • ":"",e.bar2?'
      • '+c[1]+"
      • ":"",'
      • '+c[2]+"
      • ",""].join("")),s=g.find("."+r),u=function(){var t=n.scrollTop();t>=e.showHeight?i||(s.show(),i=1):i&&(s.hide(),i=0)};t("."+a)[0]||("object"==typeof e.css&&g.css(e.css),l.append(g),u(),g.find("li").on("click",function(){var i=t(this),o=i.attr("lay-type");"top"===o&&t("html,body").animate({scrollTop:0},200),e.click&&e.click.call(this,o)}),n.on("scroll",function(){clearTimeout(o),o=setTimeout(function(){u()},100)}))},countdown:function(e,t,i){var o=this,a="function"==typeof t,r=new Date(e).getTime(),n=new Date(!t||a?(new Date).getTime():t).getTime(),l=r-n,c=[Math.floor(l/864e5),Math.floor(l/36e5)%24,Math.floor(l/6e4)%60,Math.floor(l/1e3)%60];a&&(i=t);var g=setTimeout(function(){o.countdown(e,n+1e3,i)},1e3);return i&&i(l>0?c:[0,0,0,0],t,g),l<=0&&clearTimeout(g),g},timeAgo:function(e,t){var i=this,o=[[],[]],a=(new Date).getTime()-new Date(e).getTime();return a>6912e5?(a=new Date(e),o[0][0]=i.digit(a.getFullYear(),4),o[0][1]=i.digit(a.getMonth()+1),o[0][2]=i.digit(a.getDate()),t||(o[1][0]=i.digit(a.getHours()),o[1][1]=i.digit(a.getMinutes()),o[1][2]=i.digit(a.getSeconds())),o[0].join("-")+" "+o[1].join(":")):a>=864e5?(a/1e3/60/60/24|0)+"天前":a>=36e5?(a/1e3/60/60|0)+"小时前":a>=12e4?(a/1e3/60|0)+"分钟前":a<0?"未来":"刚刚"},digit:function(e,t){var i="";e=String(e),t=t||2;for(var o=e.length;o';o.prototype.load=function(e){var o,i,n,r,a=this,c=0;e=e||{};var f=l(e.elem);if(f[0]){var m=l(e.scrollElem||document),u=e.mb||50,s=!("isAuto"in e)||e.isAuto,v=e.end||"没有更多了",y=e.scrollElem&&e.scrollElem!==document,d="加载更多",h=l('");f.find(".layui-flow-more")[0]||f.append(h);var p=function(e,t){e=l(e),h.before(e),t=0==t||null,t?h.html(v):h.find("a").html(d),i=t,o=null,n&&n()},g=function(){o=!0,h.find("a").html(t),"function"==typeof e.done&&e.done(++c,p)};if(g(),h.find("a").on("click",function(){l(this);i||o||g()}),e.isLazyimg)var n=a.lazyimg({elem:e.elem+" img",scrollElem:e.scrollElem});return s?(m.on("scroll",function(){var e=l(this),t=e.scrollTop();r&&clearTimeout(r),i||(r=setTimeout(function(){var i=y?e.height():l(window).height(),n=y?e.prop("scrollHeight"):document.documentElement.scrollHeight;n-t-i<=u&&(o||g())},100))}),a):a}},o.prototype.lazyimg=function(e){var o,t=this,i=0;e=e||{};var n=l(e.scrollElem||document),r=e.elem||"img",a=e.scrollElem&&e.scrollElem!==document,c=function(e,l){var o=n.scrollTop(),r=o+l,c=a?function(){return e.offset().top-n.offset().top+o}():e.offset().top;if(c>=o&&c<=r&&!e.attr("src")){var m=e.attr("lay-src");layui.img(m,function(){var l=t.lazyimg.elem.eq(i);e.attr("src",m).removeAttr("lay-src"),l[0]&&f(l),i++})}},f=function(e,o){var f=a?(o||n).height():l(window).height(),m=n.scrollTop(),u=m+f;if(t.lazyimg.elem=l(r),e)c(e,f);else for(var s=0;su)break}};if(f(),!o){var m;n.on("scroll",function(){var e=l(this);m&&clearTimeout(m),m=setTimeout(function(){f(null,e)},50)}),o=!0}return f},e("flow",new o)});layui.define(["layer","form"],function(t){"use strict";var e=layui.$,i=layui.layer,a=layui.form,l=(layui.hint(),layui.device()),n="layedit",o="layui-show",r="layui-disabled",c=function(){var t=this;t.index=0,t.config={tool:["strong","italic","underline","del","|","left","center","right","|","link","unlink","face","image"],hideTool:[],height:280}};c.prototype.set=function(t){var i=this;return e.extend(!0,i.config,t),i},c.prototype.on=function(t,e){return layui.onevent(n,t,e)},c.prototype.build=function(t,i){i=i||{};var a=this,n=a.config,r="layui-layedit",c=e("string"==typeof t?"#"+t:t),u="LAY_layedit_"+ ++a.index,d=c.next("."+r),y=e.extend({},n,i),f=function(){var t=[],e={};return layui.each(y.hideTool,function(t,i){e[i]=!0}),layui.each(y.tool,function(i,a){C[a]&&!e[a]&&t.push(C[a])}),t.join("")}(),m=e(['
        ','
        '+f+"
        ",'
        ','',"
        ","
        "].join(""));return l.ie&&l.ie<8?c.removeClass("layui-hide").addClass(o):(d[0]&&d.remove(),s.call(a,m,c[0],y),c.addClass("layui-hide").after(m),a.index)},c.prototype.getContent=function(t){var e=u(t);if(e[0])return d(e[0].document.body.innerHTML)},c.prototype.getText=function(t){var i=u(t);if(i[0])return e(i[0].document.body).text()},c.prototype.setContent=function(t,i,a){var l=u(t);l[0]&&(a?e(l[0].document.body).append(i):e(l[0].document.body).html(i),layedit.sync(t))},c.prototype.sync=function(t){var i=u(t);if(i[0]){var a=e("#"+i[1].attr("textarea"));a.val(d(i[0].document.body.innerHTML))}},c.prototype.getSelection=function(t){var e=u(t);if(e[0]){var i=m(e[0].document);return document.selection?i.text:i.toString()}};var s=function(t,i,a){var l=this,n=t.find("iframe");n.css({height:a.height}).on("load",function(){var o=n.contents(),r=n.prop("contentWindow"),c=o.find("head"),s=e([""].join("")),u=o.find("body");c.append(s),u.attr("contenteditable","true").css({"min-height":a.height}).html(i.value||""),y.apply(l,[r,n,i,a]),g.call(l,r,t,a)})},u=function(t){var i=e("#LAY_layedit_"+t),a=i.prop("contentWindow");return[a,i]},d=function(t){return 8==l.ie&&(t=t.replace(/<.+>/g,function(t){return t.toLowerCase()})),t},y=function(t,a,n,o){var r=t.document,c=e(r.body);c.on("keydown",function(t){var e=t.keyCode;if(13===e){var a=m(r),l=p(a),n=l.parentNode;if("pre"===n.tagName.toLowerCase()){if(t.shiftKey)return;return i.msg("请暂时用shift+enter"),!1}r.execCommand("formatBlock",!1,"

        ")}}),e(n).parents("form").on("submit",function(){var t=c.html();8==l.ie&&(t=t.replace(/<.+>/g,function(t){return t.toLowerCase()})),n.value=t}),c.on("paste",function(e){r.execCommand("formatBlock",!1,"

        "),setTimeout(function(){f.call(t,c),n.value=c.html()},100)})},f=function(t){var i=this;i.document;t.find("*[style]").each(function(){var t=this.style.textAlign;this.removeAttribute("style"),e(this).css({"text-align":t||""})}),t.find("table").addClass("layui-table"),t.find("script,link").remove()},m=function(t){return t.selection?t.selection.createRange():t.getSelection().getRangeAt(0)},p=function(t){return t.endContainer||t.parentElement().childNodes[0]},v=function(t,i,a){var l=this.document,n=document.createElement(t);for(var o in i)n.setAttribute(o,i[o]);if(n.removeAttribute("text"),l.selection){var r=a.text||i.text;if("a"===t&&!r)return;r&&(n.innerHTML=r),a.pasteHTML(e(n).prop("outerHTML")),a.select()}else{var r=a.toString()||i.text;if("a"===t&&!r)return;r&&(n.innerHTML=r),a.deleteContents(),a.insertNode(n)}},h=function(t,i){var a=this.document,l="layedit-tool-active",n=p(m(a)),o=function(e){return t.find(".layedit-tool-"+e)};i&&i[i.hasClass(l)?"removeClass":"addClass"](l),t.find(">i").removeClass(l),o("unlink").addClass(r),e(n).parents().each(function(){var t=this.tagName.toLowerCase(),e=this.style.textAlign;"b"!==t&&"strong"!==t||o("b").addClass(l),"i"!==t&&"em"!==t||o("i").addClass(l),"u"===t&&o("u").addClass(l),"strike"===t&&o("d").addClass(l),"p"===t&&("center"===e?o("center").addClass(l):"right"===e?o("right").addClass(l):o("left").addClass(l)),"a"===t&&(o("link").addClass(l),o("unlink").removeClass(r))})},g=function(t,a,l){var n=t.document,o=e(n.body),c={link:function(i){var a=p(i),l=e(a).parent();b.call(o,{href:l.attr("href"),target:l.attr("target")},function(e){var a=l[0];"A"===a.tagName?a.href=e.url:v.call(t,"a",{target:e.target,href:e.url,text:e.url},i)})},unlink:function(t){n.execCommand("unlink")},face:function(e){x.call(this,function(i){v.call(t,"img",{src:i.src,alt:i.alt},e)})},image:function(a){var n=this;layui.use("upload",function(o){var r=l.uploadImage||{};o.render({url:r.url,method:r.type,elem:e(n).find("input")[0],done:function(e){0==e.code?(e.data=e.data||{},v.call(t,"img",{src:e.data.src,alt:e.data.title},a)):i.msg(e.msg||"上传失败")}})})},code:function(e){k.call(o,function(i){v.call(t,"pre",{text:i.code,"lay-lang":i.lang},e)})},help:function(){i.open({type:2,title:"帮助",area:["600px","380px"],shadeClose:!0,shade:.1,skin:"layui-layer-msg",content:["http://www.layui.com/about/layedit/help.html","no"]})}},s=a.find(".layui-layedit-tool"),u=function(){var i=e(this),a=i.attr("layedit-event"),l=i.attr("lay-command");if(!i.hasClass(r)){o.focus();var u=m(n);u.commonAncestorContainer;l?(n.execCommand(l),/justifyLeft|justifyCenter|justifyRight/.test(l)&&n.execCommand("formatBlock",!1,"

        "),setTimeout(function(){o.focus()},10)):c[a]&&c[a].call(this,u),h.call(t,s,i)}},d=/image/;s.find(">i").on("mousedown",function(){var t=e(this),i=t.attr("layedit-event");d.test(i)||u.call(this)}).on("click",function(){var t=e(this),i=t.attr("layedit-event");d.test(i)&&u.call(this)}),o.on("click",function(){h.call(t,s),i.close(x.index)})},b=function(t,e){var l=this,n=i.open({type:1,id:"LAY_layedit_link",area:"350px",shade:.05,shadeClose:!0,moveType:1,title:"超链接",skin:"layui-layer-msg",content:['

          ','
        • ','','
          ','',"
          ","
        • ",'
        • ','','
          ','",'","
          ","
        • ",'
        • ','','',"
        • ","
        "].join(""),success:function(t,n){var o="submit(layedit-link-yes)";a.render("radio"),t.find(".layui-btn-primary").on("click",function(){i.close(n),l.focus()}),a.on(o,function(t){i.close(b.index),e&&e(t.field)})}});b.index=n},x=function(t){var a=function(){var t=["[微笑]","[嘻嘻]","[哈哈]","[可爱]","[可怜]","[挖鼻]","[吃惊]","[害羞]","[挤眼]","[闭嘴]","[鄙视]","[爱你]","[泪]","[偷笑]","[亲亲]","[生病]","[太开心]","[白眼]","[右哼哼]","[左哼哼]","[嘘]","[衰]","[委屈]","[吐]","[哈欠]","[抱抱]","[怒]","[疑问]","[馋嘴]","[拜拜]","[思考]","[汗]","[困]","[睡]","[钱]","[失望]","[酷]","[色]","[哼]","[鼓掌]","[晕]","[悲伤]","[抓狂]","[黑线]","[阴险]","[怒骂]","[互粉]","[心]","[伤心]","[猪头]","[熊猫]","[兔子]","[ok]","[耶]","[good]","[NO]","[赞]","[来]","[弱]","[草泥马]","[神马]","[囧]","[浮云]","[给力]","[围观]","[威武]","[奥特曼]","[礼物]","[钟]","[话筒]","[蜡烛]","[蛋糕]"],e={};return layui.each(t,function(t,i){e[i]=layui.cache.dir+"images/face/"+t+".gif"}),e}();return x.hide=x.hide||function(t){"face"!==e(t.target).attr("layedit-event")&&i.close(x.index)},x.index=i.tips(function(){var t=[];return layui.each(a,function(e,i){t.push('
      • '+e+'
      • ')}),'
          '+t.join("")+"
        "}(),this,{tips:1,time:0,skin:"layui-box layui-util-face",maxWidth:500,success:function(l,n){l.css({marginTop:-4,marginLeft:-10}).find(".layui-clear>li").on("click",function(){t&&t({src:a[this.title],alt:this.title}),i.close(n)}),e(document).off("click",x.hide).on("click",x.hide)}})},k=function(t){var e=this,l=i.open({type:1,id:"LAY_layedit_code",area:"550px",shade:.05,shadeClose:!0,moveType:1,title:"插入代码",skin:"layui-layer-msg",content:['
          ','
        • ','','
          ','","
          ","
        • ",'
        • ','','
          ','',"
          ","
        • ",'
        • ','','',"
        • ","
        "].join(""),success:function(l,n){var o="submit(layedit-code-yes)";a.render("select"),l.find(".layui-btn-primary").on("click",function(){i.close(n),e.focus()}),a.on(o,function(e){i.close(k.index),t&&t(e.field)})}});k.index=l},C={html:'',strong:'',italic:'',underline:'',del:'',"|":'',left:'',center:'',right:'',link:'',unlink:'',face:'',image:'',code:'',help:''},w=new c;t(n,w)});layui.define("jquery",function(e){"use strict";var a=layui.$,l="http://www.layui.com/doc/modules/code.html";e("code",function(e){var t=[];e=e||{},e.elem=a(e.elem||".layui-code"),e.about=!("about"in e)||e.about,e.elem.each(function(){t.push(this)}),layui.each(t.reverse(),function(t,i){var c=a(i),o=c.html();(c.attr("lay-encode")||e.encode)&&(o=o.replace(/&(?!#?[a-zA-Z0-9]+;)/g,"&").replace(//g,">").replace(/'/g,"'").replace(/"/g,""")),c.html('
        1. '+o.replace(/[\r\t\n]+/g,"
        2. ")+"
        "),c.find(">.layui-code-h3")[0]||c.prepend('

        '+(c.attr("lay-title")||e.title||"code")+(e.about?'layui.code':"")+"

        ");var d=c.find(">.layui-code-ol");c.addClass("layui-box layui-code-view"),(c.attr("lay-skin")||e.skin)&&c.addClass("layui-code-"+(c.attr("lay-skin")||e.skin)),(d.find("li").length/100|0)>0&&d.css("margin-left",(d.find("li").length/100|0)+"px"),(c.attr("lay-height")||e.height)&&d.css("max-height",c.attr("lay-height")||e.height)})})}).addcss("modules/code.css","skincodecss"); \ No newline at end of file diff --git a/main/webapp/static/lib/layui/layui.js b/main/webapp/static/lib/layui/layui.js new file mode 100644 index 0000000..35f63b8 --- /dev/null +++ b/main/webapp/static/lib/layui/layui.js @@ -0,0 +1,2 @@ +/** layui-v2.2.6 MIT License By https://www.layui.com */ + ;!function(e){"use strict";var t=document,n={modules:{},status:{},timeout:10,event:{}},o=function(){this.v="2.2.6"},r=function(){var e=t.currentScript?t.currentScript.src:function(){for(var e,n=t.scripts,o=n.length-1,r=o;r>0;r--)if("interactive"===n[r].readyState){e=n[r].src;break}return e||n[o].src}();return e.substring(0,e.lastIndexOf("/")+1)}(),a=function(t){e.console&&console.error&&console.error("Layui hint: "+t)},i="undefined"!=typeof opera&&"[object Opera]"===opera.toString(),u={layer:"modules/layer",laydate:"modules/laydate",laypage:"modules/laypage",laytpl:"modules/laytpl",layim:"modules/layim",layedit:"modules/layedit",form:"modules/form",upload:"modules/upload",tree:"modules/tree",table:"modules/table",element:"modules/element",util:"modules/util",flow:"modules/flow",carousel:"modules/carousel",code:"modules/code",jquery:"modules/jquery",mobile:"modules/mobile","layui.all":"../layui.all"};o.prototype.cache=n,o.prototype.define=function(e,t){var o=this,r="function"==typeof e,a=function(){var e=function(e,t){layui[e]=t,n.status[e]=!0};return"function"==typeof t&&t(function(o,r){e(o,r),n.callback[o]=function(){t(e)}}),this};return r&&(t=e,e=[]),layui["layui.all"]||!layui["layui.all"]&&layui["layui.mobile"]?a.call(o):(o.use(e,a),o)},o.prototype.use=function(e,o,l){function s(e,t){var o="PLaySTATION 3"===navigator.platform?/^complete$/:/^(complete|loaded)$/;("load"===e.type||o.test((e.currentTarget||e.srcElement).readyState))&&(n.modules[f]=t,d.removeChild(v),function r(){return++m>1e3*n.timeout/4?a(f+" is not a valid module"):void(n.status[f]?c():setTimeout(r,4))}())}function c(){l.push(layui[f]),e.length>1?y.use(e.slice(1),o,l):"function"==typeof o&&o.apply(layui,l)}var y=this,p=n.dir=n.dir?n.dir:r,d=t.getElementsByTagName("head")[0];e="string"==typeof e?[e]:e,window.jQuery&&jQuery.fn.on&&(y.each(e,function(t,n){"jquery"===n&&e.splice(t,1)}),layui.jquery=layui.$=jQuery);var f=e[0],m=0;if(l=l||[],n.host=n.host||(p.match(/\/\/([\s\S]+?)\//)||["//"+location.host+"/"])[0],0===e.length||layui["layui.all"]&&u[f]||!layui["layui.all"]&&layui["layui.mobile"]&&u[f])return c(),y;if(n.modules[f])!function g(){return++m>1e3*n.timeout/4?a(f+" is not a valid module"):void("string"==typeof n.modules[f]&&n.status[f]?c():setTimeout(g,4))}();else{var v=t.createElement("script"),h=(u[f]?p+"lay/":/^\{\/\}/.test(y.modules[f])?"":n.base||"")+(y.modules[f]||f)+".js";h=h.replace(/^\{\/\}/,""),v.async=!0,v.charset="utf-8",v.src=h+function(){var e=n.version===!0?n.v||(new Date).getTime():n.version||"";return e?"?v="+e:""}(),d.appendChild(v),!v.attachEvent||v.attachEvent.toString&&v.attachEvent.toString().indexOf("[native code")<0||i?v.addEventListener("load",function(e){s(e,h)},!1):v.attachEvent("onreadystatechange",function(e){s(e,h)}),n.modules[f]=h}return y},o.prototype.getStyle=function(t,n){var o=t.currentStyle?t.currentStyle:e.getComputedStyle(t,null);return o[o.getPropertyValue?"getPropertyValue":"getAttribute"](n)},o.prototype.link=function(e,o,r){var i=this,u=t.createElement("link"),l=t.getElementsByTagName("head")[0];"string"==typeof o&&(r=o);var s=(r||e).replace(/\.|\//g,""),c=u.id="layuicss-"+s,y=0;return u.rel="stylesheet",u.href=e+(n.debug?"?v="+(new Date).getTime():""),u.media="all",t.getElementById(c)||l.appendChild(u),"function"!=typeof o?i:(function p(){return++y>1e3*n.timeout/100?a(e+" timeout"):void(1989===parseInt(i.getStyle(t.getElementById(c),"width"))?function(){o()}():setTimeout(p,100))}(),i)},n.callback={},o.prototype.factory=function(e){if(layui[e])return"function"==typeof n.callback[e]?n.callback[e]:null},o.prototype.addcss=function(e,t,o){return layui.link(n.dir+"css/"+e,t,o)},o.prototype.img=function(e,t,n){var o=new Image;return o.src=e,o.complete?t(o):(o.onload=function(){o.onload=null,t(o)},void(o.onerror=function(e){o.onerror=null,n(e)}))},o.prototype.config=function(e){e=e||{};for(var t in e)n[t]=e[t];return this},o.prototype.modules=function(){var e={};for(var t in u)e[t]=u[t];return e}(),o.prototype.extend=function(e){var t=this;e=e||{};for(var n in e)t[n]||t.modules[n]?a("模块名 "+n+" 已被占用"):t.modules[n]=e[n];return t},o.prototype.router=function(e){var t=this,e=e||location.hash,n={path:[],search:{},hash:(e.match(/[^#](#.*$)/)||[])[1]||""};return/^#\//.test(e)?(e=e.replace(/^#\//,""),n.href="/"+e,e=e.replace(/([^#])(#.*$)/,"$1").split("/")||[],t.each(e,function(e,t){/^\w+=/.test(t)?function(){t=t.split("="),n.search[t[0]]=t[1]}():n.path.push(t)}),n):n},o.prototype.data=function(t,n,o){if(t=t||"layui",o=o||localStorage,e.JSON&&e.JSON.parse){if(null===n)return delete o[t];n="object"==typeof n?n:{key:n};try{var r=JSON.parse(o[t])}catch(a){var r={}}return"value"in n&&(r[n.key]=n.value),n.remove&&delete r[n.key],o[t]=JSON.stringify(r),n.key?r[n.key]:r}},o.prototype.sessionData=function(e,t){return this.data(e,t,sessionStorage)},o.prototype.device=function(t){var n=navigator.userAgent.toLowerCase(),o=function(e){var t=new RegExp(e+"/([^\\s\\_\\-]+)");return e=(n.match(t)||[])[1],e||!1},r={os:function(){return/windows/.test(n)?"windows":/linux/.test(n)?"linux":/iphone|ipod|ipad|ios/.test(n)?"ios":/mac/.test(n)?"mac":void 0}(),ie:function(){return!!(e.ActiveXObject||"ActiveXObject"in e)&&((n.match(/msie\s(\d+)/)||[])[1]||"11")}(),weixin:o("micromessenger")};return t&&!r[t]&&(r[t]=o(t)),r.android=/android/.test(n),r.ios="ios"===r.os,r},o.prototype.hint=function(){return{error:a}},o.prototype.each=function(e,t){var n,o=this;if("function"!=typeof t)return o;if(e=e||[],e.constructor===Object){for(n in e)if(t.call(e[n],n,e[n]))break}else for(n=0;na?1:r departmentList = departmentMapper.selectAll(); + for (Department department:departmentList){ + System.out.println(department); + } + } + @Test + public void countById(){ + System.out.println(employeeMapper.countByDid(1)+"--------------"); + } + @Test + public void selectSalaryByDepartment(){ + EchSalary echSalary = salaryMapper.selectSalaryByDepartment(1,"2018-07"); + System.out.println(echSalary); + System.out.println(); + } + @Test + public void selectAllSalaryByDate(){ + Double ymSalary = salaryMapper.selectAllSalaryByDate("2018-07"); + System.out.println(ymSalary+"--------------------"); + } +} diff --git a/test/java/com/esms/dao/EmployeeCustomVoTest.java b/test/java/com/esms/dao/EmployeeCustomVoTest.java new file mode 100644 index 0000000..dc21ee5 --- /dev/null +++ b/test/java/com/esms/dao/EmployeeCustomVoTest.java @@ -0,0 +1,27 @@ +package com.esms.dao; + +import com.esms.vo.EmployeeCustomVo; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +/** + * @program: ssm + * @Author:admin + * @Description: + * @Date:10:22 2020/02/9 + * @Version: 1.0 + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(locations = "classpath:applicationContext.xml") +public class EmployeeCustomVoTest { + @Autowired + private EmployeeCustomVoMapper employeeCustomVoMapper = null; + @Test + public void test(){ + EmployeeCustomVo employeeCustomVo = employeeCustomVoMapper.selectEmployeeById(1); + System.out.println(employeeCustomVo); + } +} diff --git a/test/java/com/esms/dao/EmployeeTest.java b/test/java/com/esms/dao/EmployeeTest.java new file mode 100644 index 0000000..67fdebe --- /dev/null +++ b/test/java/com/esms/dao/EmployeeTest.java @@ -0,0 +1,21 @@ +package com.esms.dao; + +import com.esms.po.Employee; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(locations = "classpath:applicationContext.xml") +public class EmployeeTest { + @Autowired + private EmployeeMapper employeeMapper = null; + + @Test + public void testselectByPrimaryKey(){ + Employee employee = employeeMapper.selectByPrimaryKey(1); + System.out.println(employee.toString()); + } +} diff --git a/test/java/com/esms/dao/KeyValueTest.java b/test/java/com/esms/dao/KeyValueTest.java new file mode 100644 index 0000000..c24bd7d --- /dev/null +++ b/test/java/com/esms/dao/KeyValueTest.java @@ -0,0 +1,27 @@ +package com.esms.dao; + +import com.esms.po.KeyValue; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +/** + * @program: ssm + * @Author:admin + * @Description: + * @Date:14:52 2020/02/10 + * @Version: 1.0 + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration("classpath:applicationContext.xml") +public class KeyValueTest { + @Autowired + private KeyValueMapper keyValueMapper; + @Test + public void test(){ + KeyValue keyValue = keyValueMapper.selectBykvKey("traffic_pay"); + System.out.println(keyValue.getKvValue()); + } +} diff --git a/test/java/com/esms/dao/MonthlyAttendanceCustomVoTest.java b/test/java/com/esms/dao/MonthlyAttendanceCustomVoTest.java new file mode 100644 index 0000000..8702b41 --- /dev/null +++ b/test/java/com/esms/dao/MonthlyAttendanceCustomVoTest.java @@ -0,0 +1,36 @@ +package com.esms.dao; + +import com.esms.vo.MonthlyAttendanceCustomVo; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import java.util.HashMap; +import java.util.List; + +/** + * @program: ssm + * @Author:admin + * @Description: + * @Date:11:00 2020/02/9 + * @Version: 1.0 + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration("classpath:applicationContext.xml") +public class MonthlyAttendanceCustomVoTest { + @Autowired + private MonthlyAttendanceCustomVoMapper customVoMapper = null; + @Test + public void test(){ + HashMap map = new HashMap(); + map.put("e_account",""); + map.put("d_id",4); + map.put("attendance_time",""); + + List monthlyAttendanceCustomVo + = customVoMapper.selectMonthlyAttendanceCustomVoMapperByeAccountAnddIdAndTime(map); + System.out.println(monthlyAttendanceCustomVo.toString()); + } +} diff --git a/test/java/com/esms/dao/MothlyAttendanceTest.java b/test/java/com/esms/dao/MothlyAttendanceTest.java new file mode 100644 index 0000000..e4f5ce0 --- /dev/null +++ b/test/java/com/esms/dao/MothlyAttendanceTest.java @@ -0,0 +1,34 @@ +package com.esms.dao; + +import com.esms.po.MonthlyAttendance; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import java.util.HashMap; +import java.util.List; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(locations = "classpath:applicationContext.xml") +public class MothlyAttendanceTest { + @Autowired + private MonthlyAttendanceMapper monthlyAttendanceMapper = null; + + @Test +// 级联查询懒加载 + public void testSelectByPrimaryKey(){ + MonthlyAttendance monthlyAttendance = monthlyAttendanceMapper.selectByPrimaryKey(1); + System.out.println(monthlyAttendance.toString()); + } + + @Test + public void test1(){ + HashMap map = new HashMap(); + map.put("eId",11); + map.put("attendanceTime","2018"); + List list = monthlyAttendanceMapper.selectMonthlyAttendanceMapperByeEidAndTime(map); + System.out.println(list.size()); + } +} diff --git a/test/java/com/esms/dao/SalaryTest.java b/test/java/com/esms/dao/SalaryTest.java new file mode 100644 index 0000000..ae50f75 --- /dev/null +++ b/test/java/com/esms/dao/SalaryTest.java @@ -0,0 +1,34 @@ +package com.esms.dao; + +import com.esms.po.Salary; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(locations = "classpath:applicationContext.xml") +public class SalaryTest { + @Autowired + private SalaryMapper salaryMapper = null; + @Test + public void testSelectByPrimaryKey(){ + Salary salary = salaryMapper.selectByPrimaryKey(1); + System.out.println(salary.toString()); + } + + @Test + public void selectByEaccountDIdDateTest(){ + Map map = new HashMap(); + map.put("eAccount",1); + map.put("dId",1); + map.put("sTime",""); + List salaryList = salaryMapper.selectByEaccountDIdDate(map); + System.out.println(salaryList.toString()); + } +} diff --git a/test/java/com/esms/service/EchartsServiceTest.java b/test/java/com/esms/service/EchartsServiceTest.java new file mode 100644 index 0000000..d6242ff --- /dev/null +++ b/test/java/com/esms/service/EchartsServiceTest.java @@ -0,0 +1,57 @@ +package com.esms.service; + +import com.esms.dao.SalaryMapper; +import com.esms.vo.EchMonthSalary; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import java.text.SimpleDateFormat; +import java.util.*; + +/** + * @program: ssm + * @Author:方宏泰 + * @Description: + * @Date:8:54 2020/02/9 + * @Version: 1.0 + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(locations = "classpath:applicationContext.xml") +public class EchartsServiceTest { + @Autowired + private SalaryMapper salaryMapper; + + @Test + public void getMonthSalaryTest(){ + List echMonthSalaryList = new ArrayList(); + Map> stringListMap = new HashMap>(); + SimpleDateFormat sdf = new SimpleDateFormat("yyyy"); + Integer year = Integer.parseInt(sdf.format(new Date())); //当前年 + //近三年(不包括本年) + String date = null; + for(int i = year-3;i salary = new ArrayList(); + for(int month = 1; month<=12;month++) { + if(month<10){ + date = i + "-0"+month; + } + else { + date = i + "-"+month; + } + Double ymSalary = salaryMapper.selectAllSalaryByDate(date); //查找某年某一月的总工资 + if (ymSalary == null) { + ymSalary = 0.00; + } + salary.add(ymSalary); + } + echMonthSalary.setSalary(salary); + echMonthSalaryList.add(echMonthSalary); + } + stringListMap.put("list",echMonthSalaryList); + } +} diff --git a/test/java/com/esms/service/MonthlyAttendanceCustomVoTest.java b/test/java/com/esms/service/MonthlyAttendanceCustomVoTest.java new file mode 100644 index 0000000..c6faa37 --- /dev/null +++ b/test/java/com/esms/service/MonthlyAttendanceCustomVoTest.java @@ -0,0 +1,31 @@ +package com.esms.service; + +import com.esms.exception.CustomException; +import com.esms.service.impl.SalaryServiceImpl; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +/** + * @program: ssm + * @Author:admin + * @Description: + * @Date:10:33 2020/02/10 + * @Version: 1.0 + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration("classpath:applicationContext.xml") +public class MonthlyAttendanceCustomVoTest { + @Autowired + private SalaryServiceImpl salaryService = null; + @Test + public void test(){ + try { + salaryService.insertSalaryByAcountAndDate("4","2017-06"); + } catch (CustomException e) { + e.printStackTrace(); + } + } +} diff --git a/test/java/com/esms/service/SalaryServiceTest.java b/test/java/com/esms/service/SalaryServiceTest.java new file mode 100644 index 0000000..be46d9f --- /dev/null +++ b/test/java/com/esms/service/SalaryServiceTest.java @@ -0,0 +1,32 @@ +package com.esms.service; + +import com.esms.exception.CustomException; +import com.esms.service.impl.SalaryServiceImpl; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +/** + * @program: ssm + * @Author:admin + * @Description: + * @Date:0:30 2020/02/12 + * @Version: 1.0 + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration("classpath:applicationContext.xml") +public class SalaryServiceTest { + @Autowired + private SalaryServiceImpl salaryService = null; + @Test + public void testInsertSalaryByAcountAndDate(){ + try { + salaryService.insertSalaryByAcountAndDate("1","2018-08"); + } catch (CustomException e) { + e.printStackTrace(); + } + } + +} diff --git a/test/java/com/esms/service/TestLoginService.java b/test/java/com/esms/service/TestLoginService.java new file mode 100644 index 0000000..74049f5 --- /dev/null +++ b/test/java/com/esms/service/TestLoginService.java @@ -0,0 +1,36 @@ +package com.esms.service; + +import com.esms.exception.CustomException; +import com.esms.service.impl.LoginServiceImpl; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +/** + * @program: ssm + * @Author:admin + * @Description: + * @Date:2:39 2020/02/6 + * @Version: 1.0 + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration("classpath:applicationContext.xml") +public class TestLoginService { + @Autowired + private LoginServiceImpl loginService = null; + @Test + public void testFindEmployeeByIdAndPassword() throws Exception { + loginService.findEmployeeByIdAndPassword("1","1"); + } + + @Test + public void testFindSystemManagerByIdAndPassword() { + try { + loginService.findSystemManagerByIdAndPassword("admin","1"); + } catch (CustomException e) { + System.out.println(e.getMessage()); + } + } +} diff --git a/test/java/com/esms/utils/JXLUtilsTest.java b/test/java/com/esms/utils/JXLUtilsTest.java new file mode 100644 index 0000000..2041cef --- /dev/null +++ b/test/java/com/esms/utils/JXLUtilsTest.java @@ -0,0 +1,56 @@ +package com.esms.utils; + +import com.esms.dao.MonthlyAttendanceMapper; +import com.esms.dao.SalaryMapper; +import com.esms.po.MonthlyAttendance; +import com.esms.po.Salary; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import java.io.File; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.List; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(locations = "classpath:applicationContext.xml") +public class JXLUtilsTest { + @Autowired + private SalaryMapper salaryMapper; + @Autowired + private MonthlyAttendanceMapper monthlyAttendanceMapper; + @Before + public void before() { + } + @Test + public void readMonthlyAttendanceTest() throws Exception{ + File file = new File("D://ESMSexcel/Monthly_attendance_table/考勤表_2018-7.xls"); + List monthlyAttendanceList = JXLUtils.readMonthlyAttendanceTable(file); + for (MonthlyAttendance monthlyAttendance:monthlyAttendanceList){ + monthlyAttendanceMapper.insert(monthlyAttendance); + } + } + @Test + public void readRessiueTest(){ + DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); + File file = new File("D://ESMSexcel/monthly_reissue_table/补发金额表_2018-7.xls"); + List salaryList = JXLUtils.readReissueTable(file); + for(Salary salary:salaryList){ + Salary temp = salaryMapper.selectByEidAndTime(salary.getEmployee().geteId(),dateFormat.format(salary.getsTime())); + //如果数据库表salay不存在此记录,直接插入 + if(temp == null ){ + salaryMapper.insertSelective(salary); + }else { + //如果数据库表salay已存在此记录,修改补发金额数据 + salary.setsId(temp.getsId()); + salaryMapper.updateByPrimaryKeySelective(salary); + } + + } + } + +} diff --git a/test/java/com/esms/utils/MD5UtilsTest.java b/test/java/com/esms/utils/MD5UtilsTest.java new file mode 100644 index 0000000..ac53fba --- /dev/null +++ b/test/java/com/esms/utils/MD5UtilsTest.java @@ -0,0 +1,14 @@ +package com.esms.utils; + +import org.junit.Test; + +public class MD5UtilsTest { + @Test + public void testMD5Encode(){ + // 数据源及是,密码 + String originString = "123" ; + // 静态调用,MD5Utils.encodeByMD5(),返回的字符串为md5 32位字符,及加密后的密码 + String encodeByMD5 = MD5Utils.encodeByMD5(originString); + System.out.println(encodeByMD5); + } +}