Repository: newbee-ltd/newbee-mall Branch: master Commit: a069069b0702 Files: 329 Total size: 7.9 MB Directory structure: gitextract_szhq2hcl/ ├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── docs/ │ ├── API.md │ ├── DEVELOPMENT.md │ └── FAQ.md ├── pom.xml └── src/ └── main/ ├── java/ │ └── ltd/ │ └── newbee/ │ └── mall/ │ ├── NewBeeMallApplication.java │ ├── common/ │ │ ├── Constants.java │ │ ├── IndexConfigTypeEnum.java │ │ ├── NewBeeMallCategoryLevelEnum.java │ │ ├── NewBeeMallException.java │ │ ├── NewBeeMallOrderStatusEnum.java │ │ ├── PayStatusEnum.java │ │ ├── PayTypeEnum.java │ │ └── ServiceResultEnum.java │ ├── config/ │ │ └── NeeBeeMallWebMvcConfigurer.java │ ├── controller/ │ │ ├── admin/ │ │ │ ├── AdminController.java │ │ │ ├── NewBeeMallCarouselController.java │ │ │ ├── NewBeeMallGoodsCategoryController.java │ │ │ ├── NewBeeMallGoodsController.java │ │ │ ├── NewBeeMallGoodsIndexConfigController.java │ │ │ ├── NewBeeMallOrderController.java │ │ │ └── NewBeeMallUserController.java │ │ ├── common/ │ │ │ ├── CommonController.java │ │ │ ├── ErrorPageController.java │ │ │ ├── NewBeeMallExceptionHandler.java │ │ │ └── UploadController.java │ │ ├── mall/ │ │ │ ├── GoodsController.java │ │ │ ├── IndexController.java │ │ │ ├── OrderController.java │ │ │ ├── PersonalController.java │ │ │ └── ShoppingCartController.java │ │ └── vo/ │ │ ├── NewBeeMallGoodsDetailVO.java │ │ ├── NewBeeMallIndexCarouselVO.java │ │ ├── NewBeeMallIndexCategoryVO.java │ │ ├── NewBeeMallIndexConfigGoodsVO.java │ │ ├── NewBeeMallOrderDetailVO.java │ │ ├── NewBeeMallOrderItemVO.java │ │ ├── NewBeeMallOrderListVO.java │ │ ├── NewBeeMallSearchGoodsVO.java │ │ ├── NewBeeMallShoppingCartItemVO.java │ │ ├── NewBeeMallUserVO.java │ │ ├── SearchPageCategoryVO.java │ │ ├── SecondLevelCategoryVO.java │ │ └── ThirdLevelCategoryVO.java │ ├── dao/ │ │ ├── AdminUserMapper.java │ │ ├── CarouselMapper.java │ │ ├── GoodsCategoryMapper.java │ │ ├── IndexConfigMapper.java │ │ ├── MallUserMapper.java │ │ ├── NewBeeMallGoodsMapper.java │ │ ├── NewBeeMallOrderItemMapper.java │ │ ├── NewBeeMallOrderMapper.java │ │ └── NewBeeMallShoppingCartItemMapper.java │ ├── entity/ │ │ ├── AdminUser.java │ │ ├── Carousel.java │ │ ├── GoodsCategory.java │ │ ├── IndexConfig.java │ │ ├── MallUser.java │ │ ├── NewBeeMallGoods.java │ │ ├── NewBeeMallOrder.java │ │ ├── NewBeeMallOrderItem.java │ │ ├── NewBeeMallShoppingCartItem.java │ │ └── StockNumDTO.java │ ├── interceptor/ │ │ ├── AdminLoginInterceptor.java │ │ ├── NewBeeMallCartNumberInterceptor.java │ │ └── NewBeeMallLoginInterceptor.java │ ├── service/ │ │ ├── AdminUserService.java │ │ ├── NewBeeMallCarouselService.java │ │ ├── NewBeeMallCategoryService.java │ │ ├── NewBeeMallGoodsService.java │ │ ├── NewBeeMallIndexConfigService.java │ │ ├── NewBeeMallOrderService.java │ │ ├── NewBeeMallShoppingCartService.java │ │ ├── NewBeeMallUserService.java │ │ └── impl/ │ │ ├── AdminUserServiceImpl.java │ │ ├── NewBeeMallCarouselServiceImpl.java │ │ ├── NewBeeMallCategoryServiceImpl.java │ │ ├── NewBeeMallGoodsServiceImpl.java │ │ ├── NewBeeMallIndexConfigServiceImpl.java │ │ ├── NewBeeMallOrderServiceImpl.java │ │ ├── NewBeeMallShoppingCartServiceImpl.java │ │ └── NewBeeMallUserServiceImpl.java │ └── util/ │ ├── BeanUtil.java │ ├── JsonUtil.java │ ├── MD5Util.java │ ├── NewBeeMallUtils.java │ ├── NumberUtil.java │ ├── PageQueryUtil.java │ ├── PageResult.java │ ├── PatternUtil.java │ ├── Result.java │ ├── ResultGenerator.java │ └── SystemUtil.java └── resources/ ├── application.properties ├── mapper/ │ ├── AdminUserMapper.xml │ ├── CarouselMapper.xml │ ├── GoodsCategoryMapper.xml │ ├── IndexConfigMapper.xml │ ├── MallUserMapper.xml │ ├── NewBeeMallGoodsMapper.xml │ ├── NewBeeMallOrderItemMapper.xml │ ├── NewBeeMallOrderMapper.xml │ └── NewBeeMallShoppingCartItemMapper.xml ├── newbee_mall_schema.sql ├── static/ │ ├── admin/ │ │ ├── dist/ │ │ │ ├── css/ │ │ │ │ ├── adminlte.css │ │ │ │ └── main.css │ │ │ ├── fonts/ │ │ │ │ └── FontAwesome.otf │ │ │ └── js/ │ │ │ ├── adminlte.js │ │ │ ├── demo.js │ │ │ ├── newbee_mall_carousel.js │ │ │ ├── newbee_mall_category.js │ │ │ ├── newbee_mall_goods.js │ │ │ ├── newbee_mall_goods_edit.js │ │ │ ├── newbee_mall_index_config.js │ │ │ ├── newbee_mall_order.js │ │ │ ├── newbee_mall_user.js │ │ │ ├── plugins/ │ │ │ │ ├── bootstrap/ │ │ │ │ │ ├── css/ │ │ │ │ │ │ ├── bootstrap-grid.css │ │ │ │ │ │ ├── bootstrap-reboot.css │ │ │ │ │ │ └── bootstrap.css │ │ │ │ │ └── js/ │ │ │ │ │ ├── bootstrap.bundle.js │ │ │ │ │ └── bootstrap.js │ │ │ │ ├── chart.js │ │ │ │ ├── chart.js2 │ │ │ │ ├── chartjs2/ │ │ │ │ │ ├── Chart.bundle.js │ │ │ │ │ ├── Chart.js │ │ │ │ │ └── docs/ │ │ │ │ │ ├── axes/ │ │ │ │ │ │ ├── cartesian/ │ │ │ │ │ │ │ ├── category.html │ │ │ │ │ │ │ ├── index.html │ │ │ │ │ │ │ ├── linear.html │ │ │ │ │ │ │ ├── logarithmic.html │ │ │ │ │ │ │ └── time.html │ │ │ │ │ │ ├── index.html │ │ │ │ │ │ ├── labelling.html │ │ │ │ │ │ ├── radial/ │ │ │ │ │ │ │ ├── index.html │ │ │ │ │ │ │ └── linear.html │ │ │ │ │ │ └── styling.html │ │ │ │ │ ├── charts/ │ │ │ │ │ │ ├── area.html │ │ │ │ │ │ ├── bar.html │ │ │ │ │ │ ├── bubble.html │ │ │ │ │ │ ├── doughnut.html │ │ │ │ │ │ ├── index.html │ │ │ │ │ │ ├── line.html │ │ │ │ │ │ ├── mixed.html │ │ │ │ │ │ ├── polar.html │ │ │ │ │ │ ├── radar.html │ │ │ │ │ │ └── scatter.html │ │ │ │ │ ├── configuration/ │ │ │ │ │ │ ├── animations.html │ │ │ │ │ │ ├── elements.html │ │ │ │ │ │ ├── index.html │ │ │ │ │ │ ├── layout.html │ │ │ │ │ │ ├── legend.html │ │ │ │ │ │ ├── title.html │ │ │ │ │ │ └── tooltip.html │ │ │ │ │ ├── developers/ │ │ │ │ │ │ ├── api.html │ │ │ │ │ │ ├── axes.html │ │ │ │ │ │ ├── charts.html │ │ │ │ │ │ ├── contributing.html │ │ │ │ │ │ ├── index.html │ │ │ │ │ │ ├── plugins.html │ │ │ │ │ │ └── updates.html │ │ │ │ │ ├── general/ │ │ │ │ │ │ ├── colors.html │ │ │ │ │ │ ├── device-pixel-ratio.md │ │ │ │ │ │ ├── fonts.html │ │ │ │ │ │ ├── index.html │ │ │ │ │ │ ├── interactions/ │ │ │ │ │ │ │ ├── events.html │ │ │ │ │ │ │ ├── index.html │ │ │ │ │ │ │ └── modes.html │ │ │ │ │ │ ├── options.html │ │ │ │ │ │ └── responsive.html │ │ │ │ │ ├── getting-started/ │ │ │ │ │ │ ├── index.html │ │ │ │ │ │ ├── installation.html │ │ │ │ │ │ ├── integration.html │ │ │ │ │ │ └── usage.html │ │ │ │ │ ├── gitbook/ │ │ │ │ │ │ ├── fonts/ │ │ │ │ │ │ │ └── fontawesome/ │ │ │ │ │ │ │ └── FontAwesome.otf │ │ │ │ │ │ ├── gitbook-plugin-anchorjs/ │ │ │ │ │ │ │ └── anchor-style.js │ │ │ │ │ │ ├── gitbook-plugin-chartjs/ │ │ │ │ │ │ │ ├── Chart.bundle.js │ │ │ │ │ │ │ ├── Chart.js │ │ │ │ │ │ │ ├── chartjs-plugin-deferred.js │ │ │ │ │ │ │ └── style.css │ │ │ │ │ │ ├── gitbook-plugin-fontsettings/ │ │ │ │ │ │ │ ├── fontsettings.js │ │ │ │ │ │ │ └── website.css │ │ │ │ │ │ ├── gitbook-plugin-ga/ │ │ │ │ │ │ │ └── plugin.js │ │ │ │ │ │ ├── gitbook-plugin-highlight/ │ │ │ │ │ │ │ ├── ebook.css │ │ │ │ │ │ │ └── website.css │ │ │ │ │ │ ├── gitbook-plugin-search-plus/ │ │ │ │ │ │ │ ├── search.css │ │ │ │ │ │ │ └── search.js │ │ │ │ │ │ ├── gitbook-plugin-sharing/ │ │ │ │ │ │ │ └── buttons.js │ │ │ │ │ │ ├── gitbook.js │ │ │ │ │ │ ├── style.css │ │ │ │ │ │ └── theme.js │ │ │ │ │ ├── index.html │ │ │ │ │ ├── notes/ │ │ │ │ │ │ ├── comparison.html │ │ │ │ │ │ ├── extensions.html │ │ │ │ │ │ ├── index.html │ │ │ │ │ │ └── license.html │ │ │ │ │ ├── search_plus_index.json │ │ │ │ │ └── style.css │ │ │ │ ├── font-awesome/ │ │ │ │ │ ├── css/ │ │ │ │ │ │ └── font-awesome.css │ │ │ │ │ └── fonts/ │ │ │ │ │ └── FontAwesome.otf │ │ │ │ ├── jquery/ │ │ │ │ │ ├── core.js │ │ │ │ │ ├── jquery.js │ │ │ │ │ └── jquery.slim.js │ │ │ │ ├── login-bg-particles.js │ │ │ │ ├── particles.js │ │ │ │ └── popper/ │ │ │ │ ├── esm/ │ │ │ │ │ ├── popper-utils.js │ │ │ │ │ └── popper.js │ │ │ │ ├── popper-utils.js │ │ │ │ ├── popper.js │ │ │ │ └── umd/ │ │ │ │ ├── popper-utils.js │ │ │ │ └── popper.js │ │ │ ├── profile.js │ │ │ ├── public.js │ │ │ └── test.js │ │ └── plugins/ │ │ ├── ajaxupload/ │ │ │ └── ajaxupload.js │ │ ├── bootstrap/ │ │ │ ├── css/ │ │ │ │ ├── bootstrap-grid.css │ │ │ │ ├── bootstrap-reboot.css │ │ │ │ └── bootstrap.css │ │ │ └── js/ │ │ │ ├── bootstrap.bundle.js │ │ │ └── bootstrap.js │ │ ├── jqgrid-5.7.0/ │ │ │ ├── grid.locale-cn.js │ │ │ ├── ui.jqgrid-bootstrap-ui.css │ │ │ ├── ui.jqgrid-bootstrap.css │ │ │ ├── ui.jqgrid-bootstrap4.css │ │ │ └── ui.jqgrid.css │ │ ├── jquery/ │ │ │ ├── core.js │ │ │ ├── jquery.js │ │ │ └── jquery.slim.js │ │ ├── select2/ │ │ │ ├── i18n/ │ │ │ │ ├── az.js │ │ │ │ ├── bg.js │ │ │ │ ├── ca.js │ │ │ │ ├── cs.js │ │ │ │ ├── da.js │ │ │ │ ├── de.js │ │ │ │ ├── en.js │ │ │ │ ├── es.js │ │ │ │ ├── et.js │ │ │ │ ├── eu.js │ │ │ │ ├── fa.js │ │ │ │ ├── fi.js │ │ │ │ ├── fr.js │ │ │ │ ├── gl.js │ │ │ │ ├── he.js │ │ │ │ ├── hi.js │ │ │ │ ├── hr.js │ │ │ │ ├── hu.js │ │ │ │ ├── id.js │ │ │ │ ├── is.js │ │ │ │ ├── it.js │ │ │ │ ├── ko.js │ │ │ │ ├── lt.js │ │ │ │ ├── lv.js │ │ │ │ ├── mk.js │ │ │ │ ├── nb.js │ │ │ │ ├── nl.js │ │ │ │ ├── pl.js │ │ │ │ ├── pt-BR.js │ │ │ │ ├── pt.js │ │ │ │ ├── ro.js │ │ │ │ ├── ru.js │ │ │ │ ├── sk.js │ │ │ │ ├── sr.js │ │ │ │ ├── sv.js │ │ │ │ ├── th.js │ │ │ │ ├── tr.js │ │ │ │ ├── uk.js │ │ │ │ ├── vi.js │ │ │ │ ├── zh-CN.js │ │ │ │ └── zh-TW.js │ │ │ ├── select2.css │ │ │ ├── select2.full.js │ │ │ └── select2.js │ │ └── wangeditor-5.1.23/ │ │ ├── index.js │ │ └── style.css │ └── mall/ │ ├── css/ │ │ ├── bootstrap-modal.css │ │ ├── common.css │ │ └── iconfont.css │ ├── js/ │ │ ├── bootstrap3.js │ │ ├── detail.js │ │ ├── index.js │ │ ├── jquery-1.8.0.js │ │ └── search.js │ └── styles/ │ ├── addresses.css │ ├── cart.css │ ├── detail.css │ ├── header.css │ ├── index.css │ ├── login.css │ ├── my-orders.css │ ├── order-detail.css │ ├── order-settle.css │ ├── pay-select.css │ ├── pay.css │ ├── personal.css │ └── search.css └── templates/ ├── admin/ │ ├── footer.html │ ├── header.html │ ├── index.html │ ├── login.html │ ├── newbee_mall_carousel.html │ ├── newbee_mall_category.html │ ├── newbee_mall_goods.html │ ├── newbee_mall_goods_edit.html │ ├── newbee_mall_index_config.html │ ├── newbee_mall_order.html │ ├── newbee_mall_user.html │ ├── profile.html │ └── sidebar.html ├── error/ │ ├── error.html │ ├── error_400.html │ ├── error_404.html │ └── error_5xx.html └── mall/ ├── alipay.html ├── cart.html ├── detail.html ├── footer.html ├── header.html ├── index.html ├── login.html ├── my-orders.html ├── order-detail.html ├── order-settle.html ├── pay-select.html ├── personal-sidebar.html ├── personal.html ├── register.html ├── search.html └── wxpay.html ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitattributes ================================================ *.js linguist-language=java *.css linguist-language=java ================================================ FILE: .gitignore ================================================ /target/ !.mvn/wrapper/maven-wrapper.jar ### STS ### .apt_generated .classpath .factorypath .project .settings .springBeans .sts4-cache ### IntelliJ IDEA ### .idea *.iws *.iml *.ipr ### NetBeans ### /nbproject/private/ /build/ /nbbuild/ /dist/ /nbdist/ /.nb-gradle/ ================================================ FILE: LICENSE ================================================ GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . ================================================ FILE: README.md ================================================ ![newbee-logo](https://newbee-mall.oss-cn-beijing.aliyuncs.com/poster/product/newbee-logo.png?x-oss-process=image/resize,h_240,w_480) ![Build Status](https://img.shields.io/badge/build-passing-green.svg) ![Version 1.0.0](https://img.shields.io/badge/version-1.0.0-yellow.svg) [![License](https://img.shields.io/badge/license-GPL3.0-blue.svg)](https://github.com/newbee-ltd/newbee-mall/blob/master/LICENSE) newbee-mall 项目是一套电商系统,包括 newbee-mall 商城系统及 newbee-mall-admin 商城后台管理系统,基于 Spring Boot 及相关技术栈开发。 前台商城系统包含首页门户、商品分类、新品上线、首页轮播、商品推荐、商品搜索、商品展示、购物车、订单结算、订单流程、个人订单管理、会员中心、帮助中心等模块。 后台管理系统包含数据面板、轮播图管理、商品管理、订单管理、会员管理、分类管理、设置等模块。 当前分支的 Spring Boot 版本为 2.7.5,想要学习和使用其它版本可以直接点击下方的分支名称跳转至对应的仓库分支中。 | 分支名称 | Spring Boot Version | | ------------------------------------------------------------ | ------------------- | | [spring-boot-2.3.7](https://github.com/newbee-ltd/newbee-mall/tree/spring-boot-2.3.7) | 2.3.7-RELEASE | | [spring-boot-2.6.x](https://github.com/newbee-ltd/newbee-mall/tree/spring-boot-2.6.x) | 2.6.3 | | [main](https://github.com/newbee-ltd/newbee-mall) | 2.7.5 | | [spring-boot-3.x](https://github.com/newbee-ltd/newbee-mall/tree/spring-boot-3.x) | 3.1.0 | 新蜂商城线上预览地址:[http://mall.newbee.ltd](http://mall.newbee.ltd?from=github),账号可自行注册。 **坚持不易,如果觉得项目还不错的话可以给项目一个 Star 吧,也是对我自 2019 年开始一直更新这个项目的一种鼓励啦,谢谢各位的支持。** ![newbee-mall-info](https://newbee-mall.oss-cn-beijing.aliyuncs.com/poster/store/newbee-mall-star.png) - newbee-mall 对新手开发者十分友好,无需复杂的操作步骤,**仅需 2 秒就可以启动这个完整的商城项目;** - newbee-mall **也是一个企业级别的 Spring Boot 大型项目,对于各个阶段的 Java 开发者都是极佳的选择;** - 你可以把它作为 Spring Boot 技术栈的综合实践项目,**newbee-mall 足够符合要求,且代码开源、功能完备、流程完整、页面交互美观;** - 技术栈新颖且知识点丰富,学习后可以提升大家对于知识的理解和掌握,**可以进一步提升你的市场竞争力;** - 对于部分求职中的 Java 开发者,**你也可以将该项目放入求职简历中以丰富你的工作履历;** - **newbee-mall 还有一些不完善的地方,鄙人才疏学浅,望见谅;** - **有任何问题都可以反馈给我,我会尽量完善该项目。** ![](https://raw.githubusercontent.com/newbee-ltd/newbee-mall-vue-app/master/static-files/newbee-mall.png) ## newbee-mall (新蜂商城)系列项目概览 ![newbee-mall-course-2022](https://github.com/newbee-ltd/newbee-mall-cloud/raw/main/static-files/newbee-mall-course-2022.png) | 项目名称 | 仓库地址 | 备注 | | :------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | | newbee-mall | [newbee-mall in GitHub](https://github.com/newbee-ltd/newbee-mall)
[newbee-mall in Gitee](https://gitee.com/newbee-ltd/newbee-mall) | 初始版本、Spring Boot、Thymeleaf、MyBatis、MySQL | | newbee-mall-plus | [newbee-mall-plus in GitHub](https://github.com/newbee-ltd/newbee-mall-plus)
[newbee-mall-plus in Gitee](https://gitee.com/newbee-ltd/newbee-mall-plus) | 升级版本、优惠券、秒杀、支付、Spring Boot、Thymeleaf、MyBatis、MySQL、Redis | | newbee-mall-cloud | [newbee-mall-cloud in GitHub](https://github.com/newbee-ltd/newbee-mall-cloud)
[newbee-mall-cloud in Gitee](https://gitee.com/newbee-ltd/newbee-mall-cloud) | 微服务版本、分布式事务、Spring Cloud Alibaba、Nacos、Sentinel、OpenFeign、Seata | | newbee-mall-api | [newbee-mall-api in GitHub](https://github.com/newbee-ltd/newbee-mall-api)
[newbee-mall-api in Gitee](https://gitee.com/newbee-ltd/newbee-mall-api) | 前后端分离、Spring Boot、MyBatis、Swagger、MySQL | | newbee-mall-api-go | [newbee-mall-api-go in GitHub](https://github.com/newbee-ltd/newbee-mall-api-go)
[newbee-mall-api-go in Gitee](https://gitee.com/newbee-ltd/newbee-mall-api-go) | 前后端分离、Go、Gin、MySQL | | newbee-mall-vue-app | [newbee-mall-vue-app in GitHub](https://github.com/newbee-ltd/newbee-mall-vue-app)
[newbee-mall-vue-app in Gitee](https://gitee.com/newbee-ltd/newbee-mall-vue-app) | 前后端分离、Vue2、Vant | | newbee-mall-vue3-app | [newbee-mall-vue3-app in GitHub](https://github.com/newbee-ltd/newbee-mall-vue3-app)
[newbee-mall-vue3-app in Gitee](https://gitee.com/newbee-ltd/newbee-mall-vue3-app) | 前后端分离、Vue3、Vue-Router4、Vuex4、Vant3 | | vue3-admin | [vue3-admin in GitHub](https://github.com/newbee-ltd/vue3-admin)
[vue3-admin in Gitee](https://gitee.com/newbee-ltd/vue3-admin) | 前后端分离、Vue3、Element-Plus、Vue-Router4、Vite | > 更多 Spring Boot 实战项目可以关注十三的另一个代码仓库 [spring-boot-projects](https://github.com/ZHENFENG13/spring-boot-projects),该仓库中主要是 Spring Boot 的入门学习教程以及一些常用的 Spring Boot 实战项目教程,包括 Spring Boot 使用的各种示例代码,同时也包括一些实战项目的项目源码和效果展示,实战项目包括基本的 web 开发以及目前大家普遍使用的前后端分离实践项目等,后续会根据大家的反馈继续增加一些实战项目源码,摆脱各种 hello world 入门案例的束缚,真正的掌握 Spring Boot 开发。 关注公众号:**程序员十三**,回复"勾搭"进群交流。 ![wx-gzh](https://newbee-mall.oss-cn-beijing.aliyuncs.com/wx-gzh/%E7%A8%8B%E5%BA%8F%E5%91%98%E5%8D%81%E4%B8%89-%E5%85%AC%E4%BC%97%E5%8F%B7.png) ## 项目演示 - [视频1:商城项目总览](https://edu.csdn.net/course/play/26258/326466) - [视频2:商城系统介绍](https://edu.csdn.net/course/play/26258/326467) - [视频3:商城后台管理系统介绍](https://edu.csdn.net/course/play/26258/328801) ## 开发及部署文档 - [**Spring Boot 大型线上商城项目实战教程**](https://juejin.cn/book/6844733814074245133?suid=1996368849416216&source=android) - [项目须知和课程约定](https://juejin.cn/book/6844733814074245133?suid=1996368849416216&source=android) - [2021年12月小册全新优化升级](https://juejin.cn/book/6844733814074245133?suid=1996368849416216&source=android) - [技术选型之 Spring Boot](https://juejin.cn/book/6844733814074245133?suid=1996368849416216&source=android) - [前期准备工作及基础环境搭建](https://juejin.cn/book/6844733814074245133?suid=1996368849416216&source=android) - [Spring Boot 项目初体验--项目搭建及启动](https://juejin.cn/book/6844733814074245133?suid=1996368849416216&source=android) - [Spring Boot 之 Web 开发讲解](https://juejin.cn/book/6844733814074245133?suid=1996368849416216&source=android) - [Thymeleaf 模板引擎技术介绍及整合](https://juejin.cn/book/6844733814074245133?suid=1996368849416216&source=android) - [Thymeleaf 语法详解及编码实践](https://juejin.cn/book/6844733814074245133?suid=1996368849416216&source=android) - [Spring Boot 实践之数据库操作](https://juejin.cn/book/6844733814074245133?suid=1996368849416216&source=android) - [Spring Boot 实践之整合 Mybatis 操作数据库](https://juejin.cn/book/6844733814074245133?suid=1996368849416216&source=android) - [项目初体验:启动和使用新蜂商城](https://juejin.cn/book/6844733814074245133?suid=1996368849416216&source=android) - [新蜂商城功能模块和流程设计详解](https://juejin.cn/book/6844733814074245133?suid=1996368849416216&source=android) - [前端页面设计及技术选型](https://juejin.cn/book/6844733814074245133?suid=1996368849416216&source=android) - [页面布局制作及跳转逻辑实现](https://juejin.cn/book/6844733814074245133?suid=1996368849416216&source=android) - [Spring Boot 实现验证码功能](https://juejin.cn/book/6844733814074245133?suid=1996368849416216&source=android) - [新蜂商城后台管理系统登录功能实现](https://juejin.cn/book/6844733814074245133?suid=1996368849416216&source=android) - [登陆拦截器设置并完善身份验证](https://juejin.cn/book/6844733814074245133?suid=1996368849416216&source=android) - [通用分页功能设计与开发实践](https://juejin.cn/book/6844733814074245133?suid=1996368849416216&source=android) - [jqGrid 插件整合制作分页效果](https://juejin.cn/book/6844733814074245133?suid=1996368849416216&source=android) - [Spring Boot 实践之文件上传处理-1](https://juejin.cn/book/6844733814074245133?suid=1996368849416216&source=android) - [Spring Boot 实践之文件上传处理-2](https://juejin.cn/book/6844733814074245133?suid=1996368849416216&source=android) - [新蜂商城轮播图管理模块开发](https://juejin.cn/book/6844733814074245133?suid=1996368849416216&source=android) - [新蜂商城分类管理模块开发-1](https://juejin.cn/book/6844733814074245133?suid=1996368849416216&source=android) - [新蜂商城分类管理模块开发-2](https://juejin.cn/book/6844733814074245133?suid=1996368849416216&source=android) - [新蜂商城商品类目三级联动功能实现](https://juejin.cn/book/6844733814074245133?suid=1996368849416216&source=android) - [富文本编辑器 wangEditor 介绍及整合详解](https://juejin.cn/book/6844733814074245133?suid=1996368849416216&source=android) - [新蜂商城商品编辑页面制作](https://juejin.cn/book/6844733814074245133?suid=1996368849416216&source=android) - [新蜂商城商品添加功能实现](https://juejin.cn/book/6844733814074245133?suid=1996368849416216&source=android) - [新蜂商城商品管理模块功能实现](https://juejin.cn/book/6844733814074245133?suid=1996368849416216&source=android) - [新蜂商城首页制作-1](https://juejin.cn/book/6844733814074245133?suid=1996368849416216&source=android) - [新蜂商城首页制作-2](https://juejin.cn/book/6844733814074245133?suid=1996368849416216&source=android) - [新蜂商城首页模块配置及功能完善](https://juejin.cn/book/6844733814074245133?suid=1996368849416216&source=android) - [新蜂商城会员的注册/登录功能实现](https://juejin.cn/book/6844733814074245133?suid=1996368849416216&source=android) - [新蜂商城搜索商品功能实现](https://juejin.cn/book/6844733814074245133?suid=1996368849416216&source=android) - [新蜂商城购物车功能实现](https://juejin.cn/book/6844733814074245133?suid=1996368849416216&source=android) - [新蜂商城订单确认页和订单生成功能实践](https://juejin.cn/book/6844733814074245133?suid=1996368849416216&source=android) - [新蜂商城个人订单列表和订单详情页制作](https://juejin.cn/book/6844733814074245133?suid=1996368849416216&source=android) - [新蜂商城订单流程功能完善](https://juejin.cn/book/6844733814074245133?suid=1996368849416216&source=android) - [课程总结及展望](https://juejin.cn/book/6844733814074245133?suid=1996368849416216&source=android) - [Spring Boot中的事务处理](https://juejin.cn/book/6844733814074245133?suid=1996368849416216&source=android) - [新蜂商城错误页面制作](https://juejin.cn/book/6844733814074245133?suid=1996368849416216&source=android) - [常见问题汇总讲解](https://juejin.cn/book/6844733814074245133?suid=1996368849416216&source=android) ## 联系作者 > 大家有任何问题或者建议都可以在 [issues](https://github.com/newbee-ltd/newbee-mall/issues) 中反馈给我,我会慢慢完善这个项目。 - 我的邮箱:2449207463@qq.com - QQ技术交流群:791509631 784785001 > newbee-mall 在 GitHub 和国内的码云都创建了代码仓库,如果有人访问 GitHub 比较慢的话,建议在 Gitee 上查看该项目,两个仓库会保持同步更新。 - [newbee-mall in GitHub](https://github.com/newbee-ltd/newbee-mall) - [newbee-mall in Gitee](https://gitee.com/newbee-ltd/newbee-mall) ![newbee-mall-info](https://newbee-mall.oss-cn-beijing.aliyuncs.com/poster/store/newbee-mall-info-3.png) ## 软件著作权 >本系统已申请软件著作权,受国家版权局知识产权以及国家计算机软件著作权保护! ![](https://newbee-mall.oss-cn-beijing.aliyuncs.com/poster/store/newbee-mall-copyright.png) ## 页面展示 以下为商城项目的部分页面,由于篇幅所限,无法一一列举,重要节点及重要功能的页面都已整理在下方。 ### 商城页面预览 - 商城首页 1 ![index](https://newbee-mall.oss-cn-beijing.aliyuncs.com/poster/product/index-01-2023.gif) - 商城首页 2 ![index](https://newbee-mall.oss-cn-beijing.aliyuncs.com/poster/product/index-02-2023.png) - 商品搜索 ![search](https://newbee-mall.oss-cn-beijing.aliyuncs.com/poster/product/search-2023.png) - 购物车 ![cart](https://newbee-mall.oss-cn-beijing.aliyuncs.com/poster/product/cart-2023.png) - 订单结算 ![settle](https://newbee-mall.oss-cn-beijing.aliyuncs.com/poster/product/settle-2023.png) - 订单列表 ![orders](https://newbee-mall.oss-cn-beijing.aliyuncs.com/poster/product/orders-2023.png) - 支付页面 ![settle](https://newbee-mall.oss-cn-beijing.aliyuncs.com/poster/product/wx-pay.png) ### 后台管理页面 - 登录页 ![login](https://newbee-mall.oss-cn-beijing.aliyuncs.com/poster/product/manage-login.png) - 轮播图管理 ![carousel](https://newbee-mall.oss-cn-beijing.aliyuncs.com/poster/product/manage-carousel-2023.png) - 新品上线 ![config](https://newbee-mall.oss-cn-beijing.aliyuncs.com/poster/product/manage-index-config-2023.png) - 分类管理 ![category](https://newbee-mall.oss-cn-beijing.aliyuncs.com/poster/product/manage-category.png) - 商品管理 ![goods](https://newbee-mall.oss-cn-beijing.aliyuncs.com/poster/product/manage-goods-2023.png) - 商品编辑 ![edit](https://newbee-mall.oss-cn-beijing.aliyuncs.com/poster/product/goods-edit-2023.png) - 订单管理 ![order](https://newbee-mall.oss-cn-beijing.aliyuncs.com/poster/product/manage-order-2023.png) ![newbee-mall-info](https://newbee-mall.oss-cn-beijing.aliyuncs.com/poster/store/newbee-mall-info-3.png) ## 感谢 - [spring-projects](https://github.com/spring-projects/spring-boot) - [thymeleaf](https://github.com/thymeleaf/thymeleaf) - [mybatis](https://github.com/mybatis/mybatis-3) - [ColorlibHQ](https://github.com/ColorlibHQ/AdminLTE) - [tonytomov](https://github.com/tonytomov/jqGrid) - [sweetalert2](https://github.com/sweetalert2/sweetalert2) - [skytotwo](https://github.com/skytotwo/Alipay-WeChat-HTML) - [hutool](https://github.com/dromara/hutool) - [wangeditor-team](https://github.com/wangeditor-team/wangEditor) - [VincentGarreau](https://github.com/VincentGarreau/particles.js) - [Vue](https://github.com/vuejs/vue) - [Vant](https://github.com/youzan/vant) ================================================ FILE: docs/API.md ================================================ # newbee-mall API 接口文档 ## 文档说明 本文档描述了 newbee-mall 商城系统的主要 API 接口,包括前台商城接口和后台管理接口。 **基础信息** - 基础URL: http://localhost:28089 - 编码格式: UTF-8 - 响应格式: JSON / HTML (Thymeleaf 模板) - Session 管理: 基于 Cookie 的 Session 机制 --- ## 目录 - [前台商城接口](#前台商城接口) - [首页相关](#首页相关) - [商品相关](#商品相关) - [购物车相关](#购物车相关) - [订单相关](#订单相关) - [用户相关](#用户相关) - [后台管理接口](#后台管理接口) - [管理员登录](#管理员登录) - [轮播图管理](#轮播图管理) - [商品管理](#商品管理) - [分类管理](#分类管理) - [订单管理](#订单管理) - [会员管理](#会员管理) - [公共接口](#公共接口) --- ## 前台商城接口 ### 首页相关 #### 1. 获取商城首页 **接口地址**: `GET /` 或 `GET /index` 或 `GET /index.html` **接口描述**: 获取商城首页数据,包括轮播图、分类、热销商品、新品、推荐商品 **请求参数**: 无 **响应数据**: ```html 返回 Thymeleaf 模板渲染的 HTML 页面 ``` **页面数据**: - `categories`: 商品分类列表 - `carousels`: 轮播图列表(最多5个) - `hotGoodses`: 热销商品列表(最多5个) - `newGoodses`: 新品列表(最多5个) - `recommendGoodses`: 推荐商品列表(最多10个) --- ### 商品相关 #### 2. 商品搜索 **接口地址**: `GET /search` **接口描述**: 搜索商品列表 **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | keyword | String | 否 | 搜索关键词 | | goodsCategoryId | Long | 否 | 商品分类ID | | orderBy | String | 否 | 排序方式(new:新品, price:价格) | | page | Integer | 否 | 页码,默认1 | **响应数据**: ```html 返回商品列表页面 ``` #### 3. 商品详情 **接口地址**: `GET /goods/detail/{goodsId}` **接口描述**: 获取商品详细信息 **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | goodsId | Long | 是 | 商品ID(路径参数) | **响应数据**: ```html 返回商品详情页面 ``` --- ### 购物车相关 #### 4. 查看购物车 **接口地址**: `GET /shop-cart` **接口描述**: 查看当前用户的购物车 **请求参数**: 无(需要登录) **响应数据**: ```html 返回购物车页面 ``` #### 5. 添加商品到购物车 **接口地址**: `POST /shop-cart` **接口描述**: 将商品添加到购物车 **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | goodsId | Long | 是 | 商品ID | | goodsCount | Integer | 是 | 商品数量 | **响应数据**: ```json { "resultCode": 200, "message": "success", "data": null } ``` #### 6. 更新购物车商品数量 **接口地址**: `PUT /shop-cart` **接口描述**: 更新购物车中商品的数量 **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | cartItemId | Long | 是 | 购物车项ID | | goodsCount | Integer | 是 | 新的商品数量 | **响应数据**: ```json { "resultCode": 200, "message": "success" } ``` #### 7. 删除购物车商品 **接口地址**: `DELETE /shop-cart/{cartItemId}` **接口描述**: 从购物车中删除商品 **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | cartItemId | Long | 是 | 购物车项ID(路径参数) | **响应数据**: ```json { "resultCode": 200, "message": "success" } ``` --- ### 订单相关 #### 8. 订单结算页面 **接口地址**: `GET /shop-cart/settle` **接口描述**: 获取订单结算页面 **请求参数**: 无(需要登录) **响应数据**: ```html 返回订单结算页面 ``` #### 9. 生成订单 **接口地址**: `POST /saveOrder` **接口描述**: 提交订单 **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | addressId | Long | 是 | 收货地址ID | | cartItemIds | String | 是 | 购物车项ID列表(逗号分隔) | **响应数据**: ```json { "resultCode": 200, "message": "success", "data": "订单号" } ``` #### 10. 我的订单列表 **接口地址**: `GET /orders` **接口描述**: 查看当前用户的订单列表 **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | page | Integer | 否 | 页码,默认1 | | status | String | 否 | 订单状态 | **响应数据**: ```html 返回订单列表页面 ``` #### 11. 订单详情 **接口地址**: `GET /orders/{orderNo}` **接口描述**: 查看订单详细信息 **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | orderNo | String | 是 | 订单号(路径参数) | **响应数据**: ```html 返回订单详情页面 ``` #### 12. 取消订单 **接口地址**: `PUT /orders/{orderNo}/cancel` **接口描述**: 取消订单 **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | orderNo | String | 是 | 订单号(路径参数) | **响应数据**: ```json { "resultCode": 200, "message": "success" } ``` #### 13. 确认收货 **接口地址**: `PUT /orders/{orderNo}/finish` **接口描述**: 确认收货 **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | orderNo | String | 是 | 订单号(路径参数) | **响应数据**: ```json { "resultCode": 200, "message": "success" } ``` --- ### 用户相关 #### 14. 用户注册 **接口地址**: `POST /register` **接口描述**: 用户注册 **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | loginName | String | 是 | 登录名 | | password | String | 是 | 密码 | | verifyCode | String | 是 | 验证码 | **响应数据**: ```json { "resultCode": 200, "message": "注册成功" } ``` #### 15. 用户登录 **接口地址**: `POST /login` **接口描述**: 用户登录 **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | loginName | String | 是 | 登录名 | | password | String | 是 | 密码 | | verifyCode | String | 是 | 验证码 | **响应数据**: ```json { "resultCode": 200, "message": "登录成功" } ``` #### 16. 用户登出 **接口地址**: `POST /logout` **接口描述**: 用户退出登录 **请求参数**: 无 **响应数据**: ```html 跳转到登录页面 ``` #### 17. 个人信息 **接口地址**: `GET /personal` **接口描述**: 查看个人信息 **请求参数**: 无(需要登录) **响应数据**: ```html 返回个人中心页面 ``` #### 18. 修改个人信息 **接口地址**: `POST /personal/updateInfo` **接口描述**: 修改个人信息 **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | nickName | String | 是 | 昵称 | | introduceSign | String | 否 | 个性签名 | **响应数据**: ```json { "resultCode": 200, "message": "修改成功" } ``` --- ## 后台管理接口 ### 管理员登录 #### 19. 管理员登录页面 **接口地址**: `GET /admin/login` **接口描述**: 获取管理员登录页面 **请求参数**: 无 **响应数据**: ```html 返回管理员登录页面 ``` #### 20. 管理员登录 **接口地址**: `POST /admin/login` **接口描述**: 管理员登录验证 **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | userName | String | 是 | 用户名 | | password | String | 是 | 密码 | | verifyCode | String | 是 | 验证码 | **响应数据**: ```html 登录成功:跳转到管理后台首页 登录失败:返回登录页面并显示错误信息 ``` #### 21. 管理员登出 **接口地址**: `GET /admin/logout` **接口描述**: 管理员退出登录 **请求参数**: 无 **响应数据**: ```html 跳转到登录页面 ``` #### 22. 管理员个人资料 **接口地址**: `GET /admin/profile` **接口描述**: 查看管理员个人资料 **请求参数**: 无(需要登录) **响应数据**: ```html 返回个人资料页面 ``` #### 23. 修改管理员密码 **接口地址**: `POST /admin/profile/password` **接口描述**: 修改管理员密码 **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | originalPassword | String | 是 | 原密码 | | newPassword | String | 是 | 新密码 | **响应数据**: ```json { "resultCode": 200, "message": "修改成功" } ``` --- ### 轮播图管理 #### 24. 轮播图列表 **接口地址**: `GET /admin/carousels` **接口描述**: 获取轮播图列表(分页) **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | page | Integer | 否 | 页码,默认1 | | limit | Integer | 否 | 每页数量,默认10 | **响应数据**: ```json { "resultCode": 200, "message": "success", "data": { "totalCount": 100, "currPage": 1, "list": [...] } } ``` #### 25. 添加轮播图 **接口地址**: `POST /admin/carousels/save` **接口描述**: 添加新的轮播图 **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | carouselUrl | String | 是 | 图片URL | | redirectUrl | String | 是 | 跳转URL | | carouselRank | Integer | 是 | 排序值 | **响应数据**: ```json { "resultCode": 200, "message": "保存成功" } ``` #### 26. 修改轮播图 **接口地址**: `POST /admin/carousels/update` **接口描述**: 修改轮播图信息 **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | carouselId | Integer | 是 | 轮播图ID | | carouselUrl | String | 是 | 图片URL | | redirectUrl | String | 是 | 跳转URL | | carouselRank | Integer | 是 | 排序值 | **响应数据**: ```json { "resultCode": 200, "message": "修改成功" } ``` #### 27. 删除轮播图 **接口地址**: `POST /admin/carousels/delete` **接口描述**: 删除轮播图 **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | ids | Integer[] | 是 | 轮播图ID数组 | **响应数据**: ```json { "resultCode": 200, "message": "删除成功" } ``` --- ### 商品管理 #### 28. 商品列表 **接口地址**: `GET /admin/goods/list` **接口描述**: 获取商品列表(分页) **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | page | Integer | 否 | 页码,默认1 | | limit | Integer | 否 | 每页数量,默认10 | | goodsName | String | 否 | 商品名称(模糊查询) | | goodsSellStatus | Integer | 否 | 上架状态 | **响应数据**: ```json { "resultCode": 200, "message": "success", "data": { "totalCount": 100, "currPage": 1, "list": [...] } } ``` #### 29. 添加商品 **接口地址**: `POST /admin/goods/save` **接口描述**: 添加新商品 **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | goodsName | String | 是 | 商品名称 | | goodsIntro | String | 是 | 商品简介 | | goodsCategoryId | Long | 是 | 分类ID | | goodsCoverImg | String | 是 | 封面图 | | originalPrice | Integer | 是 | 原价 | | sellingPrice | Integer | 是 | 售价 | | stockNum | Integer | 是 | 库存 | | tag | String | 否 | 标签 | | goodsSellStatus | Byte | 是 | 上架状态 | | goodsDetailContent | String | 是 | 商品详情(富文本) | **响应数据**: ```json { "resultCode": 200, "message": "保存成功" } ``` #### 30. 修改商品 **接口地址**: `POST /admin/goods/update` **接口描述**: 修改商品信息 **请求参数**: 同添加商品,需额外传递 `goodsId` **响应数据**: ```json { "resultCode": 200, "message": "修改成功" } ``` #### 31. 上架/下架商品 **接口地址**: `POST /admin/goods/status/{sellStatus}` **接口描述**: 批量修改商品上架状态 **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | sellStatus | Integer | 是 | 状态值(路径参数,0下架,1上架) | | ids | Long[] | 是 | 商品ID数组 | **响应数据**: ```json { "resultCode": 200, "message": "修改成功" } ``` --- ### 分类管理 #### 32. 分类列表 **接口地址**: `GET /admin/categories/list` **接口描述**: 获取分类列表(分页) **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | page | Integer | 否 | 页码,默认1 | | limit | Integer | 否 | 每页数量,默认10 | | categoryLevel | Integer | 否 | 分类级别(1/2/3) | | parentId | Long | 否 | 父分类ID | **响应数据**: ```json { "resultCode": 200, "message": "success", "data": { "totalCount": 100, "currPage": 1, "list": [...] } } ``` #### 33. 添加分类 **接口地址**: `POST /admin/categories/save` **接口描述**: 添加商品分类 **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | categoryLevel | Byte | 是 | 分类级别 | | categoryName | String | 是 | 分类名称 | | parentId | Long | 是 | 父分类ID | | categoryRank | Integer | 是 | 排序值 | **响应数据**: ```json { "resultCode": 200, "message": "保存成功" } ``` #### 34. 获取分类详情 **接口地址**: `GET /admin/categories/info/{id}` **接口描述**: 获取分类详细信息 **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | id | Long | 是 | 分类ID(路径参数) | **响应数据**: ```json { "resultCode": 200, "message": "success", "data": {...} } ``` --- ### 订单管理 #### 35. 订单列表 **接口地址**: `GET /admin/orders/list` **接口描述**: 获取订单列表(分页) **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | page | Integer | 否 | 页码,默认1 | | limit | Integer | 否 | 每页数量,默认10 | | orderNo | String | 否 | 订单号 | | orderStatus | Integer | 否 | 订单状态 | **响应数据**: ```json { "resultCode": 200, "message": "success", "data": { "totalCount": 100, "currPage": 1, "list": [...] } } ``` #### 36. 订单详情 **接口地址**: `GET /admin/orders/detail/{orderId}` **接口描述**: 查看订单详细信息 **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | orderId | Long | 是 | 订单ID(路径参数) | **响应数据**: ```json { "resultCode": 200, "message": "success", "data": {...} } ``` #### 37. 配货 **接口地址**: `POST /admin/orders/checkDone` **接口描述**: 订单配货 **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | ids | Long[] | 是 | 订单ID数组 | **响应数据**: ```json { "resultCode": 200, "message": "配货成功" } ``` #### 38. 出库 **接口地址**: `POST /admin/orders/checkOut` **接口描述**: 订单出库 **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | ids | Long[] | 是 | 订单ID数组 | **响应数据**: ```json { "resultCode": 200, "message": "出库成功" } ``` #### 39. 关闭订单 **接口地址**: `POST /admin/orders/close` **接口描述**: 关闭订单 **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | ids | Long[] | 是 | 订单ID数组 | **响应数据**: ```json { "resultCode": 200, "message": "关闭成功" } ``` --- ### 会员管理 #### 40. 会员列表 **接口地址**: `GET /admin/users/list` **接口描述**: 获取会员列表(分页) **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | page | Integer | 否 | 页码,默认1 | | limit | Integer | 否 | 每页数量,默认10 | **响应数据**: ```json { "resultCode": 200, "message": "success", "data": { "totalCount": 100, "currPage": 1, "list": [...] } } ``` #### 41. 禁用会员 **接口地址**: `POST /admin/users/lock/{lockStatus}` **接口描述**: 批量禁用/解禁会员 **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | lockStatus | Integer | 是 | 状态值(路径参数,0正常,1锁定) | | ids | Long[] | 是 | 用户ID数组 | **响应数据**: ```json { "resultCode": 200, "message": "修改成功" } ``` --- ## 公共接口 #### 42. 获取验证码 **接口地址**: `GET /common/kaptcha` **接口描述**: 生成验证码图片 **请求参数**: 无 **响应数据**: ``` 图片流(image/jpeg) ``` #### 43. 文件上传 **接口地址**: `POST /admin/upload/file` **接口描述**: 上传文件(图片) **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | file | MultipartFile | 是 | 文件对象 | **响应数据**: ```json { "resultCode": 200, "message": "上传成功", "data": "文件访问路径" } ``` --- ## 状态码说明 ### 通用状态码 | 状态码 | 说明 | |--------|------| | 200 | 成功 | | 500 | 服务器内部错误 | ### 业务状态码 | 状态码 | 说明 | |--------|------| | SUCCESS | 成功 | | ERROR | 失败 | | DB_ERROR | 数据库异常 | | PARAMS_ERROR | 参数异常 | | NOT_LOGIN_ERROR | 未登录 | | DATA_NOT_EXIST | 数据不存在 | | GOODS_SAVE_ERROR | 商品保存失败 | | GOODS_UPDATE_ERROR | 商品更新失败 | | ORDER_NOT_EXIST_ERROR | 订单不存在 | | NULL_ADDRESS_ERROR | 地址为空 | | LOGIN_ERROR | 登录失败 | | NOT_SALE_GOODS | 商品已下架 | --- ## 数据模型 ### 商品对象 (Goods) ```json { "goodsId": 1, "goodsName": "商品名称", "goodsIntro": "商品简介", "goodsCategoryId": 100, "goodsCoverImg": "/upload/xxx.jpg", "goodsDetailContent": "商品详情HTML", "originalPrice": 100, "sellingPrice": 80, "stockNum": 999, "tag": "热销", "goodsSellStatus": 0, "createTime": "2023-01-01 12:00:00", "updateTime": "2023-01-01 12:00:00" } ``` ### 订单对象 (Order) ```json { "orderId": 1, "orderNo": "202301010001", "userId": 1, "totalPrice": 100, "payStatus": 1, "payType": 1, "payTime": "2023-01-01 12:00:00", "orderStatus": 1, "extraInfo": "", "isDeleted": 0, "createTime": "2023-01-01 12:00:00", "updateTime": "2023-01-01 12:00:00" } ``` ### 用户对象 (User) ```json { "userId": 1, "nickName": "用户昵称", "loginName": "登录名", "passwordMd5": "MD5加密密码", "introduceSign": "个性签名", "isDeleted": 0, "lockedFlag": 0, "createTime": "2023-01-01 12:00:00" } ``` --- ## 注意事项 1. **认证机制**: 所有需要登录的接口都需要携带有效的 Session Cookie 2. **参数校验**: 所有接口都会进行参数校验,参数不合法会返回相应错误信息 3. **跨域访问**: 如果前后端分离部署,需要配置 CORS 4. **文件上传**: 支持的文件格式为 jpg、png、jpeg、gif,大小限制请查看配置 5. **分页参数**: 默认每页10条,最大不超过100条 --- **文档版本**: v1.0.0 **最后更新**: 2025-10-23 **维护者**: newbee-mall 开发团队 ================================================ FILE: docs/DEVELOPMENT.md ================================================ # newbee-mall 开发指南 ## 目录 - [项目介绍](#项目介绍) - [技术栈](#技术栈) - [项目结构](#项目结构) - [开发环境搭建](#开发环境搭建) - [快速开始](#快速开始) - [开发规范](#开发规范) - [核心功能实现](#核心功能实现) - [常用开发任务](#常用开发任务) - [测试指南](#测试指南) - [部署指南](#部署指南) --- ## 项目介绍 newbee-mall 是一个基于 Spring Boot 的电商系统,包含前台商城和后台管理两个部分。 **项目特点**: - 代码结构清晰,易于理解和学习 - 功能完善,包含电商系统的核心功能 - 技术栈主流,适合学习和实践 - 开箱即用,2秒即可启动 **主要功能模块**: **前台商城**: - 首页展示(轮播图、分类、商品推荐) - 商品搜索与展示 - 购物车管理 - 订单流程(下单、支付、查看) - 用户中心(个人信息、订单管理) **后台管理**: - 管理员登录与权限控制 - 轮播图管理 - 商品分类管理 - 商品管理 - 订单管理 - 会员管理 --- ## 技术栈 ### 后端技术 | 技术 | 版本 | 说明 | |------|------|------| | Spring Boot | 2.6.3 | 核心框架 | | Spring MVC | - | MVC 框架 | | MyBatis | 2.2.2 | ORM 框架 | | Thymeleaf | - | 模板引擎 | | MySQL | 5.7+ | 数据库 | | Hutool | 5.7.22 | Java 工具库 | | Maven | 3.x | 项目构建工具 | ### 前端技术 | 技术 | 说明 | |------|------| | Thymeleaf | 服务端模板引擎 | | jQuery | JavaScript 库 | | Bootstrap | UI 框架 | | AdminLTE | 后台管理模板 | | jqGrid | 数据表格插件 | | wangEditor | 富文本编辑器 | | SweetAlert | 弹窗组件 | --- ## 项目结构 ``` newbee-mall/ ├── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── ltd/newbee/mall/ │ │ │ ├── common/ # 公共常量和枚举 │ │ │ │ ├── Constants.java │ │ │ │ ├── ServiceResultEnum.java │ │ │ │ └── ... │ │ │ ├── config/ # 配置类 │ │ │ │ └── WebMvcConfig.java │ │ │ ├── controller/ # 控制器层 │ │ │ │ ├── admin/ # 后台管理控制器 │ │ │ │ ├── mall/ # 前台商城控制器 │ │ │ │ ├── common/ # 公共控制器 │ │ │ │ └── vo/ # 视图对象 │ │ │ ├── dao/ # 数据访问层 │ │ │ │ ├── AdminUserMapper.java │ │ │ │ ├── NewBeeMallGoodsMapper.java │ │ │ │ └── ... │ │ │ ├── entity/ # 实体类 │ │ │ │ ├── AdminUser.java │ │ │ │ ├── NewBeeMallGoods.java │ │ │ │ └── ... │ │ │ ├── interceptor/ # 拦截器 │ │ │ │ ├── AdminLoginInterceptor.java │ │ │ │ └── NewBeeMallLoginInterceptor.java │ │ │ ├── service/ # 服务层接口 │ │ │ │ ├── AdminUserService.java │ │ │ │ └── ... │ │ │ │ └── impl/ # 服务层实现 │ │ │ │ ├── AdminUserServiceImpl.java │ │ │ │ └── ... │ │ │ ├── util/ # 工具类 │ │ │ │ ├── MD5Util.java │ │ │ │ ├── Result.java │ │ │ │ └── ... │ │ │ └── NewBeeMallApplication.java # 启动类 │ │ └── resources/ │ │ ├── mapper/ # MyBatis XML 映射文件 │ │ │ ├── AdminUserMapper.xml │ │ │ └── ... │ │ ├── static/ # 静态资源 │ │ │ ├── admin/ # 后台静态资源 │ │ │ └── mall/ # 前台静态资源 │ │ ├── templates/ # Thymeleaf 模板 │ │ │ ├── admin/ # 后台页面 │ │ │ └── mall/ # 前台页面 │ │ └── application.properties # 配置文件 │ └── test/ # 测试代码 ├── docs/ # 项目文档 ├── pom.xml # Maven 配置 └── README.md # 项目说明 ``` --- ## 开发环境搭建 ### 1. 安装必要软件 #### 1.1 安装 JDK 下载并安装 JDK 1.8 或以上版本,配置环境变量。 验证安装: ```bash java -version ``` #### 1.2 安装 Maven 下载并安装 Maven 3.x,配置环境变量。 验证安装: ```bash mvn -version ``` 推荐配置阿里云镜像(修改 `settings.xml`): ```xml aliyun central Aliyun Maven https://maven.aliyun.com/repository/public ``` #### 1.3 安装 MySQL 下载并安装 MySQL 5.7 或以上版本。 创建数据库: ```sql CREATE DATABASE newbee_mall_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; ``` #### 1.4 安装 IDE 推荐使用 IntelliJ IDEA 或 Eclipse。 ### 2. 克隆项目 ```bash git clone https://github.com/newbee-ltd/newbee-mall.git cd newbee-mall ``` ### 3. 导入数据库 执行项目中的 SQL 脚本(通常在 `sql` 或 `database` 目录): ```bash mysql -u root -p newbee_mall_db < newbee_mall_schema.sql mysql -u root -p newbee_mall_db < newbee_mall_data.sql ``` ### 4. 配置项目 修改 `src/main/resources/application.properties`: ```properties # 数据库配置 spring.datasource.url=jdbc:mysql://localhost:3306/newbee_mall_db?useUnicode=true&serverTimezone=Asia/Shanghai&characterEncoding=utf8&autoReconnect=true&useSSL=false&allowMultiQueries=true spring.datasource.username=root spring.datasource.password=你的密码 # 服务器端口 server.port=28089 ``` --- ## 快速开始 ### 方式一: IDEA 启动 1. 用 IDEA 打开项目 2. 等待 Maven 依赖下载完成 3. 找到 `NewBeeMallApplication.java` 4. 右键选择 `Run 'NewBeeMallApplication'` ### 方式二: Maven 启动 ```bash mvn clean install mvn spring-boot:run ``` ### 方式三: 命令行启动 ```bash mvn clean package cd target java -jar newbee-mall-1.0.0-SNAPSHOT.jar ``` ### 访问项目 - 前台商城: http://localhost:28089 - 后台管理: http://localhost:28089/admin/login --- ## 开发规范 ### 1. 代码规范 #### 1.1 命名规范 - **类名**: 使用大驼峰命名法(PascalCase) ```java public class NewBeeMallGoods { } ``` - **方法名**: 使用小驼峰命名法(camelCase) ```java public void saveGoods() { } ``` - **常量**: 使用全大写,单词间用下划线分隔 ```java public static final int MAX_GOODS_COUNT = 100; ``` - **包名**: 全小写,使用点分隔 ```java package ltd.newbee.mall.service; ``` #### 1.2 注释规范 - 类和接口必须添加注释 - 公共方法必须添加注释 - 复杂逻辑必须添加行内注释 ```java /** * 商品服务接口 * * @author 13 */ public interface NewBeeMallGoodsService { /** * 保存商品 * * @param goods 商品对象 * @return 保存结果 */ Boolean saveGoods(NewBeeMallGoods goods); } ``` #### 1.3 代码格式 - 使用4个空格缩进(不使用 Tab) - 左大括号不换行 - if/for/while 等语句必须使用大括号 - 一行代码不超过120个字符 ### 2. 分层规范 #### 2.1 Controller 层 - 只负责请求接收和响应 - 不包含业务逻辑 - 调用 Service 层完成业务处理 - 进行参数校验 ```java @Controller public class GoodsController { @Resource private NewBeeMallGoodsService goodsService; @GetMapping("/goods/detail/{goodsId}") public String goodsDetail(@PathVariable Long goodsId, HttpServletRequest request) { // 参数校验 if (goodsId < 1) { return "error/error_5xx"; } // 调用 Service NewBeeMallGoodsDetailVO goodsDetail = goodsService.getGoodsDetail(goodsId); // 设置返回数据 request.setAttribute("goodsDetail", goodsDetail); return "mall/detail"; } } ``` #### 2.2 Service 层 - 包含业务逻辑 - 事务控制在此层 - 调用 DAO 层操作数据库 ```java @Service public class NewBeeMallGoodsServiceImpl implements NewBeeMallGoodsService { @Resource private NewBeeMallGoodsMapper goodsMapper; @Override @Transactional public Boolean saveGoods(NewBeeMallGoods goods) { // 业务逻辑处理 if (goods == null) { return false; } // 调用 DAO return goodsMapper.insertSelective(goods) > 0; } } ``` #### 2.3 DAO 层 - 只负责数据库操作 - 不包含业务逻辑 - 使用 MyBatis 接口和 XML 映射 ```java public interface NewBeeMallGoodsMapper { int insertSelective(NewBeeMallGoods record); NewBeeMallGoods selectByPrimaryKey(Long goodsId); int updateByPrimaryKeySelective(NewBeeMallGoods record); } ``` ### 3. 异常处理 - 使用统一的异常处理机制 - 自定义业务异常 ```java public class NewBeeMallException extends RuntimeException { public NewBeeMallException(String message) { super(message); } public static void fail(String message) { throw new NewBeeMallException(message); } } ``` ### 4. 返回结果规范 - 使用统一的返回对象 `Result` - 使用枚举定义返回码和消息 ```java Result result = Result.success(); Result result = Result.error("操作失败"); ``` --- ## 核心功能实现 ### 1. 用户登录功能 #### 1.1 前台用户登录 **流程**: 1. 用户输入用户名、密码、验证码 2. Controller 接收参数并校验 3. Service 验证用户名和密码(MD5) 4. 登录成功后将用户信息存入 Session 5. 返回登录结果 **代码示例**: ```java @PostMapping("/login") @ResponseBody public Result login(@RequestParam("loginName") String loginName, @RequestParam("password") String password, HttpSession session) { // 参数校验 if (StringUtils.isEmpty(loginName) || StringUtils.isEmpty(password)) { return Result.error("参数不能为空"); } // 调用 Service 验证 String loginResult = newBeeMallUserService.login(loginName, password, session); if (ServiceResultEnum.SUCCESS.getResult().equals(loginResult)) { return Result.success(); } return Result.error(loginResult); } ``` #### 1.2 登录拦截器 **作用**: 拦截需要登录才能访问的请求 ```java @Component public class NewBeeMallLoginInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { HttpSession session = request.getSession(); Object user = session.getAttribute("mallUser"); if (user == null) { response.sendRedirect("/login"); return false; } return true; } } ``` **配置拦截器**: ```java @Configuration public class WebMvcConfig implements WebMvcConfigurer { @Resource private NewBeeMallLoginInterceptor loginInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(loginInterceptor) .addPathPatterns("/personal/**") .addPathPatterns("/shop-cart/**") .addPathPatterns("/orders/**") .excludePathPatterns("/login") .excludePathPatterns("/register"); } } ``` ### 2. 购物车功能 #### 2.1 添加商品到购物车 ```java @PostMapping("/shop-cart") @ResponseBody public Result saveShoppingCartItem(@RequestBody NewBeeMallShoppingCartItem shoppingCartItem, HttpSession session) { // 获取当前登录用户 NewBeeMallUserVO user = (NewBeeMallUserVO) session.getAttribute("mallUser"); shoppingCartItem.setUserId(user.getUserId()); // 调用 Service 保存 String saveResult = newBeeMallShoppingCartService.saveNewBeeMallCartItem(shoppingCartItem); if (ServiceResultEnum.SUCCESS.getResult().equals(saveResult)) { return Result.success(); } return Result.error(saveResult); } ``` #### 2.2 购物车列表查询 ```java @GetMapping("/shop-cart") public String cartListPage(HttpServletRequest request, HttpSession session) { NewBeeMallUserVO user = (NewBeeMallUserVO) session.getAttribute("mallUser"); // 查询购物车列表 List myShoppingCartItems = newBeeMallShoppingCartService.getMyShoppingCartItems(user.getUserId()); request.setAttribute("cartItems", myShoppingCartItems); return "mall/cart"; } ``` ### 3. 订单功能 #### 3.1 生成订单 ```java @PostMapping("/saveOrder") @ResponseBody @Transactional public Result saveOrder(@RequestParam Long addressId, @RequestParam String cartItemIds, HttpSession session) { // 获取当前用户 NewBeeMallUserVO user = (NewBeeMallUserVO) session.getAttribute("mallUser"); // 购物车项 ID 转换 List itemIdList = Arrays.stream(cartItemIds.split(",")) .map(Long::parseLong) .collect(Collectors.toList()); // 生成订单 String saveOrderResult = newBeeMallOrderService.saveOrder(user.getUserId(), addressId, itemIdList); if (ServiceResultEnum.SUCCESS.getResult().equals(saveOrderResult)) { return Result.success(); } return Result.error(saveOrderResult); } ``` #### 3.2 订单状态流转 订单状态定义: - 0: 待支付 - 1: 已支付 - 2: 配货中 - 3: 出库成功 - 4: 交易成功 - -1: 手动关闭 - -2: 超时关闭 - -3: 商家关闭 ### 4. 文件上传功能 ```java @PostMapping("/admin/upload/file") @ResponseBody public Result upload(@RequestParam("file") MultipartFile file) { // 文件名校验 String fileName = file.getOriginalFilename(); String suffixName = fileName.substring(fileName.lastIndexOf(".")); // 生成新文件名 String newFileName = UUID.randomUUID().toString() + suffixName; // 保存文件 File destFile = new File(uploadPath + newFileName); file.transferTo(destFile); // 返回文件访问路径 return Result.success(newFileName); } ``` ### 5. 分页功能 ```java @GetMapping("/admin/goods/list") @ResponseBody public Result list(@RequestParam Map params) { PageQueryUtil pageUtil = new PageQueryUtil(params); // 查询分页数据 PageResult pageResult = newBeeMallGoodsService.getNewBeeMallGoodsPage(pageUtil); return Result.success(pageResult); } ``` --- ## 常用开发任务 ### 1. 添加新的页面 #### 1.1 创建 Controller ```java @Controller @RequestMapping("/demo") public class DemoController { @GetMapping("/index") public String index() { return "mall/demo/index"; } } ``` #### 1.2 创建模板文件 在 `src/main/resources/templates/mall/demo/` 目录下创建 `index.html` #### 1.3 添加静态资源 在 `src/main/resources/static/mall/` 目录下添加 CSS、JS 文件 ### 2. 添加新的 API 接口 #### 2.1 创建实体类 ```java public class Demo { private Long id; private String name; // getter/setter } ``` #### 2.2 创建 Mapper 接口 ```java public interface DemoMapper { List selectList(); int insert(Demo demo); } ``` #### 2.3 创建 Mapper XML ```xml ``` #### 2.4 创建 Service ```java public interface DemoService { List getList(); } @Service public class DemoServiceImpl implements DemoService { @Resource private DemoMapper demoMapper; @Override public List getList() { return demoMapper.selectList(); } } ``` #### 2.5 创建 Controller ```java @RestController @RequestMapping("/api/demo") public class DemoController { @Resource private DemoService demoService; @GetMapping("/list") public Result list() { return Result.success(demoService.getList()); } } ``` ### 3. 添加定时任务 ```java @Component @EnableScheduling public class ScheduledTask { @Scheduled(cron = "0 0 2 * * ?") public void task() { // 定时任务逻辑 } } ``` ### 4. 添加拦截器 ```java @Component public class CustomInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { // 前置处理 return true; } } ``` --- ## 测试指南 ### 1. 单元测试 ```java @SpringBootTest public class GoodsServiceTest { @Resource private NewBeeMallGoodsService goodsService; @Test public void testGetGoodsDetail() { NewBeeMallGoodsDetailVO goodsDetail = goodsService.getGoodsDetail(1L); assertNotNull(goodsDetail); } } ``` ### 2. 接口测试 使用 Postman 或其他工具测试 API 接口。 ### 3. 功能测试 手动测试各个功能模块是否正常运行。 --- ## 部署指南 ### 1. 打包项目 ```bash mvn clean package -DskipTests ``` ### 2. 服务器环境准备 - 安装 JDK 1.8+ - 安装 MySQL 5.7+ - 导入数据库 ### 3. 上传并运行 ```bash # 上传 jar 包 scp target/newbee-mall-1.0.0-SNAPSHOT.jar user@server:/app/ # SSH 登录服务器 ssh user@server # 运行项目 nohup java -jar /app/newbee-mall-1.0.0-SNAPSHOT.jar --spring.profiles.active=prod > /app/logs/app.log 2>&1 & ``` ### 4. 配置 Nginx 反向代理 ```nginx server { listen 80; server_name your-domain.com; location / { proxy_pass http://localhost:28089; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } } ``` ### 5. 配置 HTTPS 使用 Let's Encrypt 获取免费 SSL 证书。 --- ## 常见问题 详见 [FAQ 文档](./FAQ.md) --- ## 参考资料 - [Spring Boot 官方文档](https://spring.io/projects/spring-boot) - [MyBatis 官方文档](https://mybatis.org/mybatis-3/) - [Thymeleaf 官方文档](https://www.thymeleaf.org/) --- **文档版本**: v1.0.0 **最后更新**: 2025-10-23 **维护者**: newbee-mall 开发团队 ================================================ FILE: docs/FAQ.md ================================================ # newbee-mall 常见问题 FAQ ## 目录 - [环境配置相关](#环境配置相关) - [项目启动相关](#项目启动相关) - [功能使用相关](#功能使用相关) - [开发调试相关](#开发调试相关) - [部署运维相关](#部署运维相关) --- ## 环境配置相关 ### Q1: 项目需要什么样的开发环境? **A:** newbee-mall 项目的基本环境要求如下: - JDK 1.8 或以上版本 - Maven 3.x - MySQL 5.7 或以上版本 - IDE:推荐使用 IntelliJ IDEA 或 Eclipse ### Q2: 如何配置数据库连接? **A:** 数据库配置位于 `src/main/resources/application.properties` 文件中: ```properties spring.datasource.url=jdbc:mysql://localhost:3306/newbee_mall_db spring.datasource.username=root spring.datasource.password=123456 ``` 请根据实际情况修改数据库地址、用户名和密码。 ### Q3: 数据库脚本在哪里? **A:** 数据库初始化脚本通常位于项目的 `sql` 或 `database` 目录中。首次运行前需要先执行 SQL 脚本创建数据库和表结构。 ### Q4: Maven 依赖下载失败怎么办? **A:** 可以尝试以下方法: 1. 检查网络连接 2. 更换 Maven 镜像源(推荐使用阿里云镜像) 3. 清理本地仓库缓存:`mvn clean` 4. 重新下载依赖:`mvn dependency:resolve` --- ## 项目启动相关 ### Q5: 如何启动项目? **A:** 有以下几种方式: 1. **IDEA 启动**:直接运行 `NewBeeMallApplication` 主类 2. **Maven 启动**:执行 `mvn spring-boot:run` 3. **命令行启动**:先打包 `mvn clean package`,然后运行 `java -jar target/newbee-mall-1.0.0-SNAPSHOT.jar` ### Q6: 项目默认端口是多少? **A:** 默认端口是 **28089**,可在 `application.properties` 中修改: ```properties server.port=28089 ``` ### Q7: 启动后如何访问? **A:** - **前台商城**:http://localhost:28089 - **后台管理**:http://localhost:28089/admin/login - 默认管理员账号需要查看数据库或初始化脚本 ### Q8: 启动时报错 "端口被占用" 怎么办? **A:** 可以: 1. 修改 `application.properties` 中的端口号 2. 或者关闭占用该端口的程序 3. Windows: `netstat -ano | findstr 28089` 查找进程并关闭 4. Linux/Mac: `lsof -i:28089` 查找进程并关闭 ### Q9: 启动时报错 "数据库连接失败"? **A:** 请检查: 1. MySQL 服务是否已启动 2. 数据库名、用户名、密码是否正确 3. 数据库是否已创建并执行了初始化脚本 4. 防火墙是否阻止了数据库连接 --- ## 功能使用相关 ### Q10: 如何注册用户? **A:** 在商城首页点击注册按钮,填写用户名、密码等信息即可注册。 ### Q11: 管理员默认账号是什么? **A:** 默认管理员账号信息需要查看数据库初始化脚本中的 `tb_newbee_mall_admin_user` 表。通常为: - 用户名:admin - 密码:123456(MD5加密后存储) ### Q12: 如何添加商品? **A:** 1. 登录后台管理系统 2. 进入"商品管理"模块 3. 点击"添加商品"按钮 4. 填写商品信息(名称、价格、库存、分类等) 5. 上传商品图片 6. 保存商品 ### Q13: 购物车数据存储在哪里? **A:** 购物车数据存储在数据库的 `tb_newbee_mall_shopping_cart_item` 表中,与用户ID关联。 ### Q14: 订单状态有哪些? **A:** 订单状态包括: - 待支付 - 已支付 - 配货中 - 出库成功 - 交易成功 - 订单关闭 具体状态值定义在 `OrderStatusEnum` 枚举类中。 ### Q15: 如何处理订单? **A:** 在后台管理系统的"订单管理"模块中: 1. 查看订单列表 2. 点击订单详情查看订单信息 3. 根据订单状态进行相应操作(配货、出库、关闭等) --- ## 开发调试相关 ### Q16: 如何开启热部署? **A:** 已配置 Thymeleaf 模板缓存为 false: ```properties spring.thymeleaf.cache=false ``` 对于 Java 代码修改,建议安装 spring-boot-devtools 依赖。 ### Q17: 日志文件在哪里? **A:** 项目使用 Spring Boot 默认的日志配置,日志输出到控制台。如需输出到文件,可在 `application.properties` 中配置: ```properties logging.file.path=logs logging.level.ltd.newbee.mall=DEBUG ``` ### Q18: 如何调试 MyBatis SQL? **A:** 在 `application.properties` 中添加: ```properties logging.level.ltd.newbee.mall.dao=DEBUG mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl ``` ### Q19: 前端静态资源在哪里? **A:** 静态资源位于: - CSS/JS:`src/main/resources/static/` - Thymeleaf 模板:`src/main/resources/templates/` ### Q20: 如何自定义错误页面? **A:** 在 `src/main/resources/templates/error/` 目录下创建对应的错误页面,如 `404.html`、`500.html`。 --- ## 部署运维相关 ### Q21: 如何打包部署? **A:** 1. 执行 `mvn clean package` 打包 2. 在 `target` 目录下生成 jar 包 3. 上传到服务器 4. 执行 `java -jar newbee-mall-1.0.0-SNAPSHOT.jar` 运行 ### Q22: 如何配置生产环境数据库? **A:** 建议使用 Spring Profile 配置: 1. 创建 `application-prod.properties` 2. 配置生产环境数据库信息 3. 启动时指定 profile:`java -jar app.jar --spring.profiles.active=prod` ### Q23: 如何后台运行服务? **A:** Linux 环境下: ```bash nohup java -jar newbee-mall-1.0.0-SNAPSHOT.jar > output.log 2>&1 & ``` ### Q24: 上传文件大小限制如何配置? **A:** 在 `application.properties` 中添加: ```properties spring.servlet.multipart.max-file-size=10MB spring.servlet.multipart.max-request-size=100MB ``` ### Q25: Session 超时时间如何设置? **A:** 在 `application.properties` 中配置: ```properties server.servlet.session.timeout=30m ``` ### Q26: 如何配置反向代理(Nginx)? **A:** Nginx 配置示例: ```nginx server { listen 80; server_name your-domain.com; location / { proxy_pass http://localhost:28089; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } } ``` --- ## 性能优化相关 ### Q27: 如何优化数据库查询性能? **A:** 1. 为常用查询字段添加索引 2. 使用分页查询避免一次性加载大量数据 3. 合理使用缓存(如 Redis) 4. 优化 SQL 语句,避免 N+1 查询 ### Q28: 如何添加缓存? **A:** 可以集成 Redis: 1. 添加 spring-boot-starter-data-redis 依赖 2. 配置 Redis 连接信息 3. 使用 @Cacheable 注解标注需要缓存的方法 ### Q29: 如何监控应用状态? **A:** 可以集成 Spring Boot Actuator: 1. 添加 spring-boot-starter-actuator 依赖 2. 配置端点访问权限 3. 访问 `/actuator/health` 查看健康状态 --- ## 安全相关 ### Q30: 密码是如何加密的? **A:** 项目使用 MD5 加密存储密码,相关代码在 `MD5Util` 工具类中。建议生产环境使用更安全的加密方式如 BCrypt。 ### Q31: 如何防止 SQL 注入? **A:** 项目使用 MyBatis 的参数绑定(PreparedStatement),可以有效防止 SQL 注入。 ### Q32: Session 安全如何保障? **A:** 1. 设置合理的 Session 超时时间 2. 使用 HTTPS 传输 3. 关键操作添加二次验证 4. 登录拦截器验证用户身份 --- ## 常见错误排查 ### Q33: 验证码显示不出来? **A:** 1. 检查 hutool-captcha 依赖是否正确引入 2. 检查浏览器是否禁用了图片加载 3. 查看控制台是否有报错信息 ### Q34: 文件上传失败? **A:** 1. 检查上传文件大小是否超过限制 2. 检查上传目录是否存在且有写权限 3. 查看日志获取详细错误信息 ### Q35: 分页查询数据不准确? **A:** 1. 检查分页参数是否正确传递 2. 检查 SQL 中的 LIMIT 和 OFFSET 是否正确 3. 验证总数查询是否与分页查询条件一致 --- ## 更多帮助 如果以上 FAQ 没有解决您的问题,可以通过以下方式获取帮助: - **GitHub Issues**: https://github.com/newbee-ltd/newbee-mall/issues - **QQ 交流群**: 796794009、719099151 - **邮箱**: 2449207463@qq.com - **掘金小册**: https://juejin.cn/book/6844733814074245133 --- **最后更新时间**: 2025-10-23 ================================================ FILE: pom.xml ================================================ 4.0.0 jar ltd.newbee.mall newbee-mall 1.0.0-SNAPSHOT newbee-mall newbee-mall是一套电商系统,包括基础版本(Spring Boot+Thymeleaf)、前后端分离版本(Spring Boot+Vue 3+Element-Plus+Vue-Router 4+Vuex 4+Vant 3) 、秒杀版本、Go语言版本、微服务版本(Spring Cloud Alibaba+Nacos+Sentinel+Seata+Spring Cloud Gateway+OpenFeign)。 前台商城系统包含首页门户、商品分类、新品上线、首页轮播、商品推荐、商品搜索、商品展示、购物车、订单结算、订单流程、个人订单管理、会员中心、帮助中心等模块。 后台管理系统包含数据面板、轮播图管理、商品管理、订单管理、会员管理、分类管理、设置等模块。 org.springframework.boot spring-boot-starter-parent 2.7.5 UTF-8 UTF-8 1.8 2.2.2 5.8.7 org.springframework.boot spring-boot-starter-thymeleaf org.springframework.boot spring-boot-starter-web org.springframework.session spring-session-core org.mybatis.spring.boot mybatis-spring-boot-starter ${mybatis.start.version} cn.hutool hutool-captcha ${hutool-captcha.version} mysql mysql-connector-java runtime org.springframework.boot spring-boot-starter-test test org.springframework.boot spring-boot-maven-plugin ================================================ FILE: src/main/java/ltd/newbee/mall/NewBeeMallApplication.java ================================================ /** * 严肃声明: * 开源版本请务必保留此注释头信息,若删除我方将保留所有法律责任追究! * 本系统已申请软件著作权,受国家版权局知识产权以及国家计算机软件著作权保护! * 可正常分享和学习源码,不得用于违法犯罪活动,违者必究! * Copyright (c) 2019-2020 十三 all rights reserved. * 版权所有,侵权必究! */ package ltd.newbee.mall; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; /** * @author 13 * @qq交流群 796794009 * @email 2449207463@qq.com * @link https://github.com/newbee-ltd */ @MapperScan("ltd.newbee.mall.dao") @SpringBootApplication public class NewBeeMallApplication { public static void main(String[] args) { SpringApplication.run(NewBeeMallApplication.class, args); } } ================================================ FILE: src/main/java/ltd/newbee/mall/common/Constants.java ================================================ /** * 严肃声明: * 开源版本请务必保留此注释头信息,若删除我方将保留所有法律责任追究! * 本系统已申请软件著作权,受国家版权局知识产权以及国家计算机软件著作权保护! * 可正常分享和学习源码,不得用于违法犯罪活动,违者必究! * Copyright (c) 2019-2020 十三 all rights reserved. * 版权所有,侵权必究! */ package ltd.newbee.mall.common; /** * @author 13 * @qq交流群 796794009 * @email 2449207463@qq.com * @link https://github.com/newbee-ltd * @apiNote 常量配置 */ public class Constants { //public final static String FILE_UPLOAD_DIC = "/opt/image/upload/";//上传文件的默认url前缀,根据部署设置自行修改 public final static String FILE_UPLOAD_DIC = "D:\\upload\\";//上传文件的默认url前缀,根据部署设置自行修改 public final static int INDEX_CAROUSEL_NUMBER = 5;//首页轮播图数量(可根据自身需求修改) public final static int INDEX_CATEGORY_NUMBER = 10;//首页一级分类的最大数量 public final static int SEARCH_CATEGORY_NUMBER = 8;//搜索页一级分类的最大数量 public final static int INDEX_GOODS_HOT_NUMBER = 4;//首页热卖商品数量 public final static int INDEX_GOODS_NEW_NUMBER = 5;//首页新品数量 public final static int INDEX_GOODS_RECOMMOND_NUMBER = 10;//首页推荐商品数量 public final static int SHOPPING_CART_ITEM_TOTAL_NUMBER = 13;//购物车中商品的最大数量(可根据自身需求修改) public final static int SHOPPING_CART_ITEM_LIMIT_NUMBER = 5;//购物车中单个商品的最大购买数量(可根据自身需求修改) public final static String MALL_VERIFY_CODE_KEY = "mallVerifyCode";//验证码key public final static String MALL_USER_SESSION_KEY = "newBeeMallUser";//session中user的key public final static int GOODS_SEARCH_PAGE_LIMIT = 10;//搜索分页的默认条数(每页10条) public final static int ORDER_SEARCH_PAGE_LIMIT = 3;//我的订单列表分页的默认条数(每页3条) public final static int SELL_STATUS_UP = 0;//商品上架状态 public final static int SELL_STATUS_DOWN = 1;//商品下架状态 } ================================================ FILE: src/main/java/ltd/newbee/mall/common/IndexConfigTypeEnum.java ================================================ /** * 严肃声明: * 开源版本请务必保留此注释头信息,若删除我方将保留所有法律责任追究! * 本系统已申请软件著作权,受国家版权局知识产权以及国家计算机软件著作权保护! * 可正常分享和学习源码,不得用于违法犯罪活动,违者必究! * Copyright (c) 2019-2020 十三 all rights reserved. * 版权所有,侵权必究! */ package ltd.newbee.mall.common; /** * @author 13 * @qq交流群 796794009 * @email 2449207463@qq.com * @link https://github.com/newbee-ltd * @apiNote 首页配置项 1-搜索框热搜 2-搜索下拉框热搜 3-(首页)热销商品 4-(首页)新品上线 5-(首页)为你推荐 */ public enum IndexConfigTypeEnum { DEFAULT(0, "DEFAULT"), INDEX_SEARCH_HOTS(1, "INDEX_SEARCH_HOTS"), INDEX_SEARCH_DOWN_HOTS(2, "INDEX_SEARCH_DOWN_HOTS"), INDEX_GOODS_HOT(3, "INDEX_GOODS_HOTS"), INDEX_GOODS_NEW(4, "INDEX_GOODS_NEW"), INDEX_GOODS_RECOMMOND(5, "INDEX_GOODS_RECOMMOND"); private int type; private String name; IndexConfigTypeEnum(int type, String name) { this.type = type; this.name = name; } public static IndexConfigTypeEnum getIndexConfigTypeEnumByType(int type) { for (IndexConfigTypeEnum indexConfigTypeEnum : IndexConfigTypeEnum.values()) { if (indexConfigTypeEnum.getType() == type) { return indexConfigTypeEnum; } } return DEFAULT; } public int getType() { return type; } public void setType(int type) { this.type = type; } public String getName() { return name; } public void setName(String name) { this.name = name; } } ================================================ FILE: src/main/java/ltd/newbee/mall/common/NewBeeMallCategoryLevelEnum.java ================================================ /** * 严肃声明: * 开源版本请务必保留此注释头信息,若删除我方将保留所有法律责任追究! * 本系统已申请软件著作权,受国家版权局知识产权以及国家计算机软件著作权保护! * 可正常分享和学习源码,不得用于违法犯罪活动,违者必究! * Copyright (c) 2019-2020 十三 all rights reserved. * 版权所有,侵权必究! */ package ltd.newbee.mall.common; /** * @author 13 * @qq交流群 796794009 * @email 2449207463@qq.com * @link https://github.com/newbee-ltd * @apiNote 分类级别 */ public enum NewBeeMallCategoryLevelEnum { DEFAULT(0, "ERROR"), LEVEL_ONE(1, "一级分类"), LEVEL_TWO(2, "二级分类"), LEVEL_THREE(3, "三级分类"); private int level; private String name; NewBeeMallCategoryLevelEnum(int level, String name) { this.level = level; this.name = name; } public static NewBeeMallCategoryLevelEnum getNewBeeMallOrderStatusEnumByLevel(int level) { for (NewBeeMallCategoryLevelEnum newBeeMallCategoryLevelEnum : NewBeeMallCategoryLevelEnum.values()) { if (newBeeMallCategoryLevelEnum.getLevel() == level) { return newBeeMallCategoryLevelEnum; } } return DEFAULT; } public int getLevel() { return level; } public void setLevel(int level) { this.level = level; } public String getName() { return name; } public void setName(String name) { this.name = name; } } ================================================ FILE: src/main/java/ltd/newbee/mall/common/NewBeeMallException.java ================================================ /** * 严肃声明: * 开源版本请务必保留此注释头信息,若删除我方将保留所有法律责任追究! * 本系统已申请软件著作权,受国家版权局知识产权以及国家计算机软件著作权保护! * 可正常分享和学习源码,不得用于违法犯罪活动,违者必究! * Copyright (c) 2019-2020 十三 all rights reserved. * 版权所有,侵权必究! */ package ltd.newbee.mall.common; public class NewBeeMallException extends RuntimeException { public NewBeeMallException() { } public NewBeeMallException(String message) { super(message); } /** * 丢出一个异常 * * @param message */ public static void fail(String message) { throw new NewBeeMallException(message); } } ================================================ FILE: src/main/java/ltd/newbee/mall/common/NewBeeMallOrderStatusEnum.java ================================================ /** * 严肃声明: * 开源版本请务必保留此注释头信息,若删除我方将保留所有法律责任追究! * 本系统已申请软件著作权,受国家版权局知识产权以及国家计算机软件著作权保护! * 可正常分享和学习源码,不得用于违法犯罪活动,违者必究! * Copyright (c) 2019-2020 十三 all rights reserved. * 版权所有,侵权必究! */ package ltd.newbee.mall.common; /** * @author 13 * @qq交流群 796794009 * @email 2449207463@qq.com * @link https://github.com/newbee-ltd * @apiNote 订单状态:0.待支付 1.已支付 2.配货完成 3:出库成功 4.交易成功 -1.手动关闭 -2.超时关闭 -3.商家关闭 */ public enum NewBeeMallOrderStatusEnum { DEFAULT(-9, "ERROR"), ORDER_PRE_PAY(0, "待支付"), ORDER_PAID(1, "已支付"), ORDER_PACKAGED(2, "配货完成"), ORDER_EXPRESS(3, "出库成功"), ORDER_SUCCESS(4, "交易成功"), ORDER_CLOSED_BY_MALLUSER(-1, "手动关闭"), ORDER_CLOSED_BY_EXPIRED(-2, "超时关闭"), ORDER_CLOSED_BY_JUDGE(-3, "商家关闭"); private int orderStatus; private String name; NewBeeMallOrderStatusEnum(int orderStatus, String name) { this.orderStatus = orderStatus; this.name = name; } public static NewBeeMallOrderStatusEnum getNewBeeMallOrderStatusEnumByStatus(int orderStatus) { for (NewBeeMallOrderStatusEnum newBeeMallOrderStatusEnum : NewBeeMallOrderStatusEnum.values()) { if (newBeeMallOrderStatusEnum.getOrderStatus() == orderStatus) { return newBeeMallOrderStatusEnum; } } return DEFAULT; } public int getOrderStatus() { return orderStatus; } public void setOrderStatus(int orderStatus) { this.orderStatus = orderStatus; } public String getName() { return name; } public void setName(String name) { this.name = name; } } ================================================ FILE: src/main/java/ltd/newbee/mall/common/PayStatusEnum.java ================================================ /** * 严肃声明: * 开源版本请务必保留此注释头信息,若删除我方将保留所有法律责任追究! * 本系统已申请软件著作权,受国家版权局知识产权以及国家计算机软件著作权保护! * 可正常分享和学习源码,不得用于违法犯罪活动,违者必究! * Copyright (c) 2019-2020 十三 all rights reserved. * 版权所有,侵权必究! */ package ltd.newbee.mall.common; /** * @author 13 * @qq交流群 796794009 * @email 2449207463@qq.com * @link https://github.com/newbee-ltd * @apiNote 订单状态:0.支付中 1.支付成功 -1.支付失败 */ public enum PayStatusEnum { DEFAULT(-1, "支付失败"), PAY_ING(0, "支付中"), PAY_SUCCESS(1, "支付成功"); private int payStatus; private String name; PayStatusEnum(int payStatus, String name) { this.payStatus = payStatus; this.name = name; } public static PayStatusEnum getPayStatusEnumByStatus(int payStatus) { for (PayStatusEnum payStatusEnum : PayStatusEnum.values()) { if (payStatusEnum.getPayStatus() == payStatus) { return payStatusEnum; } } return DEFAULT; } public int getPayStatus() { return payStatus; } public void setPayStatus(int payStatus) { this.payStatus = payStatus; } public String getName() { return name; } public void setName(String name) { this.name = name; } } ================================================ FILE: src/main/java/ltd/newbee/mall/common/PayTypeEnum.java ================================================ /** * 严肃声明: * 开源版本请务必保留此注释头信息,若删除我方将保留所有法律责任追究! * 本系统已申请软件著作权,受国家版权局知识产权以及国家计算机软件著作权保护! * 可正常分享和学习源码,不得用于违法犯罪活动,违者必究! * Copyright (c) 2019-2020 十三 all rights reserved. * 版权所有,侵权必究! */ package ltd.newbee.mall.common; /** * @author 13 * @qq交流群 796794009 * @email 2449207463@qq.com * @link https://github.com/newbee-ltd * @apiNote 订单状态:0.无 1.支付宝 2.微信支付 */ public enum PayTypeEnum { DEFAULT(-1, "ERROR"), NOT_PAY(0, "无"), ALI_PAY(1, "支付宝"), WEIXIN_PAY(2, "微信支付"); private int payType; private String name; PayTypeEnum(int payType, String name) { this.payType = payType; this.name = name; } public static PayTypeEnum getPayTypeEnumByType(int payType) { for (PayTypeEnum payTypeEnum : PayTypeEnum.values()) { if (payTypeEnum.getPayType() == payType) { return payTypeEnum; } } return DEFAULT; } public int getPayType() { return payType; } public void setPayType(int payType) { this.payType = payType; } public String getName() { return name; } public void setName(String name) { this.name = name; } } ================================================ FILE: src/main/java/ltd/newbee/mall/common/ServiceResultEnum.java ================================================ /** * 严肃声明: * 开源版本请务必保留此注释头信息,若删除我方将保留所有法律责任追究! * 本系统已申请软件著作权,受国家版权局知识产权以及国家计算机软件著作权保护! * 可正常分享和学习源码,不得用于违法犯罪活动,违者必究! * Copyright (c) 2019-2020 十三 all rights reserved. * 版权所有,侵权必究! */ package ltd.newbee.mall.common; /** * @author 13 * @qq交流群 796794009 * @email 2449207463@qq.com * @link https://github.com/newbee-ltd */ public enum ServiceResultEnum { ERROR("error"), SUCCESS("success"), DATA_NOT_EXIST("未查询到记录!"), SAME_CATEGORY_EXIST("已存在同级同名的分类!"), SAME_LOGIN_NAME_EXIST("用户名已存在!"), LOGIN_NAME_NULL("请输入登录名!"), LOGIN_PASSWORD_NULL("请输入密码!"), LOGIN_VERIFY_CODE_NULL("请输入验证码!"), LOGIN_VERIFY_CODE_ERROR("验证码错误!"), SAME_INDEX_CONFIG_EXIST("已存在相同的首页配置项!"), GOODS_CATEGORY_ERROR("分类数据异常!"), SAME_GOODS_EXIST("已存在相同的商品信息!"), GOODS_NOT_EXIST("商品不存在!"), GOODS_PUT_DOWN("商品已下架!"), SHOPPING_CART_ITEM_LIMIT_NUMBER_ERROR("超出单个商品的最大购买数量!"), SHOPPING_CART_ITEM_TOTAL_NUMBER_ERROR("超出购物车最大容量!"), LOGIN_ERROR("登录失败!"), LOGIN_USER_LOCKED("用户已被禁止登录!"), ORDER_NOT_EXIST_ERROR("订单不存在!"), ORDER_ITEM_NOT_EXIST_ERROR("订单项不存在!"), NULL_ADDRESS_ERROR("地址不能为空!"), ORDER_PRICE_ERROR("订单价格异常!"), ORDER_GENERATE_ERROR("生成订单异常!"), SHOPPING_ITEM_ERROR("购物车数据异常!"), SHOPPING_ITEM_COUNT_ERROR("库存不足!"), ORDER_STATUS_ERROR("订单状态异常!"), CLOSE_ORDER_ERROR("关闭订单失败!"), OPERATE_ERROR("操作失败!"), NO_PERMISSION_ERROR("无权限!"), DB_ERROR("database error"); private String result; ServiceResultEnum(String result) { this.result = result; } public String getResult() { return result; } public void setResult(String result) { this.result = result; } } ================================================ FILE: src/main/java/ltd/newbee/mall/config/NeeBeeMallWebMvcConfigurer.java ================================================ /** * 严肃声明: * 开源版本请务必保留此注释头信息,若删除我方将保留所有法律责任追究! * 本系统已申请软件著作权,受国家版权局知识产权以及国家计算机软件著作权保护! * 可正常分享和学习源码,不得用于违法犯罪活动,违者必究! * Copyright (c) 2019-2020 十三 all rights reserved. * 版权所有,侵权必究! */ package ltd.newbee.mall.config; import ltd.newbee.mall.common.Constants; import ltd.newbee.mall.interceptor.AdminLoginInterceptor; import ltd.newbee.mall.interceptor.NewBeeMallCartNumberInterceptor; import ltd.newbee.mall.interceptor.NewBeeMallLoginInterceptor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration public class NeeBeeMallWebMvcConfigurer implements WebMvcConfigurer { @Autowired private AdminLoginInterceptor adminLoginInterceptor; @Autowired private NewBeeMallLoginInterceptor newBeeMallLoginInterceptor; @Autowired private NewBeeMallCartNumberInterceptor newBeeMallCartNumberInterceptor; public void addInterceptors(InterceptorRegistry registry) { // 添加一个拦截器,拦截以/admin为前缀的url路径(后台登陆拦截) registry.addInterceptor(adminLoginInterceptor) .addPathPatterns("/admin/**") .excludePathPatterns("/admin/login") .excludePathPatterns("/admin/dist/**") .excludePathPatterns("/admin/plugins/**"); // 购物车中的数量统一处理 registry.addInterceptor(newBeeMallCartNumberInterceptor) .excludePathPatterns("/admin/**") .excludePathPatterns("/register") .excludePathPatterns("/login") .excludePathPatterns("/logout"); // 商城页面登陆拦截 registry.addInterceptor(newBeeMallLoginInterceptor) .excludePathPatterns("/admin/**") .excludePathPatterns("/register") .excludePathPatterns("/login") .excludePathPatterns("/logout") .addPathPatterns("/goods/detail/**") .addPathPatterns("/shop-cart") .addPathPatterns("/shop-cart/**") .addPathPatterns("/saveOrder") .addPathPatterns("/orders") .addPathPatterns("/orders/**") .addPathPatterns("/personal") .addPathPatterns("/personal/updateInfo") .addPathPatterns("/selectPayType") .addPathPatterns("/payPage"); } public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/upload/**").addResourceLocations("file:" + Constants.FILE_UPLOAD_DIC); registry.addResourceHandler("/goods-img/**").addResourceLocations("file:" + Constants.FILE_UPLOAD_DIC); } } ================================================ FILE: src/main/java/ltd/newbee/mall/controller/admin/AdminController.java ================================================ /** * 严肃声明: * 开源版本请务必保留此注释头信息,若删除我方将保留所有法律责任追究! * 本系统已申请软件著作权,受国家版权局知识产权以及国家计算机软件著作权保护! * 可正常分享和学习源码,不得用于违法犯罪活动,违者必究! * Copyright (c) 2019-2020 十三 all rights reserved. * 版权所有,侵权必究! */ package ltd.newbee.mall.controller.admin; import cn.hutool.captcha.ShearCaptcha; import ltd.newbee.mall.common.ServiceResultEnum; import ltd.newbee.mall.entity.AdminUser; import ltd.newbee.mall.service.AdminUserService; import org.springframework.stereotype.Controller; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; /** * @author 13 * @qq交流群 796794009 * @email 2449207463@qq.com * @link https://github.com/newbee-ltd */ @Controller @RequestMapping("/admin") public class AdminController { @Resource private AdminUserService adminUserService; @GetMapping({"/login"}) public String login() { return "admin/login"; } @GetMapping({"/test"}) public String test() { return "admin/test"; } @GetMapping({"", "/", "/index", "/index.html"}) public String index(HttpServletRequest request) { request.setAttribute("path", "index"); return "admin/index"; } @PostMapping(value = "/login") public String login(@RequestParam("userName") String userName, @RequestParam("password") String password, @RequestParam("verifyCode") String verifyCode, HttpSession session) { if (!StringUtils.hasText(verifyCode)) { session.setAttribute("errorMsg", "验证码不能为空"); return "admin/login"; } if (!StringUtils.hasText(userName) || !StringUtils.hasText(password)) { session.setAttribute("errorMsg", "用户名或密码不能为空"); return "admin/login"; } ShearCaptcha shearCaptcha = (ShearCaptcha) session.getAttribute("verifyCode"); if (shearCaptcha == null || !shearCaptcha.verify(verifyCode)) { session.setAttribute("errorMsg", "验证码错误"); return "admin/login"; } AdminUser adminUser = adminUserService.login(userName, password); if (adminUser != null) { session.setAttribute("loginUser", adminUser.getNickName()); session.setAttribute("loginUserId", adminUser.getAdminUserId()); //session过期时间设置为7200秒 即两小时 //session.setMaxInactiveInterval(60 * 60 * 2); return "redirect:/admin/index"; } else { session.setAttribute("errorMsg", "登录失败"); return "admin/login"; } } @GetMapping("/profile") public String profile(HttpServletRequest request) { Integer loginUserId = (int) request.getSession().getAttribute("loginUserId"); AdminUser adminUser = adminUserService.getUserDetailById(loginUserId); if (adminUser == null) { return "admin/login"; } request.setAttribute("path", "profile"); request.setAttribute("loginUserName", adminUser.getLoginUserName()); request.setAttribute("nickName", adminUser.getNickName()); return "admin/profile"; } @PostMapping("/profile/password") @ResponseBody public String passwordUpdate(HttpServletRequest request, @RequestParam("originalPassword") String originalPassword, @RequestParam("newPassword") String newPassword) { if (!StringUtils.hasText(originalPassword) || !StringUtils.hasText(newPassword)) { return "参数不能为空"; } Integer loginUserId = (int) request.getSession().getAttribute("loginUserId"); if (adminUserService.updatePassword(loginUserId, originalPassword, newPassword)) { //修改成功后清空session中的数据,前端控制跳转至登录页 request.getSession().removeAttribute("loginUserId"); request.getSession().removeAttribute("loginUser"); request.getSession().removeAttribute("errorMsg"); return ServiceResultEnum.SUCCESS.getResult(); } else { return "修改失败"; } } @PostMapping("/profile/name") @ResponseBody public String nameUpdate(HttpServletRequest request, @RequestParam("loginUserName") String loginUserName, @RequestParam("nickName") String nickName) { if (!StringUtils.hasText(loginUserName) || !StringUtils.hasText(nickName)) { return "参数不能为空"; } Integer loginUserId = (int) request.getSession().getAttribute("loginUserId"); if (adminUserService.updateName(loginUserId, loginUserName, nickName)) { return ServiceResultEnum.SUCCESS.getResult(); } else { return "修改失败"; } } @GetMapping("/logout") public String logout(HttpServletRequest request) { request.getSession().removeAttribute("loginUserId"); request.getSession().removeAttribute("loginUser"); request.getSession().removeAttribute("errorMsg"); return "admin/login"; } } ================================================ FILE: src/main/java/ltd/newbee/mall/controller/admin/NewBeeMallCarouselController.java ================================================ /** * 严肃声明: * 开源版本请务必保留此注释头信息,若删除我方将保留所有法律责任追究! * 本系统已申请软件著作权,受国家版权局知识产权以及国家计算机软件著作权保护! * 可正常分享和学习源码,不得用于违法犯罪活动,违者必究! * Copyright (c) 2019-2020 十三 all rights reserved. * 版权所有,侵权必究! */ package ltd.newbee.mall.controller.admin; import ltd.newbee.mall.common.ServiceResultEnum; import ltd.newbee.mall.entity.Carousel; import ltd.newbee.mall.service.NewBeeMallCarouselService; import ltd.newbee.mall.util.PageQueryUtil; import ltd.newbee.mall.util.Result; import ltd.newbee.mall.util.ResultGenerator; import org.springframework.stereotype.Controller; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import java.util.Map; import java.util.Objects; /** * @author 13 * @qq交流群 796794009 * @email 2449207463@qq.com * @link https://github.com/newbee-ltd */ @Controller @RequestMapping("/admin") public class NewBeeMallCarouselController { @Resource NewBeeMallCarouselService newBeeMallCarouselService; @GetMapping("/carousels") public String carouselPage(HttpServletRequest request) { request.setAttribute("path", "newbee_mall_carousel"); return "admin/newbee_mall_carousel"; } /** * 列表 */ @RequestMapping(value = "/carousels/list", method = RequestMethod.GET) @ResponseBody public Result list(@RequestParam Map params) { if (ObjectUtils.isEmpty(params.get("page")) || ObjectUtils.isEmpty(params.get("limit"))) { return ResultGenerator.genFailResult("参数异常!"); } PageQueryUtil pageUtil = new PageQueryUtil(params); return ResultGenerator.genSuccessResult(newBeeMallCarouselService.getCarouselPage(pageUtil)); } /** * 添加 */ @RequestMapping(value = "/carousels/save", method = RequestMethod.POST) @ResponseBody public Result save(@RequestBody Carousel carousel) { if (!StringUtils.hasText(carousel.getCarouselUrl()) || Objects.isNull(carousel.getCarouselRank())) { return ResultGenerator.genFailResult("参数异常!"); } String result = newBeeMallCarouselService.saveCarousel(carousel); if (ServiceResultEnum.SUCCESS.getResult().equals(result)) { return ResultGenerator.genSuccessResult(); } else { return ResultGenerator.genFailResult(result); } } /** * 修改 */ @RequestMapping(value = "/carousels/update", method = RequestMethod.POST) @ResponseBody public Result update(@RequestBody Carousel carousel) { if (Objects.isNull(carousel.getCarouselId()) || !StringUtils.hasText(carousel.getCarouselUrl()) || Objects.isNull(carousel.getCarouselRank())) { return ResultGenerator.genFailResult("参数异常!"); } String result = newBeeMallCarouselService.updateCarousel(carousel); if (ServiceResultEnum.SUCCESS.getResult().equals(result)) { return ResultGenerator.genSuccessResult(); } else { return ResultGenerator.genFailResult(result); } } /** * 详情 */ @GetMapping("/carousels/info/{id}") @ResponseBody public Result info(@PathVariable("id") Integer id) { Carousel carousel = newBeeMallCarouselService.getCarouselById(id); if (carousel == null) { return ResultGenerator.genFailResult(ServiceResultEnum.DATA_NOT_EXIST.getResult()); } return ResultGenerator.genSuccessResult(carousel); } /** * 删除 */ @RequestMapping(value = "/carousels/delete", method = RequestMethod.POST) @ResponseBody public Result delete(@RequestBody Integer[] ids) { if (ids.length < 1) { return ResultGenerator.genFailResult("参数异常!"); } if (newBeeMallCarouselService.deleteBatch(ids)) { return ResultGenerator.genSuccessResult(); } else { return ResultGenerator.genFailResult("删除失败"); } } } ================================================ FILE: src/main/java/ltd/newbee/mall/controller/admin/NewBeeMallGoodsCategoryController.java ================================================ /** * 严肃声明: * 开源版本请务必保留此注释头信息,若删除我方将保留所有法律责任追究! * 本系统已申请软件著作权,受国家版权局知识产权以及国家计算机软件著作权保护! * 可正常分享和学习源码,不得用于违法犯罪活动,违者必究! * Copyright (c) 2019-2020 十三 all rights reserved. * 版权所有,侵权必究! */ package ltd.newbee.mall.controller.admin; import ltd.newbee.mall.common.NewBeeMallCategoryLevelEnum; import ltd.newbee.mall.common.NewBeeMallException; import ltd.newbee.mall.common.ServiceResultEnum; import ltd.newbee.mall.entity.GoodsCategory; import ltd.newbee.mall.service.NewBeeMallCategoryService; import ltd.newbee.mall.util.PageQueryUtil; import ltd.newbee.mall.util.Result; import ltd.newbee.mall.util.ResultGenerator; import org.springframework.stereotype.Controller; import org.springframework.util.CollectionUtils; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import java.util.*; /** * @author 13 * @qq交流群 796794009 * @email 2449207463@qq.com * @link https://github.com/newbee-ltd */ @Controller @RequestMapping("/admin") public class NewBeeMallGoodsCategoryController { @Resource private NewBeeMallCategoryService newBeeMallCategoryService; @GetMapping("/categories") public String categoriesPage(HttpServletRequest request, @RequestParam("categoryLevel") Byte categoryLevel, @RequestParam("parentId") Long parentId, @RequestParam("backParentId") Long backParentId) { if (categoryLevel == null || categoryLevel < 1 || categoryLevel > 3) { NewBeeMallException.fail("参数异常"); } request.setAttribute("path", "newbee_mall_category"); request.setAttribute("parentId", parentId); request.setAttribute("backParentId", backParentId); request.setAttribute("categoryLevel", categoryLevel); return "admin/newbee_mall_category"; } /** * 列表 */ @RequestMapping(value = "/categories/list", method = RequestMethod.GET) @ResponseBody public Result list(@RequestParam Map params) { if (ObjectUtils.isEmpty(params.get("page")) || ObjectUtils.isEmpty(params.get("limit")) || ObjectUtils.isEmpty(params.get("categoryLevel")) || ObjectUtils.isEmpty(params.get("parentId"))) { return ResultGenerator.genFailResult("参数异常!"); } PageQueryUtil pageUtil = new PageQueryUtil(params); return ResultGenerator.genSuccessResult(newBeeMallCategoryService.getCategorisPage(pageUtil)); } /** * 列表 */ @RequestMapping(value = "/categories/listForSelect", method = RequestMethod.GET) @ResponseBody public Result listForSelect(@RequestParam("categoryId") Long categoryId) { if (categoryId == null || categoryId < 1) { return ResultGenerator.genFailResult("缺少参数!"); } GoodsCategory category = newBeeMallCategoryService.getGoodsCategoryById(categoryId); //既不是一级分类也不是二级分类则为不返回数据 if (category == null || category.getCategoryLevel() == NewBeeMallCategoryLevelEnum.LEVEL_THREE.getLevel()) { return ResultGenerator.genFailResult("参数异常!"); } Map categoryResult = new HashMap(4); if (category.getCategoryLevel() == NewBeeMallCategoryLevelEnum.LEVEL_ONE.getLevel()) { //如果是一级分类则返回当前一级分类下的所有二级分类,以及二级分类列表中第一条数据下的所有三级分类列表 //查询一级分类列表中第一个实体的所有二级分类 List secondLevelCategories = newBeeMallCategoryService.selectByLevelAndParentIdsAndNumber(Collections.singletonList(categoryId), NewBeeMallCategoryLevelEnum.LEVEL_TWO.getLevel()); if (!CollectionUtils.isEmpty(secondLevelCategories)) { //查询二级分类列表中第一个实体的所有三级分类 List thirdLevelCategories = newBeeMallCategoryService.selectByLevelAndParentIdsAndNumber(Collections.singletonList(secondLevelCategories.get(0).getCategoryId()), NewBeeMallCategoryLevelEnum.LEVEL_THREE.getLevel()); categoryResult.put("secondLevelCategories", secondLevelCategories); categoryResult.put("thirdLevelCategories", thirdLevelCategories); } } if (category.getCategoryLevel() == NewBeeMallCategoryLevelEnum.LEVEL_TWO.getLevel()) { //如果是二级分类则返回当前分类下的所有三级分类列表 List thirdLevelCategories = newBeeMallCategoryService.selectByLevelAndParentIdsAndNumber(Collections.singletonList(categoryId), NewBeeMallCategoryLevelEnum.LEVEL_THREE.getLevel()); categoryResult.put("thirdLevelCategories", thirdLevelCategories); } return ResultGenerator.genSuccessResult(categoryResult); } /** * 添加 */ @RequestMapping(value = "/categories/save", method = RequestMethod.POST) @ResponseBody public Result save(@RequestBody GoodsCategory goodsCategory) { if (Objects.isNull(goodsCategory.getCategoryLevel()) || !StringUtils.hasText(goodsCategory.getCategoryName()) || Objects.isNull(goodsCategory.getParentId()) || Objects.isNull(goodsCategory.getCategoryRank())) { return ResultGenerator.genFailResult("参数异常!"); } String result = newBeeMallCategoryService.saveCategory(goodsCategory); if (ServiceResultEnum.SUCCESS.getResult().equals(result)) { return ResultGenerator.genSuccessResult(); } else { return ResultGenerator.genFailResult(result); } } /** * 修改 */ @RequestMapping(value = "/categories/update", method = RequestMethod.POST) @ResponseBody public Result update(@RequestBody GoodsCategory goodsCategory) { if (Objects.isNull(goodsCategory.getCategoryId()) || Objects.isNull(goodsCategory.getCategoryLevel()) || !StringUtils.hasText(goodsCategory.getCategoryName()) || Objects.isNull(goodsCategory.getParentId()) || Objects.isNull(goodsCategory.getCategoryRank())) { return ResultGenerator.genFailResult("参数异常!"); } String result = newBeeMallCategoryService.updateGoodsCategory(goodsCategory); if (ServiceResultEnum.SUCCESS.getResult().equals(result)) { return ResultGenerator.genSuccessResult(); } else { return ResultGenerator.genFailResult(result); } } /** * 详情 */ @GetMapping("/categories/info/{id}") @ResponseBody public Result info(@PathVariable("id") Long id) { GoodsCategory goodsCategory = newBeeMallCategoryService.getGoodsCategoryById(id); if (goodsCategory == null) { return ResultGenerator.genFailResult("未查询到数据"); } return ResultGenerator.genSuccessResult(goodsCategory); } /** * 分类删除 */ @RequestMapping(value = "/categories/delete", method = RequestMethod.POST) @ResponseBody public Result delete(@RequestBody Integer[] ids) { if (ids.length < 1) { return ResultGenerator.genFailResult("参数异常!"); } if (newBeeMallCategoryService.deleteBatch(ids)) { return ResultGenerator.genSuccessResult(); } else { return ResultGenerator.genFailResult("删除失败"); } } } ================================================ FILE: src/main/java/ltd/newbee/mall/controller/admin/NewBeeMallGoodsController.java ================================================ /** * 严肃声明: * 开源版本请务必保留此注释头信息,若删除我方将保留所有法律责任追究! * 本系统已申请软件著作权,受国家版权局知识产权以及国家计算机软件著作权保护! * 可正常分享和学习源码,不得用于违法犯罪活动,违者必究! * Copyright (c) 2019-2020 十三 all rights reserved. * 版权所有,侵权必究! */ package ltd.newbee.mall.controller.admin; import ltd.newbee.mall.common.Constants; import ltd.newbee.mall.common.NewBeeMallCategoryLevelEnum; import ltd.newbee.mall.common.NewBeeMallException; import ltd.newbee.mall.common.ServiceResultEnum; import ltd.newbee.mall.entity.GoodsCategory; import ltd.newbee.mall.entity.NewBeeMallGoods; import ltd.newbee.mall.service.NewBeeMallCategoryService; import ltd.newbee.mall.service.NewBeeMallGoodsService; import ltd.newbee.mall.util.PageQueryUtil; import ltd.newbee.mall.util.Result; import ltd.newbee.mall.util.ResultGenerator; import org.springframework.stereotype.Controller; import org.springframework.util.CollectionUtils; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Objects; /** * @author 13 * @qq交流群 796794009 * @email 2449207463@qq.com * @link https://github.com/newbee-ltd */ @Controller @RequestMapping("/admin") public class NewBeeMallGoodsController { @Resource private NewBeeMallGoodsService newBeeMallGoodsService; @Resource private NewBeeMallCategoryService newBeeMallCategoryService; @GetMapping("/goods") public String goodsPage(HttpServletRequest request) { request.setAttribute("path", "newbee_mall_goods"); return "admin/newbee_mall_goods"; } @GetMapping("/goods/edit") public String edit(HttpServletRequest request) { request.setAttribute("path", "edit"); //查询所有的一级分类 List firstLevelCategories = newBeeMallCategoryService.selectByLevelAndParentIdsAndNumber(Collections.singletonList(0L), NewBeeMallCategoryLevelEnum.LEVEL_ONE.getLevel()); if (!CollectionUtils.isEmpty(firstLevelCategories)) { //查询一级分类列表中第一个实体的所有二级分类 List secondLevelCategories = newBeeMallCategoryService.selectByLevelAndParentIdsAndNumber(Collections.singletonList(firstLevelCategories.get(0).getCategoryId()), NewBeeMallCategoryLevelEnum.LEVEL_TWO.getLevel()); if (!CollectionUtils.isEmpty(secondLevelCategories)) { //查询二级分类列表中第一个实体的所有三级分类 List thirdLevelCategories = newBeeMallCategoryService.selectByLevelAndParentIdsAndNumber(Collections.singletonList(secondLevelCategories.get(0).getCategoryId()), NewBeeMallCategoryLevelEnum.LEVEL_THREE.getLevel()); request.setAttribute("firstLevelCategories", firstLevelCategories); request.setAttribute("secondLevelCategories", secondLevelCategories); request.setAttribute("thirdLevelCategories", thirdLevelCategories); request.setAttribute("path", "goods-edit"); return "admin/newbee_mall_goods_edit"; } } NewBeeMallException.fail("分类数据不完善"); return null; } @GetMapping("/goods/edit/{goodsId}") public String edit(HttpServletRequest request, @PathVariable("goodsId") Long goodsId) { request.setAttribute("path", "edit"); NewBeeMallGoods newBeeMallGoods = newBeeMallGoodsService.getNewBeeMallGoodsById(goodsId); if (newBeeMallGoods.getGoodsCategoryId() > 0) { if (newBeeMallGoods.getGoodsCategoryId() != null || newBeeMallGoods.getGoodsCategoryId() > 0) { //有分类字段则查询相关分类数据返回给前端以供分类的三级联动显示 GoodsCategory currentGoodsCategory = newBeeMallCategoryService.getGoodsCategoryById(newBeeMallGoods.getGoodsCategoryId()); //商品表中存储的分类id字段为三级分类的id,不为三级分类则是错误数据 if (currentGoodsCategory != null && currentGoodsCategory.getCategoryLevel() == NewBeeMallCategoryLevelEnum.LEVEL_THREE.getLevel()) { //查询所有的一级分类 List firstLevelCategories = newBeeMallCategoryService.selectByLevelAndParentIdsAndNumber(Collections.singletonList(0L), NewBeeMallCategoryLevelEnum.LEVEL_ONE.getLevel()); //根据parentId查询当前parentId下所有的三级分类 List thirdLevelCategories = newBeeMallCategoryService.selectByLevelAndParentIdsAndNumber(Collections.singletonList(currentGoodsCategory.getParentId()), NewBeeMallCategoryLevelEnum.LEVEL_THREE.getLevel()); //查询当前三级分类的父级二级分类 GoodsCategory secondCategory = newBeeMallCategoryService.getGoodsCategoryById(currentGoodsCategory.getParentId()); if (secondCategory != null) { //根据parentId查询当前parentId下所有的二级分类 List secondLevelCategories = newBeeMallCategoryService.selectByLevelAndParentIdsAndNumber(Collections.singletonList(secondCategory.getParentId()), NewBeeMallCategoryLevelEnum.LEVEL_TWO.getLevel()); //查询当前二级分类的父级一级分类 GoodsCategory firstCategory = newBeeMallCategoryService.getGoodsCategoryById(secondCategory.getParentId()); if (firstCategory != null) { //所有分类数据都得到之后放到request对象中供前端读取 request.setAttribute("firstLevelCategories", firstLevelCategories); request.setAttribute("secondLevelCategories", secondLevelCategories); request.setAttribute("thirdLevelCategories", thirdLevelCategories); request.setAttribute("firstLevelCategoryId", firstCategory.getCategoryId()); request.setAttribute("secondLevelCategoryId", secondCategory.getCategoryId()); request.setAttribute("thirdLevelCategoryId", currentGoodsCategory.getCategoryId()); } } } } } if (newBeeMallGoods.getGoodsCategoryId() == 0) { //查询所有的一级分类 List firstLevelCategories = newBeeMallCategoryService.selectByLevelAndParentIdsAndNumber(Collections.singletonList(0L), NewBeeMallCategoryLevelEnum.LEVEL_ONE.getLevel()); if (!CollectionUtils.isEmpty(firstLevelCategories)) { //查询一级分类列表中第一个实体的所有二级分类 List secondLevelCategories = newBeeMallCategoryService.selectByLevelAndParentIdsAndNumber(Collections.singletonList(firstLevelCategories.get(0).getCategoryId()), NewBeeMallCategoryLevelEnum.LEVEL_TWO.getLevel()); if (!CollectionUtils.isEmpty(secondLevelCategories)) { //查询二级分类列表中第一个实体的所有三级分类 List thirdLevelCategories = newBeeMallCategoryService.selectByLevelAndParentIdsAndNumber(Collections.singletonList(secondLevelCategories.get(0).getCategoryId()), NewBeeMallCategoryLevelEnum.LEVEL_THREE.getLevel()); request.setAttribute("firstLevelCategories", firstLevelCategories); request.setAttribute("secondLevelCategories", secondLevelCategories); request.setAttribute("thirdLevelCategories", thirdLevelCategories); } } } request.setAttribute("goods", newBeeMallGoods); request.setAttribute("path", "goods-edit"); return "admin/newbee_mall_goods_edit"; } /** * 列表 */ @RequestMapping(value = "/goods/list", method = RequestMethod.GET) @ResponseBody public Result list(@RequestParam Map params) { if (ObjectUtils.isEmpty(params.get("page")) || ObjectUtils.isEmpty(params.get("limit"))) { return ResultGenerator.genFailResult("参数异常!"); } PageQueryUtil pageUtil = new PageQueryUtil(params); return ResultGenerator.genSuccessResult(newBeeMallGoodsService.getNewBeeMallGoodsPage(pageUtil)); } /** * 添加 */ @RequestMapping(value = "/goods/save", method = RequestMethod.POST) @ResponseBody public Result save(@RequestBody NewBeeMallGoods newBeeMallGoods) { if (!StringUtils.hasText(newBeeMallGoods.getGoodsName()) || !StringUtils.hasText(newBeeMallGoods.getGoodsIntro()) || !StringUtils.hasText(newBeeMallGoods.getTag()) || Objects.isNull(newBeeMallGoods.getOriginalPrice()) || Objects.isNull(newBeeMallGoods.getGoodsCategoryId()) || Objects.isNull(newBeeMallGoods.getSellingPrice()) || Objects.isNull(newBeeMallGoods.getStockNum()) || Objects.isNull(newBeeMallGoods.getGoodsSellStatus()) || !StringUtils.hasText(newBeeMallGoods.getGoodsCoverImg()) || !StringUtils.hasText(newBeeMallGoods.getGoodsDetailContent())) { return ResultGenerator.genFailResult("参数异常!"); } String result = newBeeMallGoodsService.saveNewBeeMallGoods(newBeeMallGoods); if (ServiceResultEnum.SUCCESS.getResult().equals(result)) { return ResultGenerator.genSuccessResult(); } else { return ResultGenerator.genFailResult(result); } } /** * 修改 */ @RequestMapping(value = "/goods/update", method = RequestMethod.POST) @ResponseBody public Result update(@RequestBody NewBeeMallGoods newBeeMallGoods) { if (Objects.isNull(newBeeMallGoods.getGoodsId()) || !StringUtils.hasText(newBeeMallGoods.getGoodsName()) || !StringUtils.hasText(newBeeMallGoods.getGoodsIntro()) || !StringUtils.hasText(newBeeMallGoods.getTag()) || Objects.isNull(newBeeMallGoods.getOriginalPrice()) || Objects.isNull(newBeeMallGoods.getSellingPrice()) || Objects.isNull(newBeeMallGoods.getGoodsCategoryId()) || Objects.isNull(newBeeMallGoods.getStockNum()) || Objects.isNull(newBeeMallGoods.getGoodsSellStatus()) || !StringUtils.hasText(newBeeMallGoods.getGoodsCoverImg()) || !StringUtils.hasText(newBeeMallGoods.getGoodsDetailContent())) { return ResultGenerator.genFailResult("参数异常!"); } String result = newBeeMallGoodsService.updateNewBeeMallGoods(newBeeMallGoods); if (ServiceResultEnum.SUCCESS.getResult().equals(result)) { return ResultGenerator.genSuccessResult(); } else { return ResultGenerator.genFailResult(result); } } /** * 详情 */ @GetMapping("/goods/info/{id}") @ResponseBody public Result info(@PathVariable("id") Long id) { NewBeeMallGoods goods = newBeeMallGoodsService.getNewBeeMallGoodsById(id); return ResultGenerator.genSuccessResult(goods); } /** * 批量修改销售状态 */ @RequestMapping(value = "/goods/status/{sellStatus}", method = RequestMethod.PUT) @ResponseBody public Result delete(@RequestBody Long[] ids, @PathVariable("sellStatus") int sellStatus) { if (ids.length < 1) { return ResultGenerator.genFailResult("参数异常!"); } if (sellStatus != Constants.SELL_STATUS_UP && sellStatus != Constants.SELL_STATUS_DOWN) { return ResultGenerator.genFailResult("状态异常!"); } if (newBeeMallGoodsService.batchUpdateSellStatus(ids, sellStatus)) { return ResultGenerator.genSuccessResult(); } else { return ResultGenerator.genFailResult("修改失败"); } } } ================================================ FILE: src/main/java/ltd/newbee/mall/controller/admin/NewBeeMallGoodsIndexConfigController.java ================================================ /** * 严肃声明: * 开源版本请务必保留此注释头信息,若删除我方将保留所有法律责任追究! * 本系统已申请软件著作权,受国家版权局知识产权以及国家计算机软件著作权保护! * 可正常分享和学习源码,不得用于违法犯罪活动,违者必究! * Copyright (c) 2019-2020 十三 all rights reserved. * 版权所有,侵权必究! */ package ltd.newbee.mall.controller.admin; import ltd.newbee.mall.common.IndexConfigTypeEnum; import ltd.newbee.mall.common.NewBeeMallException; import ltd.newbee.mall.common.ServiceResultEnum; import ltd.newbee.mall.entity.IndexConfig; import ltd.newbee.mall.service.NewBeeMallIndexConfigService; import ltd.newbee.mall.util.PageQueryUtil; import ltd.newbee.mall.util.Result; import ltd.newbee.mall.util.ResultGenerator; import org.springframework.stereotype.Controller; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import java.util.Map; import java.util.Objects; /** * @author 13 * @qq交流群 796794009 * @email 2449207463@qq.com * @link https://github.com/newbee-ltd */ @Controller @RequestMapping("/admin") public class NewBeeMallGoodsIndexConfigController { @Resource private NewBeeMallIndexConfigService newBeeMallIndexConfigService; @GetMapping("/indexConfigs") public String indexConfigsPage(HttpServletRequest request, @RequestParam("configType") int configType) { IndexConfigTypeEnum indexConfigTypeEnum = IndexConfigTypeEnum.getIndexConfigTypeEnumByType(configType); if (indexConfigTypeEnum.equals(IndexConfigTypeEnum.DEFAULT)) { NewBeeMallException.fail("参数异常"); } request.setAttribute("path", indexConfigTypeEnum.getName()); request.setAttribute("configType", configType); return "admin/newbee_mall_index_config"; } /** * 列表 */ @RequestMapping(value = "/indexConfigs/list", method = RequestMethod.GET) @ResponseBody public Result list(@RequestParam Map params) { if (ObjectUtils.isEmpty(params.get("page")) || ObjectUtils.isEmpty(params.get("limit"))) { return ResultGenerator.genFailResult("参数异常!"); } PageQueryUtil pageUtil = new PageQueryUtil(params); return ResultGenerator.genSuccessResult(newBeeMallIndexConfigService.getConfigsPage(pageUtil)); } /** * 添加 */ @RequestMapping(value = "/indexConfigs/save", method = RequestMethod.POST) @ResponseBody public Result save(@RequestBody IndexConfig indexConfig) { if (Objects.isNull(indexConfig.getConfigType()) || !StringUtils.hasText(indexConfig.getConfigName()) || Objects.isNull(indexConfig.getConfigRank())) { return ResultGenerator.genFailResult("参数异常!"); } String result = newBeeMallIndexConfigService.saveIndexConfig(indexConfig); if (ServiceResultEnum.SUCCESS.getResult().equals(result)) { return ResultGenerator.genSuccessResult(); } else { return ResultGenerator.genFailResult(result); } } /** * 修改 */ @RequestMapping(value = "/indexConfigs/update", method = RequestMethod.POST) @ResponseBody public Result update(@RequestBody IndexConfig indexConfig) { if (Objects.isNull(indexConfig.getConfigType()) || Objects.isNull(indexConfig.getConfigId()) || !StringUtils.hasText(indexConfig.getConfigName()) || Objects.isNull(indexConfig.getConfigRank())) { return ResultGenerator.genFailResult("参数异常!"); } String result = newBeeMallIndexConfigService.updateIndexConfig(indexConfig); if (ServiceResultEnum.SUCCESS.getResult().equals(result)) { return ResultGenerator.genSuccessResult(); } else { return ResultGenerator.genFailResult(result); } } /** * 详情 */ @GetMapping("/indexConfigs/info/{id}") @ResponseBody public Result info(@PathVariable("id") Long id) { IndexConfig config = newBeeMallIndexConfigService.getIndexConfigById(id); if (config == null) { return ResultGenerator.genFailResult("未查询到数据"); } return ResultGenerator.genSuccessResult(config); } /** * 删除 */ @RequestMapping(value = "/indexConfigs/delete", method = RequestMethod.POST) @ResponseBody public Result delete(@RequestBody Long[] ids) { if (ids.length < 1) { return ResultGenerator.genFailResult("参数异常!"); } if (newBeeMallIndexConfigService.deleteBatch(ids)) { return ResultGenerator.genSuccessResult(); } else { return ResultGenerator.genFailResult("删除失败"); } } } ================================================ FILE: src/main/java/ltd/newbee/mall/controller/admin/NewBeeMallOrderController.java ================================================ /** * 严肃声明: * 开源版本请务必保留此注释头信息,若删除我方将保留所有法律责任追究! * 本系统已申请软件著作权,受国家版权局知识产权以及国家计算机软件著作权保护! * 可正常分享和学习源码,不得用于违法犯罪活动,违者必究! * Copyright (c) 2019-2020 十三 all rights reserved. * 版权所有,侵权必究! */ package ltd.newbee.mall.controller.admin; import ltd.newbee.mall.common.ServiceResultEnum; import ltd.newbee.mall.controller.vo.NewBeeMallOrderItemVO; import ltd.newbee.mall.entity.NewBeeMallOrder; import ltd.newbee.mall.service.NewBeeMallOrderService; import ltd.newbee.mall.util.PageQueryUtil; import ltd.newbee.mall.util.Result; import ltd.newbee.mall.util.ResultGenerator; import org.springframework.stereotype.Controller; import org.springframework.util.CollectionUtils; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import java.util.List; import java.util.Map; import java.util.Objects; /** * @author 13 * @qq交流群 796794009 * @email 2449207463@qq.com * @link https://github.com/newbee-ltd */ @Controller @RequestMapping("/admin") public class NewBeeMallOrderController { @Resource private NewBeeMallOrderService newBeeMallOrderService; @GetMapping("/orders") public String ordersPage(HttpServletRequest request) { request.setAttribute("path", "orders"); return "admin/newbee_mall_order"; } /** * 列表 */ @RequestMapping(value = "/orders/list", method = RequestMethod.GET) @ResponseBody public Result list(@RequestParam Map params) { if (ObjectUtils.isEmpty(params.get("page")) || ObjectUtils.isEmpty(params.get("limit"))) { return ResultGenerator.genFailResult("参数异常!"); } PageQueryUtil pageUtil = new PageQueryUtil(params); return ResultGenerator.genSuccessResult(newBeeMallOrderService.getNewBeeMallOrdersPage(pageUtil)); } /** * 修改 */ @RequestMapping(value = "/orders/update", method = RequestMethod.POST) @ResponseBody public Result update(@RequestBody NewBeeMallOrder newBeeMallOrder) { if (Objects.isNull(newBeeMallOrder.getTotalPrice()) || Objects.isNull(newBeeMallOrder.getOrderId()) || newBeeMallOrder.getOrderId() < 1 || newBeeMallOrder.getTotalPrice() < 1 || !StringUtils.hasText(newBeeMallOrder.getUserAddress())) { return ResultGenerator.genFailResult("参数异常!"); } String result = newBeeMallOrderService.updateOrderInfo(newBeeMallOrder); if (ServiceResultEnum.SUCCESS.getResult().equals(result)) { return ResultGenerator.genSuccessResult(); } else { return ResultGenerator.genFailResult(result); } } /** * 详情 */ @GetMapping("/order-items/{id}") @ResponseBody public Result info(@PathVariable("id") Long id) { List orderItems = newBeeMallOrderService.getOrderItems(id); if (!CollectionUtils.isEmpty(orderItems)) { return ResultGenerator.genSuccessResult(orderItems); } return ResultGenerator.genFailResult(ServiceResultEnum.DATA_NOT_EXIST.getResult()); } /** * 配货 */ @RequestMapping(value = "/orders/checkDone", method = RequestMethod.POST) @ResponseBody public Result checkDone(@RequestBody Long[] ids) { if (ids.length < 1) { return ResultGenerator.genFailResult("参数异常!"); } String result = newBeeMallOrderService.checkDone(ids); if (ServiceResultEnum.SUCCESS.getResult().equals(result)) { return ResultGenerator.genSuccessResult(); } else { return ResultGenerator.genFailResult(result); } } /** * 出库 */ @RequestMapping(value = "/orders/checkOut", method = RequestMethod.POST) @ResponseBody public Result checkOut(@RequestBody Long[] ids) { if (ids.length < 1) { return ResultGenerator.genFailResult("参数异常!"); } String result = newBeeMallOrderService.checkOut(ids); if (ServiceResultEnum.SUCCESS.getResult().equals(result)) { return ResultGenerator.genSuccessResult(); } else { return ResultGenerator.genFailResult(result); } } /** * 关闭订单 */ @RequestMapping(value = "/orders/close", method = RequestMethod.POST) @ResponseBody public Result closeOrder(@RequestBody Long[] ids) { if (ids.length < 1) { return ResultGenerator.genFailResult("参数异常!"); } String result = newBeeMallOrderService.closeOrder(ids); if (ServiceResultEnum.SUCCESS.getResult().equals(result)) { return ResultGenerator.genSuccessResult(); } else { return ResultGenerator.genFailResult(result); } } } ================================================ FILE: src/main/java/ltd/newbee/mall/controller/admin/NewBeeMallUserController.java ================================================ /** * 严肃声明: * 开源版本请务必保留此注释头信息,若删除我方将保留所有法律责任追究! * 本系统已申请软件著作权,受国家版权局知识产权以及国家计算机软件著作权保护! * 可正常分享和学习源码,不得用于违法犯罪活动,违者必究! * Copyright (c) 2019-2020 十三 all rights reserved. * 版权所有,侵权必究! */ package ltd.newbee.mall.controller.admin; import ltd.newbee.mall.service.NewBeeMallUserService; import ltd.newbee.mall.util.PageQueryUtil; import ltd.newbee.mall.util.Result; import ltd.newbee.mall.util.ResultGenerator; import org.springframework.stereotype.Controller; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import java.util.Map; /** * @author 13 * @qq交流群 796794009 * @email 2449207463@qq.com * @link https://github.com/newbee-ltd */ @Controller @RequestMapping("/admin") public class NewBeeMallUserController { @Resource private NewBeeMallUserService newBeeMallUserService; @GetMapping("/users") public String usersPage(HttpServletRequest request) { request.setAttribute("path", "users"); return "admin/newbee_mall_user"; } /** * 列表 */ @RequestMapping(value = "/users/list", method = RequestMethod.GET) @ResponseBody public Result list(@RequestParam Map params) { if (ObjectUtils.isEmpty(params.get("page")) || ObjectUtils.isEmpty(params.get("limit"))) { return ResultGenerator.genFailResult("参数异常!"); } PageQueryUtil pageUtil = new PageQueryUtil(params); return ResultGenerator.genSuccessResult(newBeeMallUserService.getNewBeeMallUsersPage(pageUtil)); } /** * 用户禁用与解除禁用(0-未锁定 1-已锁定) */ @RequestMapping(value = "/users/lock/{lockStatus}", method = RequestMethod.POST) @ResponseBody public Result delete(@RequestBody Integer[] ids, @PathVariable int lockStatus) { if (ids.length < 1) { return ResultGenerator.genFailResult("参数异常!"); } if (lockStatus != 0 && lockStatus != 1) { return ResultGenerator.genFailResult("操作非法!"); } if (newBeeMallUserService.lockUsers(ids, lockStatus)) { return ResultGenerator.genSuccessResult(); } else { return ResultGenerator.genFailResult("禁用失败"); } } } ================================================ FILE: src/main/java/ltd/newbee/mall/controller/common/CommonController.java ================================================ /** * 严肃声明: * 开源版本请务必保留此注释头信息,若删除我方将保留所有法律责任追究! * 本系统已申请软件著作权,受国家版权局知识产权以及国家计算机软件著作权保护! * 可正常分享和学习源码,不得用于违法犯罪活动,违者必究! * Copyright (c) 2019-2020 十三 all rights reserved. * 版权所有,侵权必究! */ package ltd.newbee.mall.controller.common; import cn.hutool.captcha.CaptchaUtil; import cn.hutool.captcha.ShearCaptcha; import ltd.newbee.mall.common.Constants; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * @author 13 * @qq交流群 796794009 * @email 2449207463@qq.com * @link https://github.com/newbee-ltd */ @Controller public class CommonController { @GetMapping("/common/kaptcha") public void defaultKaptcha(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception { httpServletResponse.setHeader("Cache-Control", "no-store"); httpServletResponse.setHeader("Pragma", "no-cache"); httpServletResponse.setDateHeader("Expires", 0); httpServletResponse.setContentType("image/png"); ShearCaptcha shearCaptcha= CaptchaUtil.createShearCaptcha(150, 30, 4, 2); // 验证码存入session httpServletRequest.getSession().setAttribute("verifyCode", shearCaptcha); // 输出图片流 shearCaptcha.write(httpServletResponse.getOutputStream()); } @GetMapping("/common/mall/kaptcha") public void mallKaptcha(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception { httpServletResponse.setHeader("Cache-Control", "no-store"); httpServletResponse.setHeader("Pragma", "no-cache"); httpServletResponse.setDateHeader("Expires", 0); httpServletResponse.setContentType("image/png"); ShearCaptcha shearCaptcha= CaptchaUtil.createShearCaptcha(110, 40, 4, 2); // 验证码存入session httpServletRequest.getSession().setAttribute(Constants.MALL_VERIFY_CODE_KEY, shearCaptcha); // 输出图片流 shearCaptcha.write(httpServletResponse.getOutputStream()); } } ================================================ FILE: src/main/java/ltd/newbee/mall/controller/common/ErrorPageController.java ================================================ /** * 严肃声明: * 开源版本请务必保留此注释头信息,若删除我方将保留所有法律责任追究! * 本系统已申请软件著作权,受国家版权局知识产权以及国家计算机软件著作权保护! * 可正常分享和学习源码,不得用于违法犯罪活动,违者必究! * Copyright (c) 2019-2020 十三 all rights reserved. * 版权所有,侵权必究! */ package ltd.newbee.mall.controller.common; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.web.servlet.error.ErrorViewResolver; import org.springframework.boot.web.servlet.error.ErrorAttributes; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Controller; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import java.util.Map; @Controller public class ErrorPageController implements ErrorViewResolver { private static ErrorPageController errorPageController; @Autowired private ErrorAttributes errorAttributes; public ErrorPageController(ErrorAttributes errorAttributes) { this.errorAttributes = errorAttributes; } public ErrorPageController() { if (errorPageController == null) { errorPageController = new ErrorPageController(errorAttributes); } } @Override public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map model) { if (HttpStatus.BAD_REQUEST == status) { return new ModelAndView("error/error_400"); } else if (HttpStatus.NOT_FOUND == status) { return new ModelAndView("error/error_404"); } else { return new ModelAndView("error/error_5xx"); } } } ================================================ FILE: src/main/java/ltd/newbee/mall/controller/common/NewBeeMallExceptionHandler.java ================================================ /** * 严肃声明: * 开源版本请务必保留此注释头信息,若删除我方将保留所有法律责任追究! * 本系统已申请软件著作权,受国家版权局知识产权以及国家计算机软件著作权保护! * 可正常分享和学习源码,不得用于违法犯罪活动,违者必究! * Copyright (c) 2019-2020 十三 all rights reserved. * 版权所有,侵权必究! */ package ltd.newbee.mall.controller.common; import ltd.newbee.mall.common.NewBeeMallException; import ltd.newbee.mall.util.Result; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; /** * newbee-mall全局异常处理 */ @RestControllerAdvice public class NewBeeMallExceptionHandler { @ExceptionHandler(Exception.class) public Object handleException(Exception e, HttpServletRequest req) { Result result = new Result(); result.setResultCode(500); //区分是否为自定义异常 if (e instanceof NewBeeMallException) { result.setMessage(e.getMessage()); } else { e.printStackTrace(); result.setMessage("未知异常"); } //检查请求是否为ajax, 如果是 ajax 请求则返回 Result json串, 如果不是 ajax 请求则返回 error 视图 String contentTypeHeader = req.getHeader("Content-Type"); String acceptHeader = req.getHeader("Accept"); String xRequestedWith = req.getHeader("X-Requested-With"); if ((contentTypeHeader != null && contentTypeHeader.contains("application/json")) || (acceptHeader != null && acceptHeader.contains("application/json")) || "XMLHttpRequest".equalsIgnoreCase(xRequestedWith)) { return result; } else { ModelAndView modelAndView = new ModelAndView(); modelAndView.addObject("message", e.getMessage()); modelAndView.addObject("url", req.getRequestURL()); modelAndView.addObject("stackTrace", e.getStackTrace()); modelAndView.addObject("author", "十三"); modelAndView.addObject("ltd", "新蜂商城"); modelAndView.setViewName("error/error"); return modelAndView; } } } ================================================ FILE: src/main/java/ltd/newbee/mall/controller/common/UploadController.java ================================================ /** * 严肃声明: * 开源版本请务必保留此注释头信息,若删除我方将保留所有法律责任追究! * 本系统已申请软件著作权,受国家版权局知识产权以及国家计算机软件著作权保护! * 可正常分享和学习源码,不得用于违法犯罪活动,违者必究! * Copyright (c) 2019-2020 十三 all rights reserved. * 版权所有,侵权必究! */ package ltd.newbee.mall.controller.common; import ltd.newbee.mall.common.Constants; import ltd.newbee.mall.util.NewBeeMallUtils; import ltd.newbee.mall.util.Result; import ltd.newbee.mall.util.ResultGenerator; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.util.CollectionUtils; import org.springframework.web.bind.annotation.PostMapping; 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 org.springframework.web.multipart.MultipartHttpServletRequest; import org.springframework.web.multipart.support.StandardServletMultipartResolver; import javax.imageio.ImageIO; import javax.servlet.http.HttpServletRequest; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.text.SimpleDateFormat; import java.util.*; /** * @author 13 * @qq交流群 796794009 * @email 2449207463@qq.com * @link https://github.com/newbee-ltd */ @Controller @RequestMapping("/admin") public class UploadController { @Autowired private StandardServletMultipartResolver standardServletMultipartResolver; @PostMapping({"/upload/file"}) @ResponseBody public Result upload(HttpServletRequest httpServletRequest, @RequestParam("file") MultipartFile file) throws URISyntaxException, IOException { String fileName = file.getOriginalFilename(); BufferedImage bufferedImage = ImageIO.read(file.getInputStream()); if (bufferedImage == null) { return ResultGenerator.genFailResult("请上传图片类型的文件"); } String suffixName = fileName.substring(fileName.lastIndexOf(".")); //生成文件名称通用方法 SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd_HHmmss"); Random r = new Random(); StringBuilder tempName = new StringBuilder(); tempName.append(sdf.format(new Date())).append(r.nextInt(100)).append(suffixName); String newFileName = tempName.toString(); File fileDirectory = new File(Constants.FILE_UPLOAD_DIC); //创建文件 File destFile = new File(Constants.FILE_UPLOAD_DIC + newFileName); try { if (!fileDirectory.exists()) { if (!fileDirectory.mkdir()) { throw new IOException("文件夹创建失败,路径为:" + fileDirectory); } } file.transferTo(destFile); Result resultSuccess = ResultGenerator.genSuccessResult(); resultSuccess.setData(NewBeeMallUtils.getHost(new URI(httpServletRequest.getRequestURL() + "")) + "/upload/" + newFileName); return resultSuccess; } catch (IOException e) { e.printStackTrace(); return ResultGenerator.genFailResult("文件上传失败"); } } @PostMapping({"/upload/files"}) @ResponseBody public Result uploadV2(HttpServletRequest httpServletRequest) throws URISyntaxException, IOException { List multipartFiles = new ArrayList<>(8); if (standardServletMultipartResolver.isMultipart(httpServletRequest)) { MultipartHttpServletRequest multiRequest = (MultipartHttpServletRequest) httpServletRequest; Iterator iter = multiRequest.getFileNames(); int total = 0; while (iter.hasNext()) { if (total > 5) { return ResultGenerator.genFailResult("最多上传5张图片"); } total += 1; MultipartFile file = multiRequest.getFile(iter.next()); BufferedImage bufferedImage = ImageIO.read(file.getInputStream()); // 只处理图片类型的文件 if (bufferedImage != null) { multipartFiles.add(file); } } } if (CollectionUtils.isEmpty(multipartFiles)) { return ResultGenerator.genFailResult("请选择图片类型的文件上传"); } if (multipartFiles != null && multipartFiles.size() > 5) { return ResultGenerator.genFailResult("最多上传5张图片"); } List fileNames = new ArrayList(multipartFiles.size()); for (int i = 0; i < multipartFiles.size(); i++) { String fileName = multipartFiles.get(i).getOriginalFilename(); String suffixName = fileName.substring(fileName.lastIndexOf(".")); //生成文件名称通用方法 SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd_HHmmss"); Random r = new Random(); StringBuilder tempName = new StringBuilder(); tempName.append(sdf.format(new Date())).append(r.nextInt(100)).append(suffixName); String newFileName = tempName.toString(); File fileDirectory = new File(Constants.FILE_UPLOAD_DIC); //创建文件 File destFile = new File(Constants.FILE_UPLOAD_DIC + newFileName); try { if (!fileDirectory.exists()) { if (!fileDirectory.mkdir()) { throw new IOException("文件夹创建失败,路径为:" + fileDirectory); } } multipartFiles.get(i).transferTo(destFile); fileNames.add(NewBeeMallUtils.getHost(new URI(httpServletRequest.getRequestURL() + "")) + "/upload/" + newFileName); } catch (IOException e) { e.printStackTrace(); return ResultGenerator.genFailResult("文件上传失败"); } } Result resultSuccess = ResultGenerator.genSuccessResult(); resultSuccess.setData(fileNames); return resultSuccess; } } ================================================ FILE: src/main/java/ltd/newbee/mall/controller/mall/GoodsController.java ================================================ /** * 严肃声明: * 开源版本请务必保留此注释头信息,若删除我方将保留所有法律责任追究! * 本系统已申请软件著作权,受国家版权局知识产权以及国家计算机软件著作权保护! * 可正常分享和学习源码,不得用于违法犯罪活动,违者必究! * Copyright (c) 2019-2020 十三 all rights reserved. * 版权所有,侵权必究! */ package ltd.newbee.mall.controller.mall; import ltd.newbee.mall.common.Constants; import ltd.newbee.mall.common.NewBeeMallException; import ltd.newbee.mall.common.ServiceResultEnum; import ltd.newbee.mall.controller.vo.NewBeeMallGoodsDetailVO; import ltd.newbee.mall.controller.vo.SearchPageCategoryVO; import ltd.newbee.mall.entity.NewBeeMallGoods; import ltd.newbee.mall.service.NewBeeMallCategoryService; import ltd.newbee.mall.service.NewBeeMallGoodsService; import ltd.newbee.mall.util.BeanUtil; import ltd.newbee.mall.util.PageQueryUtil; import org.springframework.stereotype.Controller; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestParam; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import java.util.Map; @Controller public class GoodsController { @Resource private NewBeeMallGoodsService newBeeMallGoodsService; @Resource private NewBeeMallCategoryService newBeeMallCategoryService; @GetMapping({"/search", "/search.html"}) public String searchPage(@RequestParam Map params, HttpServletRequest request) { if (ObjectUtils.isEmpty(params.get("page"))) { params.put("page", 1); } params.put("limit", Constants.GOODS_SEARCH_PAGE_LIMIT); //封装分类数据 if (params.containsKey("goodsCategoryId") && StringUtils.hasText(params.get("goodsCategoryId") + "")) { Long categoryId = Long.valueOf(params.get("goodsCategoryId") + ""); SearchPageCategoryVO searchPageCategoryVO = newBeeMallCategoryService.getCategoriesForSearch(categoryId); if (searchPageCategoryVO != null) { request.setAttribute("goodsCategoryId", categoryId); request.setAttribute("searchPageCategoryVO", searchPageCategoryVO); } } //封装参数供前端回显 if (params.containsKey("orderBy") && StringUtils.hasText(params.get("orderBy") + "")) { request.setAttribute("orderBy", params.get("orderBy") + ""); } String keyword = ""; //对keyword做过滤 去掉空格 if (params.containsKey("keyword") && StringUtils.hasText((params.get("keyword") + "").trim())) { keyword = params.get("keyword") + ""; } request.setAttribute("keyword", keyword); params.put("keyword", keyword); //搜索上架状态下的商品 params.put("goodsSellStatus", Constants.SELL_STATUS_UP); //封装商品数据 PageQueryUtil pageUtil = new PageQueryUtil(params); request.setAttribute("pageResult", newBeeMallGoodsService.searchNewBeeMallGoods(pageUtil)); return "mall/search"; } @GetMapping("/goods/detail/{goodsId}") public String detailPage(@PathVariable("goodsId") Long goodsId, HttpServletRequest request) { if (goodsId < 1) { NewBeeMallException.fail("参数异常"); } NewBeeMallGoods goods = newBeeMallGoodsService.getNewBeeMallGoodsById(goodsId); if (Constants.SELL_STATUS_UP != goods.getGoodsSellStatus()) { NewBeeMallException.fail(ServiceResultEnum.GOODS_PUT_DOWN.getResult()); } NewBeeMallGoodsDetailVO goodsDetailVO = new NewBeeMallGoodsDetailVO(); BeanUtil.copyProperties(goods, goodsDetailVO); goodsDetailVO.setGoodsCarouselList(goods.getGoodsCarousel().split(",")); request.setAttribute("goodsDetail", goodsDetailVO); return "mall/detail"; } } ================================================ FILE: src/main/java/ltd/newbee/mall/controller/mall/IndexController.java ================================================ /** * 严肃声明: * 开源版本请务必保留此注释头信息,若删除我方将保留所有法律责任追究! * 本系统已申请软件著作权,受国家版权局知识产权以及国家计算机软件著作权保护! * 可正常分享和学习源码,不得用于违法犯罪活动,违者必究! * Copyright (c) 2019-2020 十三 all rights reserved. * 版权所有,侵权必究! */ package ltd.newbee.mall.controller.mall; import ltd.newbee.mall.common.Constants; import ltd.newbee.mall.common.IndexConfigTypeEnum; import ltd.newbee.mall.common.NewBeeMallException; import ltd.newbee.mall.controller.vo.NewBeeMallIndexCarouselVO; import ltd.newbee.mall.controller.vo.NewBeeMallIndexCategoryVO; import ltd.newbee.mall.controller.vo.NewBeeMallIndexConfigGoodsVO; import ltd.newbee.mall.service.NewBeeMallCarouselService; import ltd.newbee.mall.service.NewBeeMallCategoryService; import ltd.newbee.mall.service.NewBeeMallIndexConfigService; import org.springframework.stereotype.Controller; import org.springframework.util.CollectionUtils; import org.springframework.web.bind.annotation.GetMapping; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import java.util.List; @Controller public class IndexController { @Resource private NewBeeMallCarouselService newBeeMallCarouselService; @Resource private NewBeeMallIndexConfigService newBeeMallIndexConfigService; @Resource private NewBeeMallCategoryService newBeeMallCategoryService; @GetMapping({"/index", "/", "/index.html"}) public String indexPage(HttpServletRequest request) { List categories = newBeeMallCategoryService.getCategoriesForIndex(); if (CollectionUtils.isEmpty(categories)) { NewBeeMallException.fail("分类数据不完善"); } List carousels = newBeeMallCarouselService.getCarouselsForIndex(Constants.INDEX_CAROUSEL_NUMBER); List hotGoodses = newBeeMallIndexConfigService.getConfigGoodsesForIndex(IndexConfigTypeEnum.INDEX_GOODS_HOT.getType(), Constants.INDEX_GOODS_HOT_NUMBER); List newGoodses = newBeeMallIndexConfigService.getConfigGoodsesForIndex(IndexConfigTypeEnum.INDEX_GOODS_NEW.getType(), Constants.INDEX_GOODS_NEW_NUMBER); List recommendGoodses = newBeeMallIndexConfigService.getConfigGoodsesForIndex(IndexConfigTypeEnum.INDEX_GOODS_RECOMMOND.getType(), Constants.INDEX_GOODS_RECOMMOND_NUMBER); request.setAttribute("categories", categories);//分类数据 request.setAttribute("carousels", carousels);//轮播图 request.setAttribute("hotGoodses", hotGoodses);//热销商品 request.setAttribute("newGoodses", newGoodses);//新品 request.setAttribute("recommendGoodses", recommendGoodses);//推荐商品 return "mall/index"; } } ================================================ FILE: src/main/java/ltd/newbee/mall/controller/mall/OrderController.java ================================================ /** * 严肃声明: * 开源版本请务必保留此注释头信息,若删除我方将保留所有法律责任追究! * 本系统已申请软件著作权,受国家版权局知识产权以及国家计算机软件著作权保护! * 可正常分享和学习源码,不得用于违法犯罪活动,违者必究! * Copyright (c) 2019-2020 十三 all rights reserved. * 版权所有,侵权必究! */ package ltd.newbee.mall.controller.mall; import ltd.newbee.mall.common.Constants; import ltd.newbee.mall.common.NewBeeMallException; import ltd.newbee.mall.common.NewBeeMallOrderStatusEnum; import ltd.newbee.mall.common.ServiceResultEnum; import ltd.newbee.mall.controller.vo.NewBeeMallOrderDetailVO; import ltd.newbee.mall.controller.vo.NewBeeMallShoppingCartItemVO; import ltd.newbee.mall.controller.vo.NewBeeMallUserVO; import ltd.newbee.mall.entity.NewBeeMallOrder; import ltd.newbee.mall.service.NewBeeMallOrderService; import ltd.newbee.mall.service.NewBeeMallShoppingCartService; import ltd.newbee.mall.util.PageQueryUtil; import ltd.newbee.mall.util.Result; import ltd.newbee.mall.util.ResultGenerator; import org.springframework.stereotype.Controller; import org.springframework.util.CollectionUtils; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import java.util.List; import java.util.Map; @Controller public class OrderController { @Resource private NewBeeMallShoppingCartService newBeeMallShoppingCartService; @Resource private NewBeeMallOrderService newBeeMallOrderService; @GetMapping("/orders/{orderNo}") public String orderDetailPage(HttpServletRequest request, @PathVariable("orderNo") String orderNo, HttpSession httpSession) { NewBeeMallUserVO user = (NewBeeMallUserVO) httpSession.getAttribute(Constants.MALL_USER_SESSION_KEY); NewBeeMallOrderDetailVO orderDetailVO = newBeeMallOrderService.getOrderDetailByOrderNo(orderNo, user.getUserId()); request.setAttribute("orderDetailVO", orderDetailVO); return "mall/order-detail"; } @GetMapping("/orders") public String orderListPage(@RequestParam Map params, HttpServletRequest request, HttpSession httpSession) { NewBeeMallUserVO user = (NewBeeMallUserVO) httpSession.getAttribute(Constants.MALL_USER_SESSION_KEY); params.put("userId", user.getUserId()); if (ObjectUtils.isEmpty(params.get("page"))) { params.put("page", 1); } params.put("limit", Constants.ORDER_SEARCH_PAGE_LIMIT); //封装我的订单数据 PageQueryUtil pageUtil = new PageQueryUtil(params); request.setAttribute("orderPageResult", newBeeMallOrderService.getMyOrders(pageUtil)); request.setAttribute("path", "orders"); return "mall/my-orders"; } @GetMapping("/saveOrder") public String saveOrder(HttpSession httpSession) { NewBeeMallUserVO user = (NewBeeMallUserVO) httpSession.getAttribute(Constants.MALL_USER_SESSION_KEY); List myShoppingCartItems = newBeeMallShoppingCartService.getMyShoppingCartItems(user.getUserId()); if (!StringUtils.hasText(user.getAddress().trim())) { //无收货地址 NewBeeMallException.fail(ServiceResultEnum.NULL_ADDRESS_ERROR.getResult()); } if (CollectionUtils.isEmpty(myShoppingCartItems)) { //购物车中无数据则跳转至错误页 NewBeeMallException.fail(ServiceResultEnum.SHOPPING_ITEM_ERROR.getResult()); } //保存订单并返回订单号 String saveOrderResult = newBeeMallOrderService.saveOrder(user, myShoppingCartItems); //跳转到订单详情页 return "redirect:/orders/" + saveOrderResult; } @PutMapping("/orders/{orderNo}/cancel") @ResponseBody public Result cancelOrder(@PathVariable("orderNo") String orderNo, HttpSession httpSession) { NewBeeMallUserVO user = (NewBeeMallUserVO) httpSession.getAttribute(Constants.MALL_USER_SESSION_KEY); String cancelOrderResult = newBeeMallOrderService.cancelOrder(orderNo, user.getUserId()); if (ServiceResultEnum.SUCCESS.getResult().equals(cancelOrderResult)) { return ResultGenerator.genSuccessResult(); } else { return ResultGenerator.genFailResult(cancelOrderResult); } } @PutMapping("/orders/{orderNo}/finish") @ResponseBody public Result finishOrder(@PathVariable("orderNo") String orderNo, HttpSession httpSession) { NewBeeMallUserVO user = (NewBeeMallUserVO) httpSession.getAttribute(Constants.MALL_USER_SESSION_KEY); String finishOrderResult = newBeeMallOrderService.finishOrder(orderNo, user.getUserId()); if (ServiceResultEnum.SUCCESS.getResult().equals(finishOrderResult)) { return ResultGenerator.genSuccessResult(); } else { return ResultGenerator.genFailResult(finishOrderResult); } } @GetMapping("/selectPayType") public String selectPayType(HttpServletRequest request, @RequestParam("orderNo") String orderNo, HttpSession httpSession) { NewBeeMallUserVO user = (NewBeeMallUserVO) httpSession.getAttribute(Constants.MALL_USER_SESSION_KEY); NewBeeMallOrder newBeeMallOrder = newBeeMallOrderService.getNewBeeMallOrderByOrderNo(orderNo); //判断订单userId if (!user.getUserId().equals(newBeeMallOrder.getUserId())) { NewBeeMallException.fail(ServiceResultEnum.NO_PERMISSION_ERROR.getResult()); } //判断订单状态 if (newBeeMallOrder.getOrderStatus().intValue() != NewBeeMallOrderStatusEnum.ORDER_PRE_PAY.getOrderStatus()) { NewBeeMallException.fail(ServiceResultEnum.ORDER_STATUS_ERROR.getResult()); } request.setAttribute("orderNo", orderNo); request.setAttribute("totalPrice", newBeeMallOrder.getTotalPrice()); return "mall/pay-select"; } @GetMapping("/payPage") public String payOrder(HttpServletRequest request, @RequestParam("orderNo") String orderNo, HttpSession httpSession, @RequestParam("payType") int payType) { NewBeeMallUserVO user = (NewBeeMallUserVO) httpSession.getAttribute(Constants.MALL_USER_SESSION_KEY); NewBeeMallOrder newBeeMallOrder = newBeeMallOrderService.getNewBeeMallOrderByOrderNo(orderNo); //判断订单userId if (!user.getUserId().equals(newBeeMallOrder.getUserId())) { NewBeeMallException.fail(ServiceResultEnum.NO_PERMISSION_ERROR.getResult()); } //判断订单状态 if (newBeeMallOrder.getOrderStatus().intValue() != NewBeeMallOrderStatusEnum.ORDER_PRE_PAY.getOrderStatus()) { NewBeeMallException.fail(ServiceResultEnum.ORDER_STATUS_ERROR.getResult()); } request.setAttribute("orderNo", orderNo); request.setAttribute("totalPrice", newBeeMallOrder.getTotalPrice()); if (payType == 1) { return "mall/alipay"; } else { return "mall/wxpay"; } } @GetMapping("/paySuccess") @ResponseBody public Result paySuccess(@RequestParam("orderNo") String orderNo, @RequestParam("payType") int payType) { String payResult = newBeeMallOrderService.paySuccess(orderNo, payType); if (ServiceResultEnum.SUCCESS.getResult().equals(payResult)) { return ResultGenerator.genSuccessResult(); } else { return ResultGenerator.genFailResult(payResult); } } } ================================================ FILE: src/main/java/ltd/newbee/mall/controller/mall/PersonalController.java ================================================ /** * 严肃声明: * 开源版本请务必保留此注释头信息,若删除我方将保留所有法律责任追究! * 本系统已申请软件著作权,受国家版权局知识产权以及国家计算机软件著作权保护! * 可正常分享和学习源码,不得用于违法犯罪活动,违者必究! * Copyright (c) 2019-2020 十三 all rights reserved. * 版权所有,侵权必究! */ package ltd.newbee.mall.controller.mall; import cn.hutool.captcha.ShearCaptcha; import ltd.newbee.mall.common.Constants; import ltd.newbee.mall.common.ServiceResultEnum; import ltd.newbee.mall.controller.vo.NewBeeMallUserVO; import ltd.newbee.mall.entity.MallUser; import ltd.newbee.mall.service.NewBeeMallUserService; import ltd.newbee.mall.util.MD5Util; import ltd.newbee.mall.util.Result; import ltd.newbee.mall.util.ResultGenerator; import org.springframework.stereotype.Controller; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; @Controller public class PersonalController { @Resource private NewBeeMallUserService newBeeMallUserService; @GetMapping("/personal") public String personalPage(HttpServletRequest request, HttpSession httpSession) { request.setAttribute("path", "personal"); return "mall/personal"; } @GetMapping("/logout") public String logout(HttpSession httpSession) { httpSession.removeAttribute(Constants.MALL_USER_SESSION_KEY); return "mall/login"; } @GetMapping({"/login", "login.html"}) public String loginPage() { return "mall/login"; } @GetMapping({"/register", "register.html"}) public String registerPage() { return "mall/register"; } @GetMapping("/personal/addresses") public String addressesPage() { return "mall/addresses"; } @PostMapping("/login") @ResponseBody public Result login(@RequestParam("loginName") String loginName, @RequestParam("verifyCode") String verifyCode, @RequestParam("password") String password, HttpSession httpSession) { if (!StringUtils.hasText(loginName)) { return ResultGenerator.genFailResult(ServiceResultEnum.LOGIN_NAME_NULL.getResult()); } if (!StringUtils.hasText(password)) { return ResultGenerator.genFailResult(ServiceResultEnum.LOGIN_PASSWORD_NULL.getResult()); } if (!StringUtils.hasText(verifyCode)) { return ResultGenerator.genFailResult(ServiceResultEnum.LOGIN_VERIFY_CODE_NULL.getResult()); } ShearCaptcha shearCaptcha = (ShearCaptcha) httpSession.getAttribute(Constants.MALL_VERIFY_CODE_KEY); if (shearCaptcha == null || !shearCaptcha.verify(verifyCode)) { return ResultGenerator.genFailResult(ServiceResultEnum.LOGIN_VERIFY_CODE_ERROR.getResult()); } String loginResult = newBeeMallUserService.login(loginName, MD5Util.MD5Encode(password, "UTF-8"), httpSession); //登录成功 if (ServiceResultEnum.SUCCESS.getResult().equals(loginResult)) { //删除session中的verifyCode httpSession.removeAttribute(Constants.MALL_VERIFY_CODE_KEY); return ResultGenerator.genSuccessResult(); } //登录失败 return ResultGenerator.genFailResult(loginResult); } @PostMapping("/register") @ResponseBody public Result register(@RequestParam("loginName") String loginName, @RequestParam("verifyCode") String verifyCode, @RequestParam("password") String password, HttpSession httpSession) { if (!StringUtils.hasText(loginName)) { return ResultGenerator.genFailResult(ServiceResultEnum.LOGIN_NAME_NULL.getResult()); } if (!StringUtils.hasText(password)) { return ResultGenerator.genFailResult(ServiceResultEnum.LOGIN_PASSWORD_NULL.getResult()); } if (!StringUtils.hasText(verifyCode)) { return ResultGenerator.genFailResult(ServiceResultEnum.LOGIN_VERIFY_CODE_NULL.getResult()); } ShearCaptcha shearCaptcha = (ShearCaptcha) httpSession.getAttribute(Constants.MALL_VERIFY_CODE_KEY); if (shearCaptcha == null || !shearCaptcha.verify(verifyCode)) { return ResultGenerator.genFailResult(ServiceResultEnum.LOGIN_VERIFY_CODE_ERROR.getResult()); } String registerResult = newBeeMallUserService.register(loginName, password); //注册成功 if (ServiceResultEnum.SUCCESS.getResult().equals(registerResult)) { //删除session中的verifyCode httpSession.removeAttribute(Constants.MALL_VERIFY_CODE_KEY); return ResultGenerator.genSuccessResult(); } //注册失败 return ResultGenerator.genFailResult(registerResult); } @PostMapping("/personal/updateInfo") @ResponseBody public Result updateInfo(@RequestBody MallUser mallUser, HttpSession httpSession) { NewBeeMallUserVO mallUserTemp = newBeeMallUserService.updateUserInfo(mallUser, httpSession); if (mallUserTemp == null) { Result result = ResultGenerator.genFailResult("修改失败"); return result; } else { //返回成功 Result result = ResultGenerator.genSuccessResult(); return result; } } } ================================================ FILE: src/main/java/ltd/newbee/mall/controller/mall/ShoppingCartController.java ================================================ /** * 严肃声明: * 开源版本请务必保留此注释头信息,若删除我方将保留所有法律责任追究! * 本系统已申请软件著作权,受国家版权局知识产权以及国家计算机软件著作权保护! * 可正常分享和学习源码,不得用于违法犯罪活动,违者必究! * Copyright (c) 2019-2020 十三 all rights reserved. * 版权所有,侵权必究! */ package ltd.newbee.mall.controller.mall; import ltd.newbee.mall.common.Constants; import ltd.newbee.mall.common.NewBeeMallException; import ltd.newbee.mall.common.ServiceResultEnum; import ltd.newbee.mall.controller.vo.NewBeeMallShoppingCartItemVO; import ltd.newbee.mall.controller.vo.NewBeeMallUserVO; import ltd.newbee.mall.entity.NewBeeMallShoppingCartItem; import ltd.newbee.mall.service.NewBeeMallShoppingCartService; import ltd.newbee.mall.util.Result; import ltd.newbee.mall.util.ResultGenerator; import org.springframework.stereotype.Controller; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import java.util.List; @Controller public class ShoppingCartController { @Resource private NewBeeMallShoppingCartService newBeeMallShoppingCartService; @GetMapping("/shop-cart") public String cartListPage(HttpServletRequest request, HttpSession httpSession) { NewBeeMallUserVO user = (NewBeeMallUserVO) httpSession.getAttribute(Constants.MALL_USER_SESSION_KEY); int itemsTotal = 0; int priceTotal = 0; List myShoppingCartItems = newBeeMallShoppingCartService.getMyShoppingCartItems(user.getUserId()); if (!CollectionUtils.isEmpty(myShoppingCartItems)) { //购物项总数 itemsTotal = myShoppingCartItems.stream().mapToInt(NewBeeMallShoppingCartItemVO::getGoodsCount).sum(); if (itemsTotal < 1) { NewBeeMallException.fail("购物项不能为空"); } //总价 for (NewBeeMallShoppingCartItemVO newBeeMallShoppingCartItemVO : myShoppingCartItems) { priceTotal += newBeeMallShoppingCartItemVO.getGoodsCount() * newBeeMallShoppingCartItemVO.getSellingPrice(); } if (priceTotal < 1) { NewBeeMallException.fail("购物项价格异常"); } } request.setAttribute("itemsTotal", itemsTotal); request.setAttribute("priceTotal", priceTotal); request.setAttribute("myShoppingCartItems", myShoppingCartItems); return "mall/cart"; } @PostMapping("/shop-cart") @ResponseBody public Result saveNewBeeMallShoppingCartItem(@RequestBody NewBeeMallShoppingCartItem newBeeMallShoppingCartItem, HttpSession httpSession) { NewBeeMallUserVO user = (NewBeeMallUserVO) httpSession.getAttribute(Constants.MALL_USER_SESSION_KEY); newBeeMallShoppingCartItem.setUserId(user.getUserId()); String saveResult = newBeeMallShoppingCartService.saveNewBeeMallCartItem(newBeeMallShoppingCartItem); //添加成功 if (ServiceResultEnum.SUCCESS.getResult().equals(saveResult)) { return ResultGenerator.genSuccessResult(); } //添加失败 return ResultGenerator.genFailResult(saveResult); } @PutMapping("/shop-cart") @ResponseBody public Result updateNewBeeMallShoppingCartItem(@RequestBody NewBeeMallShoppingCartItem newBeeMallShoppingCartItem, HttpSession httpSession) { NewBeeMallUserVO user = (NewBeeMallUserVO) httpSession.getAttribute(Constants.MALL_USER_SESSION_KEY); newBeeMallShoppingCartItem.setUserId(user.getUserId()); String updateResult = newBeeMallShoppingCartService.updateNewBeeMallCartItem(newBeeMallShoppingCartItem); //修改成功 if (ServiceResultEnum.SUCCESS.getResult().equals(updateResult)) { return ResultGenerator.genSuccessResult(); } //修改失败 return ResultGenerator.genFailResult(updateResult); } @DeleteMapping("/shop-cart/{newBeeMallShoppingCartItemId}") @ResponseBody public Result updateNewBeeMallShoppingCartItem(@PathVariable("newBeeMallShoppingCartItemId") Long newBeeMallShoppingCartItemId, HttpSession httpSession) { NewBeeMallUserVO user = (NewBeeMallUserVO) httpSession.getAttribute(Constants.MALL_USER_SESSION_KEY); Boolean deleteResult = newBeeMallShoppingCartService.deleteById(newBeeMallShoppingCartItemId,user.getUserId()); //删除成功 if (deleteResult) { return ResultGenerator.genSuccessResult(); } //删除失败 return ResultGenerator.genFailResult(ServiceResultEnum.OPERATE_ERROR.getResult()); } @GetMapping("/shop-cart/settle") public String settlePage(HttpServletRequest request, HttpSession httpSession) { int priceTotal = 0; NewBeeMallUserVO user = (NewBeeMallUserVO) httpSession.getAttribute(Constants.MALL_USER_SESSION_KEY); List myShoppingCartItems = newBeeMallShoppingCartService.getMyShoppingCartItems(user.getUserId()); if (CollectionUtils.isEmpty(myShoppingCartItems)) { //无数据则不跳转至结算页 return "/shop-cart"; } else { //总价 for (NewBeeMallShoppingCartItemVO newBeeMallShoppingCartItemVO : myShoppingCartItems) { priceTotal += newBeeMallShoppingCartItemVO.getGoodsCount() * newBeeMallShoppingCartItemVO.getSellingPrice(); } if (priceTotal < 1) { NewBeeMallException.fail("购物项价格异常"); } } request.setAttribute("priceTotal", priceTotal); request.setAttribute("myShoppingCartItems", myShoppingCartItems); return "mall/order-settle"; } } ================================================ FILE: src/main/java/ltd/newbee/mall/controller/vo/NewBeeMallGoodsDetailVO.java ================================================ /** * 严肃声明: * 开源版本请务必保留此注释头信息,若删除我方将保留所有法律责任追究! * 本系统已申请软件著作权,受国家版权局知识产权以及国家计算机软件著作权保护! * 可正常分享和学习源码,不得用于违法犯罪活动,违者必究! * Copyright (c) 2019-2020 十三 all rights reserved. * 版权所有,侵权必究! */ package ltd.newbee.mall.controller.vo; import java.io.Serializable; import java.util.List; /** * 商品详情页VO */ public class NewBeeMallGoodsDetailVO implements Serializable { private Long goodsId; private String goodsName; private String goodsIntro; private String goodsCoverImg; private String[] goodsCarouselList; private Integer sellingPrice; private Integer originalPrice; private String goodsDetailContent; public Long getGoodsId() { return goodsId; } public void setGoodsId(Long goodsId) { this.goodsId = goodsId; } public String getGoodsName() { return goodsName; } public void setGoodsName(String goodsName) { this.goodsName = goodsName; } public String getGoodsIntro() { return goodsIntro; } public void setGoodsIntro(String goodsIntro) { this.goodsIntro = goodsIntro; } public String getGoodsCoverImg() { return goodsCoverImg; } public void setGoodsCoverImg(String goodsCoverImg) { this.goodsCoverImg = goodsCoverImg; } public Integer getSellingPrice() { return sellingPrice; } public void setSellingPrice(Integer sellingPrice) { this.sellingPrice = sellingPrice; } public Integer getOriginalPrice() { return originalPrice; } public void setOriginalPrice(Integer originalPrice) { this.originalPrice = originalPrice; } public String getGoodsDetailContent() { return goodsDetailContent; } public void setGoodsDetailContent(String goodsDetailContent) { this.goodsDetailContent = goodsDetailContent; } public String[] getGoodsCarouselList() { return goodsCarouselList; } public void setGoodsCarouselList(String[] goodsCarouselList) { this.goodsCarouselList = goodsCarouselList; } } ================================================ FILE: src/main/java/ltd/newbee/mall/controller/vo/NewBeeMallIndexCarouselVO.java ================================================ /** * 严肃声明: * 开源版本请务必保留此注释头信息,若删除我方将保留所有法律责任追究! * 本系统已申请软件著作权,受国家版权局知识产权以及国家计算机软件著作权保护! * 可正常分享和学习源码,不得用于违法犯罪活动,违者必究! * Copyright (c) 2019-2020 十三 all rights reserved. * 版权所有,侵权必究! */ package ltd.newbee.mall.controller.vo; import java.io.Serializable; /** * 首页轮播图VO */ public class NewBeeMallIndexCarouselVO implements Serializable { private String carouselUrl; private String redirectUrl; public String getCarouselUrl() { return carouselUrl; } public void setCarouselUrl(String carouselUrl) { this.carouselUrl = carouselUrl; } public String getRedirectUrl() { return redirectUrl; } public void setRedirectUrl(String redirectUrl) { this.redirectUrl = redirectUrl; } } ================================================ FILE: src/main/java/ltd/newbee/mall/controller/vo/NewBeeMallIndexCategoryVO.java ================================================ /** * 严肃声明: * 开源版本请务必保留此注释头信息,若删除我方将保留所有法律责任追究! * 本系统已申请软件著作权,受国家版权局知识产权以及国家计算机软件著作权保护! * 可正常分享和学习源码,不得用于违法犯罪活动,违者必究! * Copyright (c) 2019-2020 十三 all rights reserved. * 版权所有,侵权必究! */ package ltd.newbee.mall.controller.vo; import java.io.Serializable; import java.util.List; /** * 首页分类数据VO */ public class NewBeeMallIndexCategoryVO implements Serializable { private Long categoryId; private Byte categoryLevel; private String categoryName; private List secondLevelCategoryVOS; public Long getCategoryId() { return categoryId; } public void setCategoryId(Long categoryId) { this.categoryId = categoryId; } public Byte getCategoryLevel() { return categoryLevel; } public void setCategoryLevel(Byte categoryLevel) { this.categoryLevel = categoryLevel; } public String getCategoryName() { return categoryName; } public void setCategoryName(String categoryName) { this.categoryName = categoryName; } public List getSecondLevelCategoryVOS() { return secondLevelCategoryVOS; } public void setSecondLevelCategoryVOS(List secondLevelCategoryVOS) { this.secondLevelCategoryVOS = secondLevelCategoryVOS; } } ================================================ FILE: src/main/java/ltd/newbee/mall/controller/vo/NewBeeMallIndexConfigGoodsVO.java ================================================ /** * 严肃声明: * 开源版本请务必保留此注释头信息,若删除我方将保留所有法律责任追究! * 本系统已申请软件著作权,受国家版权局知识产权以及国家计算机软件著作权保护! * 可正常分享和学习源码,不得用于违法犯罪活动,违者必究! * Copyright (c) 2019-2020 十三 all rights reserved. * 版权所有,侵权必究! */ package ltd.newbee.mall.controller.vo; import java.io.Serializable; /** * 首页配置商品VO */ public class NewBeeMallIndexConfigGoodsVO implements Serializable { private Long goodsId; private String goodsName; private String goodsIntro; private String goodsCoverImg; private Integer sellingPrice; private String tag; public Long getGoodsId() { return goodsId; } public void setGoodsId(Long goodsId) { this.goodsId = goodsId; } public String getGoodsName() { return goodsName; } public void setGoodsName(String goodsName) { this.goodsName = goodsName; } public String getGoodsIntro() { return goodsIntro; } public void setGoodsIntro(String goodsIntro) { this.goodsIntro = goodsIntro; } public String getGoodsCoverImg() { return goodsCoverImg; } public void setGoodsCoverImg(String goodsCoverImg) { this.goodsCoverImg = goodsCoverImg; } public Integer getSellingPrice() { return sellingPrice; } public void setSellingPrice(Integer sellingPrice) { this.sellingPrice = sellingPrice; } public String getTag() { return tag; } public void setTag(String tag) { this.tag = tag; } } ================================================ FILE: src/main/java/ltd/newbee/mall/controller/vo/NewBeeMallOrderDetailVO.java ================================================ /** * 严肃声明: * 开源版本请务必保留此注释头信息,若删除我方将保留所有法律责任追究! * 本系统已申请软件著作权,受国家版权局知识产权以及国家计算机软件著作权保护! * 可正常分享和学习源码,不得用于违法犯罪活动,违者必究! * Copyright (c) 2019-2020 十三 all rights reserved. * 版权所有,侵权必究! */ package ltd.newbee.mall.controller.vo; import java.io.Serializable; import java.util.Date; import java.util.List; /** * 订单详情页页面VO */ public class NewBeeMallOrderDetailVO implements Serializable { private String orderNo; private Integer totalPrice; private Byte payStatus; private String payStatusString; private Byte payType; private String payTypeString; private Date payTime; private Byte orderStatus; private String orderStatusString; private String userAddress; private Date createTime; private List newBeeMallOrderItemVOS; public String getOrderNo() { return orderNo; } public void setOrderNo(String orderNo) { this.orderNo = orderNo; } public Integer getTotalPrice() { return totalPrice; } public void setTotalPrice(Integer totalPrice) { this.totalPrice = totalPrice; } public Byte getPayStatus() { return payStatus; } public void setPayStatus(Byte payStatus) { this.payStatus = payStatus; } public Byte getPayType() { return payType; } public void setPayType(Byte payType) { this.payType = payType; } public Date getPayTime() { return payTime; } public void setPayTime(Date payTime) { this.payTime = payTime; } public Byte getOrderStatus() { return orderStatus; } public void setOrderStatus(Byte orderStatus) { this.orderStatus = orderStatus; } public String getUserAddress() { return userAddress; } public void setUserAddress(String userAddress) { this.userAddress = userAddress; } public Date getCreateTime() { return createTime; } public void setCreateTime(Date createTime) { this.createTime = createTime; } public String getPayStatusString() { return payStatusString; } public void setPayStatusString(String payStatusString) { this.payStatusString = payStatusString; } public String getPayTypeString() { return payTypeString; } public void setPayTypeString(String payTypeString) { this.payTypeString = payTypeString; } public String getOrderStatusString() { return orderStatusString; } public void setOrderStatusString(String orderStatusString) { this.orderStatusString = orderStatusString; } public List getNewBeeMallOrderItemVOS() { return newBeeMallOrderItemVOS; } public void setNewBeeMallOrderItemVOS(List newBeeMallOrderItemVOS) { this.newBeeMallOrderItemVOS = newBeeMallOrderItemVOS; } } ================================================ FILE: src/main/java/ltd/newbee/mall/controller/vo/NewBeeMallOrderItemVO.java ================================================ /** * 严肃声明: * 开源版本请务必保留此注释头信息,若删除我方将保留所有法律责任追究! * 本系统已申请软件著作权,受国家版权局知识产权以及国家计算机软件著作权保护! * 可正常分享和学习源码,不得用于违法犯罪活动,违者必究! * Copyright (c) 2019-2020 十三 all rights reserved. * 版权所有,侵权必究! */ package ltd.newbee.mall.controller.vo; import java.io.Serializable; /** * 订单详情页页面订单项VO */ public class NewBeeMallOrderItemVO implements Serializable { private Long goodsId; private Integer goodsCount; private String goodsName; private String goodsCoverImg; private Integer sellingPrice; public Long getGoodsId() { return goodsId; } public void setGoodsId(Long goodsId) { this.goodsId = goodsId; } public String getGoodsName() { return goodsName; } public void setGoodsName(String goodsName) { this.goodsName = goodsName; } public String getGoodsCoverImg() { return goodsCoverImg; } public void setGoodsCoverImg(String goodsCoverImg) { this.goodsCoverImg = goodsCoverImg; } public Integer getSellingPrice() { return sellingPrice; } public void setSellingPrice(Integer sellingPrice) { this.sellingPrice = sellingPrice; } public Integer getGoodsCount() { return goodsCount; } public void setGoodsCount(Integer goodsCount) { this.goodsCount = goodsCount; } } ================================================ FILE: src/main/java/ltd/newbee/mall/controller/vo/NewBeeMallOrderListVO.java ================================================ /** * 严肃声明: * 开源版本请务必保留此注释头信息,若删除我方将保留所有法律责任追究! * 本系统已申请软件著作权,受国家版权局知识产权以及国家计算机软件著作权保护! * 可正常分享和学习源码,不得用于违法犯罪活动,违者必究! * Copyright (c) 2019-2020 十三 all rights reserved. * 版权所有,侵权必究! */ package ltd.newbee.mall.controller.vo; import java.io.Serializable; import java.util.Date; import java.util.List; /** * 订单列表页面VO */ public class NewBeeMallOrderListVO implements Serializable { private Long orderId; private String orderNo; private Integer totalPrice; private Byte payType; private Byte orderStatus; private String orderStatusString; private String userAddress; private Date createTime; private List newBeeMallOrderItemVOS; public Long getOrderId() { return orderId; } public void setOrderId(Long orderId) { this.orderId = orderId; } public String getOrderNo() { return orderNo; } public void setOrderNo(String orderNo) { this.orderNo = orderNo; } public Integer getTotalPrice() { return totalPrice; } public void setTotalPrice(Integer totalPrice) { this.totalPrice = totalPrice; } public Byte getPayType() { return payType; } public void setPayType(Byte payType) { this.payType = payType; } public Byte getOrderStatus() { return orderStatus; } public void setOrderStatus(Byte orderStatus) { this.orderStatus = orderStatus; } public String getOrderStatusString() { return orderStatusString; } public void setOrderStatusString(String orderStatusString) { this.orderStatusString = orderStatusString; } public String getUserAddress() { return userAddress; } public void setUserAddress(String userAddress) { this.userAddress = userAddress; } public Date getCreateTime() { return createTime; } public void setCreateTime(Date createTime) { this.createTime = createTime; } public List getNewBeeMallOrderItemVOS() { return newBeeMallOrderItemVOS; } public void setNewBeeMallOrderItemVOS(List newBeeMallOrderItemVOS) { this.newBeeMallOrderItemVOS = newBeeMallOrderItemVOS; } } ================================================ FILE: src/main/java/ltd/newbee/mall/controller/vo/NewBeeMallSearchGoodsVO.java ================================================ /** * 严肃声明: * 开源版本请务必保留此注释头信息,若删除我方将保留所有法律责任追究! * 本系统已申请软件著作权,受国家版权局知识产权以及国家计算机软件著作权保护! * 可正常分享和学习源码,不得用于违法犯罪活动,违者必究! * Copyright (c) 2019-2020 十三 all rights reserved. * 版权所有,侵权必究! */ package ltd.newbee.mall.controller.vo; import java.io.Serializable; /** * 搜索列表页商品VO */ public class NewBeeMallSearchGoodsVO implements Serializable { private Long goodsId; private String goodsName; private String goodsIntro; private String goodsCoverImg; private Integer sellingPrice; public Long getGoodsId() { return goodsId; } public void setGoodsId(Long goodsId) { this.goodsId = goodsId; } public String getGoodsName() { return goodsName; } public void setGoodsName(String goodsName) { this.goodsName = goodsName; } public String getGoodsIntro() { return goodsIntro; } public void setGoodsIntro(String goodsIntro) { this.goodsIntro = goodsIntro; } public String getGoodsCoverImg() { return goodsCoverImg; } public void setGoodsCoverImg(String goodsCoverImg) { this.goodsCoverImg = goodsCoverImg; } public Integer getSellingPrice() { return sellingPrice; } public void setSellingPrice(Integer sellingPrice) { this.sellingPrice = sellingPrice; } } ================================================ FILE: src/main/java/ltd/newbee/mall/controller/vo/NewBeeMallShoppingCartItemVO.java ================================================ /** * 严肃声明: * 开源版本请务必保留此注释头信息,若删除我方将保留所有法律责任追究! * 本系统已申请软件著作权,受国家版权局知识产权以及国家计算机软件著作权保护! * 可正常分享和学习源码,不得用于违法犯罪活动,违者必究! * Copyright (c) 2019-2020 十三 all rights reserved. * 版权所有,侵权必究! */ package ltd.newbee.mall.controller.vo; import java.io.Serializable; /** * 购物车页面购物项VO */ public class NewBeeMallShoppingCartItemVO implements Serializable { private Long cartItemId; private Long goodsId; private Integer goodsCount; private String goodsName; private String goodsCoverImg; private Integer sellingPrice; public Long getGoodsId() { return goodsId; } public void setGoodsId(Long goodsId) { this.goodsId = goodsId; } public String getGoodsName() { return goodsName; } public void setGoodsName(String goodsName) { this.goodsName = goodsName; } public String getGoodsCoverImg() { return goodsCoverImg; } public void setGoodsCoverImg(String goodsCoverImg) { this.goodsCoverImg = goodsCoverImg; } public Integer getSellingPrice() { return sellingPrice; } public void setSellingPrice(Integer sellingPrice) { this.sellingPrice = sellingPrice; } public Long getCartItemId() { return cartItemId; } public void setCartItemId(Long cartItemId) { this.cartItemId = cartItemId; } public Integer getGoodsCount() { return goodsCount; } public void setGoodsCount(Integer goodsCount) { this.goodsCount = goodsCount; } } ================================================ FILE: src/main/java/ltd/newbee/mall/controller/vo/NewBeeMallUserVO.java ================================================ /** * 严肃声明: * 开源版本请务必保留此注释头信息,若删除我方将保留所有法律责任追究! * 本系统已申请软件著作权,受国家版权局知识产权以及国家计算机软件著作权保护! * 可正常分享和学习源码,不得用于违法犯罪活动,违者必究! * Copyright (c) 2019-2020 十三 all rights reserved. * 版权所有,侵权必究! */ package ltd.newbee.mall.controller.vo; import java.io.Serializable; public class NewBeeMallUserVO implements Serializable { private Long userId; private String nickName; private String loginName; private String introduceSign; private String address; private int shopCartItemCount; public Long getUserId() { return userId; } public void setUserId(Long userId) { this.userId = userId; } public String getNickName() { return nickName; } public void setNickName(String nickName) { this.nickName = nickName; } public String getLoginName() { return loginName; } public void setLoginName(String loginName) { this.loginName = loginName; } public String getIntroduceSign() { return introduceSign; } public void setIntroduceSign(String introduceSign) { this.introduceSign = introduceSign; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public int getShopCartItemCount() { return shopCartItemCount; } public void setShopCartItemCount(int shopCartItemCount) { this.shopCartItemCount = shopCartItemCount; } } ================================================ FILE: src/main/java/ltd/newbee/mall/controller/vo/SearchPageCategoryVO.java ================================================ /** * 严肃声明: * 开源版本请务必保留此注释头信息,若删除我方将保留所有法律责任追究! * 本系统已申请软件著作权,受国家版权局知识产权以及国家计算机软件著作权保护! * 可正常分享和学习源码,不得用于违法犯罪活动,违者必究! * Copyright (c) 2019-2020 十三 all rights reserved. * 版权所有,侵权必究! */ package ltd.newbee.mall.controller.vo; import ltd.newbee.mall.entity.GoodsCategory; import java.io.Serializable; import java.util.List; /** * 搜索页面分类数据VO */ public class SearchPageCategoryVO implements Serializable { private String firstLevelCategoryName; private List secondLevelCategoryList; private String secondLevelCategoryName; private List thirdLevelCategoryList; private String currentCategoryName; public String getFirstLevelCategoryName() { return firstLevelCategoryName; } public void setFirstLevelCategoryName(String firstLevelCategoryName) { this.firstLevelCategoryName = firstLevelCategoryName; } public List getSecondLevelCategoryList() { return secondLevelCategoryList; } public void setSecondLevelCategoryList(List secondLevelCategoryList) { this.secondLevelCategoryList = secondLevelCategoryList; } public String getSecondLevelCategoryName() { return secondLevelCategoryName; } public void setSecondLevelCategoryName(String secondLevelCategoryName) { this.secondLevelCategoryName = secondLevelCategoryName; } public List getThirdLevelCategoryList() { return thirdLevelCategoryList; } public void setThirdLevelCategoryList(List thirdLevelCategoryList) { this.thirdLevelCategoryList = thirdLevelCategoryList; } public String getCurrentCategoryName() { return currentCategoryName; } public void setCurrentCategoryName(String currentCategoryName) { this.currentCategoryName = currentCategoryName; } } ================================================ FILE: src/main/java/ltd/newbee/mall/controller/vo/SecondLevelCategoryVO.java ================================================ /** * 严肃声明: * 开源版本请务必保留此注释头信息,若删除我方将保留所有法律责任追究! * 本系统已申请软件著作权,受国家版权局知识产权以及国家计算机软件著作权保护! * 可正常分享和学习源码,不得用于违法犯罪活动,违者必究! * Copyright (c) 2019-2020 十三 all rights reserved. * 版权所有,侵权必究! */ package ltd.newbee.mall.controller.vo; import java.io.Serializable; import java.util.List; /** * 首页分类数据VO(第二级) */ public class SecondLevelCategoryVO implements Serializable { private Long categoryId; private Long parentId; private Byte categoryLevel; private String categoryName; private List thirdLevelCategoryVOS; public Long getCategoryId() { return categoryId; } public void setCategoryId(Long categoryId) { this.categoryId = categoryId; } public Byte getCategoryLevel() { return categoryLevel; } public void setCategoryLevel(Byte categoryLevel) { this.categoryLevel = categoryLevel; } public String getCategoryName() { return categoryName; } public void setCategoryName(String categoryName) { this.categoryName = categoryName; } public List getThirdLevelCategoryVOS() { return thirdLevelCategoryVOS; } public void setThirdLevelCategoryVOS(List thirdLevelCategoryVOS) { this.thirdLevelCategoryVOS = thirdLevelCategoryVOS; } public Long getParentId() { return parentId; } public void setParentId(Long parentId) { this.parentId = parentId; } } ================================================ FILE: src/main/java/ltd/newbee/mall/controller/vo/ThirdLevelCategoryVO.java ================================================ /** * 严肃声明: * 开源版本请务必保留此注释头信息,若删除我方将保留所有法律责任追究! * 本系统已申请软件著作权,受国家版权局知识产权以及国家计算机软件著作权保护! * 可正常分享和学习源码,不得用于违法犯罪活动,违者必究! * Copyright (c) 2019-2020 十三 all rights reserved. * 版权所有,侵权必究! */ package ltd.newbee.mall.controller.vo; import java.io.Serializable; /** * 首页分类数据VO(第三级) */ public class ThirdLevelCategoryVO implements Serializable { private Long categoryId; private Byte categoryLevel; private String categoryName; public Long getCategoryId() { return categoryId; } public void setCategoryId(Long categoryId) { this.categoryId = categoryId; } public Byte getCategoryLevel() { return categoryLevel; } public void setCategoryLevel(Byte categoryLevel) { this.categoryLevel = categoryLevel; } public String getCategoryName() { return categoryName; } public void setCategoryName(String categoryName) { this.categoryName = categoryName; } } ================================================ FILE: src/main/java/ltd/newbee/mall/dao/AdminUserMapper.java ================================================ /** * 严肃声明: * 开源版本请务必保留此注释头信息,若删除我方将保留所有法律责任追究! * 本系统已申请软件著作权,受国家版权局知识产权以及国家计算机软件著作权保护! * 可正常分享和学习源码,不得用于违法犯罪活动,违者必究! * Copyright (c) 2019-2020 十三 all rights reserved. * 版权所有,侵权必究! */ package ltd.newbee.mall.dao; import ltd.newbee.mall.entity.AdminUser; import org.apache.ibatis.annotations.Param; public interface AdminUserMapper { int insert(AdminUser record); int insertSelective(AdminUser record); /** * 登陆方法 * * @param userName * @param password * @return */ AdminUser login(@Param("userName") String userName, @Param("password") String password); AdminUser selectByPrimaryKey(Integer adminUserId); int updateByPrimaryKeySelective(AdminUser record); int updateByPrimaryKey(AdminUser record); } ================================================ FILE: src/main/java/ltd/newbee/mall/dao/CarouselMapper.java ================================================ /** * 严肃声明: * 开源版本请务必保留此注释头信息,若删除我方将保留所有法律责任追究! * 本系统已申请软件著作权,受国家版权局知识产权以及国家计算机软件著作权保护! * 可正常分享和学习源码,不得用于违法犯罪活动,违者必究! * Copyright (c) 2019-2020 十三 all rights reserved. * 版权所有,侵权必究! */ package ltd.newbee.mall.dao; import ltd.newbee.mall.entity.Carousel; import ltd.newbee.mall.util.PageQueryUtil; import org.apache.ibatis.annotations.Param; import java.util.List; public interface CarouselMapper { int deleteByPrimaryKey(Integer carouselId); int insert(Carousel record); int insertSelective(Carousel record); Carousel selectByPrimaryKey(Integer carouselId); int updateByPrimaryKeySelective(Carousel record); int updateByPrimaryKey(Carousel record); List findCarouselList(PageQueryUtil pageUtil); int getTotalCarousels(PageQueryUtil pageUtil); int deleteBatch(Integer[] ids); List findCarouselsByNum(@Param("number") int number); } ================================================ FILE: src/main/java/ltd/newbee/mall/dao/GoodsCategoryMapper.java ================================================ /** * 严肃声明: * 开源版本请务必保留此注释头信息,若删除我方将保留所有法律责任追究! * 本系统已申请软件著作权,受国家版权局知识产权以及国家计算机软件著作权保护! * 可正常分享和学习源码,不得用于违法犯罪活动,违者必究! * Copyright (c) 2019-2020 十三 all rights reserved. * 版权所有,侵权必究! */ package ltd.newbee.mall.dao; import ltd.newbee.mall.entity.GoodsCategory; import ltd.newbee.mall.util.PageQueryUtil; import org.apache.ibatis.annotations.Param; import java.util.List; public interface GoodsCategoryMapper { int deleteByPrimaryKey(Long categoryId); int insert(GoodsCategory record); int insertSelective(GoodsCategory record); GoodsCategory selectByPrimaryKey(Long categoryId); GoodsCategory selectByLevelAndName(@Param("categoryLevel") Byte categoryLevel, @Param("categoryName") String categoryName); int updateByPrimaryKeySelective(GoodsCategory record); int updateByPrimaryKey(GoodsCategory record); List findGoodsCategoryList(PageQueryUtil pageUtil); int getTotalGoodsCategories(PageQueryUtil pageUtil); int deleteBatch(Integer[] ids); List selectByLevelAndParentIdsAndNumber(@Param("parentIds") List parentIds, @Param("categoryLevel") int categoryLevel, @Param("number") int number); } ================================================ FILE: src/main/java/ltd/newbee/mall/dao/IndexConfigMapper.java ================================================ /** * 严肃声明: * 开源版本请务必保留此注释头信息,若删除我方将保留所有法律责任追究! * 本系统已申请软件著作权,受国家版权局知识产权以及国家计算机软件著作权保护! * 可正常分享和学习源码,不得用于违法犯罪活动,违者必究! * Copyright (c) 2019-2020 十三 all rights reserved. * 版权所有,侵权必究! */ package ltd.newbee.mall.dao; import ltd.newbee.mall.entity.IndexConfig; import ltd.newbee.mall.util.PageQueryUtil; import org.apache.ibatis.annotations.Param; import java.util.List; public interface IndexConfigMapper { int deleteByPrimaryKey(Long configId); int insert(IndexConfig record); int insertSelective(IndexConfig record); IndexConfig selectByPrimaryKey(Long configId); IndexConfig selectByTypeAndGoodsId(@Param("configType") int configType, @Param("goodsId") Long goodsId); int updateByPrimaryKeySelective(IndexConfig record); int updateByPrimaryKey(IndexConfig record); List findIndexConfigList(PageQueryUtil pageUtil); int getTotalIndexConfigs(PageQueryUtil pageUtil); int deleteBatch(Long[] ids); List findIndexConfigsByTypeAndNum(@Param("configType") int configType, @Param("number") int number); } ================================================ FILE: src/main/java/ltd/newbee/mall/dao/MallUserMapper.java ================================================ /** * 严肃声明: * 开源版本请务必保留此注释头信息,若删除我方将保留所有法律责任追究! * 本系统已申请软件著作权,受国家版权局知识产权以及国家计算机软件著作权保护! * 可正常分享和学习源码,不得用于违法犯罪活动,违者必究! * Copyright (c) 2019-2020 十三 all rights reserved. * 版权所有,侵权必究! */ package ltd.newbee.mall.dao; import ltd.newbee.mall.entity.MallUser; import ltd.newbee.mall.util.PageQueryUtil; import org.apache.ibatis.annotations.Param; import java.util.List; public interface MallUserMapper { int deleteByPrimaryKey(Long userId); int insert(MallUser record); int insertSelective(MallUser record); MallUser selectByPrimaryKey(Long userId); MallUser selectByLoginName(String loginName); MallUser selectByLoginNameAndPasswd(@Param("loginName") String loginName, @Param("password") String password); int updateByPrimaryKeySelective(MallUser record); int updateByPrimaryKey(MallUser record); List findMallUserList(PageQueryUtil pageUtil); int getTotalMallUsers(PageQueryUtil pageUtil); int lockUserBatch(@Param("ids") Integer[] ids, @Param("lockStatus") int lockStatus); } ================================================ FILE: src/main/java/ltd/newbee/mall/dao/NewBeeMallGoodsMapper.java ================================================ /** * 严肃声明: * 开源版本请务必保留此注释头信息,若删除我方将保留所有法律责任追究! * 本系统已申请软件著作权,受国家版权局知识产权以及国家计算机软件著作权保护! * 可正常分享和学习源码,不得用于违法犯罪活动,违者必究! * Copyright (c) 2019-2020 十三 all rights reserved. * 版权所有,侵权必究! */ package ltd.newbee.mall.dao; import ltd.newbee.mall.entity.NewBeeMallGoods; import ltd.newbee.mall.entity.StockNumDTO; import ltd.newbee.mall.util.PageQueryUtil; import org.apache.ibatis.annotations.Param; import java.util.List; public interface NewBeeMallGoodsMapper { int deleteByPrimaryKey(Long goodsId); int insert(NewBeeMallGoods record); int insertSelective(NewBeeMallGoods record); NewBeeMallGoods selectByPrimaryKey(Long goodsId); NewBeeMallGoods selectByCategoryIdAndName(@Param("goodsName") String goodsName, @Param("goodsCategoryId") Long goodsCategoryId); int updateByPrimaryKeySelective(NewBeeMallGoods record); int updateByPrimaryKeyWithBLOBs(NewBeeMallGoods record); int updateByPrimaryKey(NewBeeMallGoods record); List findNewBeeMallGoodsList(PageQueryUtil pageUtil); int getTotalNewBeeMallGoods(PageQueryUtil pageUtil); List selectByPrimaryKeys(List goodsIds); List findNewBeeMallGoodsListBySearch(PageQueryUtil pageUtil); int getTotalNewBeeMallGoodsBySearch(PageQueryUtil pageUtil); int batchInsert(@Param("newBeeMallGoodsList") List newBeeMallGoodsList); int updateStockNum(@Param("stockNumDTOS") List stockNumDTOS); int recoverStockNum(@Param("stockNumDTOS") List stockNumDTOS); int batchUpdateSellStatus(@Param("orderIds")Long[] orderIds,@Param("sellStatus") int sellStatus); } ================================================ FILE: src/main/java/ltd/newbee/mall/dao/NewBeeMallOrderItemMapper.java ================================================ /** * 严肃声明: * 开源版本请务必保留此注释头信息,若删除我方将保留所有法律责任追究! * 本系统已申请软件著作权,受国家版权局知识产权以及国家计算机软件著作权保护! * 可正常分享和学习源码,不得用于违法犯罪活动,违者必究! * Copyright (c) 2019-2020 十三 all rights reserved. * 版权所有,侵权必究! */ package ltd.newbee.mall.dao; import ltd.newbee.mall.entity.NewBeeMallOrderItem; import org.apache.ibatis.annotations.Param; import java.util.List; public interface NewBeeMallOrderItemMapper { int deleteByPrimaryKey(Long orderItemId); int insert(NewBeeMallOrderItem record); int insertSelective(NewBeeMallOrderItem record); NewBeeMallOrderItem selectByPrimaryKey(Long orderItemId); /** * 根据订单id获取订单项列表 * * @param orderId * @return */ List selectByOrderId(Long orderId); /** * 根据订单ids获取订单项列表 * * @param orderIds * @return */ List selectByOrderIds(@Param("orderIds") List orderIds); /** * 批量insert订单项数据 * * @param orderItems * @return */ int insertBatch(@Param("orderItems") List orderItems); int updateByPrimaryKeySelective(NewBeeMallOrderItem record); int updateByPrimaryKey(NewBeeMallOrderItem record); } ================================================ FILE: src/main/java/ltd/newbee/mall/dao/NewBeeMallOrderMapper.java ================================================ /** * 严肃声明: * 开源版本请务必保留此注释头信息,若删除我方将保留所有法律责任追究! * 本系统已申请软件著作权,受国家版权局知识产权以及国家计算机软件著作权保护! * 可正常分享和学习源码,不得用于违法犯罪活动,违者必究! * Copyright (c) 2019-2020 十三 all rights reserved. * 版权所有,侵权必究! */ package ltd.newbee.mall.dao; import ltd.newbee.mall.entity.NewBeeMallOrder; import ltd.newbee.mall.util.PageQueryUtil; import org.apache.ibatis.annotations.Param; import java.util.List; public interface NewBeeMallOrderMapper { int deleteByPrimaryKey(Long orderId); int insert(NewBeeMallOrder record); int insertSelective(NewBeeMallOrder record); NewBeeMallOrder selectByPrimaryKey(Long orderId); NewBeeMallOrder selectByOrderNo(String orderNo); int updateByPrimaryKeySelective(NewBeeMallOrder record); int updateByPrimaryKey(NewBeeMallOrder record); List findNewBeeMallOrderList(PageQueryUtil pageUtil); int getTotalNewBeeMallOrders(PageQueryUtil pageUtil); List selectByPrimaryKeys(@Param("orderIds") List orderIds); int checkOut(@Param("orderIds") List orderIds); int closeOrder(@Param("orderIds") List orderIds, @Param("orderStatus") int orderStatus); int checkDone(@Param("orderIds") List asList); } ================================================ FILE: src/main/java/ltd/newbee/mall/dao/NewBeeMallShoppingCartItemMapper.java ================================================ /** * 严肃声明: * 开源版本请务必保留此注释头信息,若删除我方将保留所有法律责任追究! * 本系统已申请软件著作权,受国家版权局知识产权以及国家计算机软件著作权保护! * 可正常分享和学习源码,不得用于违法犯罪活动,违者必究! * Copyright (c) 2019-2020 十三 all rights reserved. * 版权所有,侵权必究! */ package ltd.newbee.mall.dao; import ltd.newbee.mall.entity.NewBeeMallShoppingCartItem; import org.apache.ibatis.annotations.Param; import java.util.List; public interface NewBeeMallShoppingCartItemMapper { int deleteByPrimaryKey(Long cartItemId); int insert(NewBeeMallShoppingCartItem record); int insertSelective(NewBeeMallShoppingCartItem record); NewBeeMallShoppingCartItem selectByPrimaryKey(Long cartItemId); NewBeeMallShoppingCartItem selectByUserIdAndGoodsId(@Param("newBeeMallUserId") Long newBeeMallUserId, @Param("goodsId") Long goodsId); List selectByUserId(@Param("newBeeMallUserId") Long newBeeMallUserId, @Param("number") int number); int selectCountByUserId(Long newBeeMallUserId); int updateByPrimaryKeySelective(NewBeeMallShoppingCartItem record); int updateByPrimaryKey(NewBeeMallShoppingCartItem record); int deleteBatch(List ids); } ================================================ FILE: src/main/java/ltd/newbee/mall/entity/AdminUser.java ================================================ /** * 严肃声明: * 开源版本请务必保留此注释头信息,若删除我方将保留所有法律责任追究! * 本系统已申请软件著作权,受国家版权局知识产权以及国家计算机软件著作权保护! * 可正常分享和学习源码,不得用于违法犯罪活动,违者必究! * Copyright (c) 2019-2020 十三 all rights reserved. * 版权所有,侵权必究! */ package ltd.newbee.mall.entity; public class AdminUser { private Integer adminUserId; private String loginUserName; private String loginPassword; private String nickName; private Byte locked; public Integer getAdminUserId() { return adminUserId; } public void setAdminUserId(Integer adminUserId) { this.adminUserId = adminUserId; } public String getLoginUserName() { return loginUserName; } public void setLoginUserName(String loginUserName) { this.loginUserName = loginUserName == null ? null : loginUserName.trim(); } public String getLoginPassword() { return loginPassword; } public void setLoginPassword(String loginPassword) { this.loginPassword = loginPassword == null ? null : loginPassword.trim(); } public String getNickName() { return nickName; } public void setNickName(String nickName) { this.nickName = nickName == null ? null : nickName.trim(); } public Byte getLocked() { return locked; } public void setLocked(Byte locked) { this.locked = locked; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(getClass().getSimpleName()); sb.append(" ["); sb.append("Hash = ").append(hashCode()); sb.append(", adminUserId=").append(adminUserId); sb.append(", loginUserName=").append(loginUserName); sb.append(", loginPassword=").append(loginPassword); sb.append(", nickName=").append(nickName); sb.append(", locked=").append(locked); sb.append("]"); return sb.toString(); } } ================================================ FILE: src/main/java/ltd/newbee/mall/entity/Carousel.java ================================================ /** * 严肃声明: * 开源版本请务必保留此注释头信息,若删除我方将保留所有法律责任追究! * 本系统已申请软件著作权,受国家版权局知识产权以及国家计算机软件著作权保护! * 可正常分享和学习源码,不得用于违法犯罪活动,违者必究! * Copyright (c) 2019-2020 十三 all rights reserved. * 版权所有,侵权必究! */ package ltd.newbee.mall.entity; import com.fasterxml.jackson.annotation.JsonFormat; import java.util.Date; public class Carousel { private Integer carouselId; private String carouselUrl; private String redirectUrl; private Integer carouselRank; private Byte isDeleted; @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private Date createTime; private Integer createUser; @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private Date updateTime; private Integer updateUser; public Integer getCarouselId() { return carouselId; } public void setCarouselId(Integer carouselId) { this.carouselId = carouselId; } public String getCarouselUrl() { return carouselUrl; } public void setCarouselUrl(String carouselUrl) { this.carouselUrl = carouselUrl == null ? null : carouselUrl.trim(); } public String getRedirectUrl() { return redirectUrl; } public void setRedirectUrl(String redirectUrl) { this.redirectUrl = redirectUrl == null ? null : redirectUrl.trim(); } public Integer getCarouselRank() { return carouselRank; } public void setCarouselRank(Integer carouselRank) { this.carouselRank = carouselRank; } public Byte getIsDeleted() { return isDeleted; } public void setIsDeleted(Byte isDeleted) { this.isDeleted = isDeleted; } public Date getCreateTime() { return createTime; } public void setCreateTime(Date createTime) { this.createTime = createTime; } public Integer getCreateUser() { return createUser; } public void setCreateUser(Integer createUser) { this.createUser = createUser; } public Date getUpdateTime() { return updateTime; } public void setUpdateTime(Date updateTime) { this.updateTime = updateTime; } public Integer getUpdateUser() { return updateUser; } public void setUpdateUser(Integer updateUser) { this.updateUser = updateUser; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(getClass().getSimpleName()); sb.append(" ["); sb.append("Hash = ").append(hashCode()); sb.append(", carouselId=").append(carouselId); sb.append(", carouselUrl=").append(carouselUrl); sb.append(", redirectUrl=").append(redirectUrl); sb.append(", carouselRank=").append(carouselRank); sb.append(", isDeleted=").append(isDeleted); sb.append(", createTime=").append(createTime); sb.append(", createUser=").append(createUser); sb.append(", updateTime=").append(updateTime); sb.append(", updateUser=").append(updateUser); sb.append("]"); return sb.toString(); } } ================================================ FILE: src/main/java/ltd/newbee/mall/entity/GoodsCategory.java ================================================ /** * 严肃声明: * 开源版本请务必保留此注释头信息,若删除我方将保留所有法律责任追究! * 本系统已申请软件著作权,受国家版权局知识产权以及国家计算机软件著作权保护! * 可正常分享和学习源码,不得用于违法犯罪活动,违者必究! * Copyright (c) 2019-2020 十三 all rights reserved. * 版权所有,侵权必究! */ package ltd.newbee.mall.entity; import com.fasterxml.jackson.annotation.JsonFormat; import java.util.Date; public class GoodsCategory { private Long categoryId; private Byte categoryLevel; private Long parentId; private String categoryName; private Integer categoryRank; private Byte isDeleted; @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private Date createTime; private Integer createUser; @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private Date updateTime; private Integer updateUser; public Long getCategoryId() { return categoryId; } public void setCategoryId(Long categoryId) { this.categoryId = categoryId; } public Byte getCategoryLevel() { return categoryLevel; } public void setCategoryLevel(Byte categoryLevel) { this.categoryLevel = categoryLevel; } public Long getParentId() { return parentId; } public void setParentId(Long parentId) { this.parentId = parentId; } public String getCategoryName() { return categoryName; } public void setCategoryName(String categoryName) { this.categoryName = categoryName == null ? null : categoryName.trim(); } public Integer getCategoryRank() { return categoryRank; } public void setCategoryRank(Integer categoryRank) { this.categoryRank = categoryRank; } public Byte getIsDeleted() { return isDeleted; } public void setIsDeleted(Byte isDeleted) { this.isDeleted = isDeleted; } public Date getCreateTime() { return createTime; } public void setCreateTime(Date createTime) { this.createTime = createTime; } public Integer getCreateUser() { return createUser; } public void setCreateUser(Integer createUser) { this.createUser = createUser; } public Date getUpdateTime() { return updateTime; } public void setUpdateTime(Date updateTime) { this.updateTime = updateTime; } public Integer getUpdateUser() { return updateUser; } public void setUpdateUser(Integer updateUser) { this.updateUser = updateUser; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(getClass().getSimpleName()); sb.append(" ["); sb.append("Hash = ").append(hashCode()); sb.append(", categoryId=").append(categoryId); sb.append(", categoryLevel=").append(categoryLevel); sb.append(", parentId=").append(parentId); sb.append(", categoryName=").append(categoryName); sb.append(", categoryRank=").append(categoryRank); sb.append(", isDeleted=").append(isDeleted); sb.append(", createTime=").append(createTime); sb.append(", createUser=").append(createUser); sb.append(", updateTime=").append(updateTime); sb.append(", updateUser=").append(updateUser); sb.append("]"); return sb.toString(); } } ================================================ FILE: src/main/java/ltd/newbee/mall/entity/IndexConfig.java ================================================ /** * 严肃声明: * 开源版本请务必保留此注释头信息,若删除我方将保留所有法律责任追究! * 本系统已申请软件著作权,受国家版权局知识产权以及国家计算机软件著作权保护! * 可正常分享和学习源码,不得用于违法犯罪活动,违者必究! * Copyright (c) 2019-2020 十三 all rights reserved. * 版权所有,侵权必究! */ package ltd.newbee.mall.entity; import com.fasterxml.jackson.annotation.JsonFormat; import java.util.Date; public class IndexConfig { private Long configId; private String configName; private Byte configType; private Long goodsId; private String redirectUrl; private Integer configRank; private Byte isDeleted; @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private Date createTime; private Integer createUser; @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private Date updateTime; private Integer updateUser; public Long getConfigId() { return configId; } public void setConfigId(Long configId) { this.configId = configId; } public String getConfigName() { return configName; } public void setConfigName(String configName) { this.configName = configName == null ? null : configName.trim(); } public Byte getConfigType() { return configType; } public void setConfigType(Byte configType) { this.configType = configType; } public Long getGoodsId() { return goodsId; } public void setGoodsId(Long goodsId) { this.goodsId = goodsId; } public String getRedirectUrl() { return redirectUrl; } public void setRedirectUrl(String redirectUrl) { this.redirectUrl = redirectUrl == null ? null : redirectUrl.trim(); } public Integer getConfigRank() { return configRank; } public void setConfigRank(Integer configRank) { this.configRank = configRank; } public Byte getIsDeleted() { return isDeleted; } public void setIsDeleted(Byte isDeleted) { this.isDeleted = isDeleted; } public Date getCreateTime() { return createTime; } public void setCreateTime(Date createTime) { this.createTime = createTime; } public Integer getCreateUser() { return createUser; } public void setCreateUser(Integer createUser) { this.createUser = createUser; } public Date getUpdateTime() { return updateTime; } public void setUpdateTime(Date updateTime) { this.updateTime = updateTime; } public Integer getUpdateUser() { return updateUser; } public void setUpdateUser(Integer updateUser) { this.updateUser = updateUser; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(getClass().getSimpleName()); sb.append(" ["); sb.append("Hash = ").append(hashCode()); sb.append(", configId=").append(configId); sb.append(", configName=").append(configName); sb.append(", configType=").append(configType); sb.append(", goodsId=").append(goodsId); sb.append(", redirectUrl=").append(redirectUrl); sb.append(", configRank=").append(configRank); sb.append(", isDeleted=").append(isDeleted); sb.append(", createTime=").append(createTime); sb.append(", createUser=").append(createUser); sb.append(", updateTime=").append(updateTime); sb.append(", updateUser=").append(updateUser); sb.append("]"); return sb.toString(); } } ================================================ FILE: src/main/java/ltd/newbee/mall/entity/MallUser.java ================================================ /** * 严肃声明: * 开源版本请务必保留此注释头信息,若删除我方将保留所有法律责任追究! * 本系统已申请软件著作权,受国家版权局知识产权以及国家计算机软件著作权保护! * 可正常分享和学习源码,不得用于违法犯罪活动,违者必究! * Copyright (c) 2019-2020 十三 all rights reserved. * 版权所有,侵权必究! */ package ltd.newbee.mall.entity; import com.fasterxml.jackson.annotation.JsonFormat; import java.util.Date; public class MallUser { private Long userId; private String nickName; private String loginName; private String passwordMd5; private String introduceSign; private String address; private Byte isDeleted; private Byte lockedFlag; @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private Date createTime; public Long getUserId() { return userId; } public void setUserId(Long userId) { this.userId = userId; } public String getNickName() { return nickName; } public void setNickName(String nickName) { this.nickName = nickName == null ? null : nickName.trim(); } public String getLoginName() { return loginName; } public void setLoginName(String loginName) { this.loginName = loginName == null ? null : loginName.trim(); } public String getPasswordMd5() { return passwordMd5; } public void setPasswordMd5(String passwordMd5) { this.passwordMd5 = passwordMd5 == null ? null : passwordMd5.trim(); } public String getIntroduceSign() { return introduceSign; } public void setIntroduceSign(String introduceSign) { this.introduceSign = introduceSign == null ? null : introduceSign.trim(); } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public Byte getIsDeleted() { return isDeleted; } public void setIsDeleted(Byte isDeleted) { this.isDeleted = isDeleted; } public Byte getLockedFlag() { return lockedFlag; } public void setLockedFlag(Byte lockedFlag) { this.lockedFlag = lockedFlag; } public Date getCreateTime() { return createTime; } public void setCreateTime(Date createTime) { this.createTime = createTime; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(getClass().getSimpleName()); sb.append(" ["); sb.append("Hash = ").append(hashCode()); sb.append(", userId=").append(userId); sb.append(", nickName=").append(nickName); sb.append(", loginName=").append(loginName); sb.append(", passwordMd5=").append(passwordMd5); sb.append(", introduceSign=").append(introduceSign); sb.append(", address=").append(address); sb.append(", isDeleted=").append(isDeleted); sb.append(", lockedFlag=").append(lockedFlag); sb.append(", createTime=").append(createTime); sb.append("]"); return sb.toString(); } } ================================================ FILE: src/main/java/ltd/newbee/mall/entity/NewBeeMallGoods.java ================================================ /** * 严肃声明: * 开源版本请务必保留此注释头信息,若删除我方将保留所有法律责任追究! * 本系统已申请软件著作权,受国家版权局知识产权以及国家计算机软件著作权保护! * 可正常分享和学习源码,不得用于违法犯罪活动,违者必究! * Copyright (c) 2019-2020 十三 all rights reserved. * 版权所有,侵权必究! */ package ltd.newbee.mall.entity; import com.fasterxml.jackson.annotation.JsonFormat; import java.util.Date; public class NewBeeMallGoods { private Long goodsId; private String goodsName; private String goodsIntro; private Long goodsCategoryId; private String goodsCoverImg; private String goodsCarousel; private Integer originalPrice; private Integer sellingPrice; private Integer stockNum; private String tag; private Byte goodsSellStatus; private Integer createUser; @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private Date createTime; private Integer updateUser; @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private Date updateTime; private String goodsDetailContent; public Long getGoodsId() { return goodsId; } public void setGoodsId(Long goodsId) { this.goodsId = goodsId; } public String getGoodsName() { return goodsName; } public void setGoodsName(String goodsName) { this.goodsName = goodsName == null ? null : goodsName.trim(); } public String getGoodsIntro() { return goodsIntro; } public void setGoodsIntro(String goodsIntro) { this.goodsIntro = goodsIntro == null ? null : goodsIntro.trim(); } public Long getGoodsCategoryId() { return goodsCategoryId; } public void setGoodsCategoryId(Long goodsCategoryId) { this.goodsCategoryId = goodsCategoryId; } public String getGoodsCoverImg() { return goodsCoverImg; } public void setGoodsCoverImg(String goodsCoverImg) { this.goodsCoverImg = goodsCoverImg == null ? null : goodsCoverImg.trim(); } public String getGoodsCarousel() { return goodsCarousel; } public void setGoodsCarousel(String goodsCarousel) { this.goodsCarousel = goodsCarousel == null ? null : goodsCarousel.trim(); } public Integer getOriginalPrice() { return originalPrice; } public void setOriginalPrice(Integer originalPrice) { this.originalPrice = originalPrice; } public Integer getSellingPrice() { return sellingPrice; } public void setSellingPrice(Integer sellingPrice) { this.sellingPrice = sellingPrice; } public Integer getStockNum() { return stockNum; } public void setStockNum(Integer stockNum) { this.stockNum = stockNum; } public String getTag() { return tag; } public void setTag(String tag) { this.tag = tag == null ? null : tag.trim(); } public Byte getGoodsSellStatus() { return goodsSellStatus; } public void setGoodsSellStatus(Byte goodsSellStatus) { this.goodsSellStatus = goodsSellStatus; } public Integer getCreateUser() { return createUser; } public void setCreateUser(Integer createUser) { this.createUser = createUser; } public Date getCreateTime() { return createTime; } public void setCreateTime(Date createTime) { this.createTime = createTime; } public Integer getUpdateUser() { return updateUser; } public void setUpdateUser(Integer updateUser) { this.updateUser = updateUser; } public Date getUpdateTime() { return updateTime; } public void setUpdateTime(Date updateTime) { this.updateTime = updateTime; } public String getGoodsDetailContent() { return goodsDetailContent; } public void setGoodsDetailContent(String goodsDetailContent) { this.goodsDetailContent = goodsDetailContent == null ? null : goodsDetailContent.trim(); } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(getClass().getSimpleName()); sb.append(" ["); sb.append("Hash = ").append(hashCode()); sb.append(", goodsId=").append(goodsId); sb.append(", goodsName=").append(goodsName); sb.append(", goodsIntro=").append(goodsIntro); sb.append(", goodsCoverImg=").append(goodsCoverImg); sb.append(", goodsCarousel=").append(goodsCarousel); sb.append(", originalPrice=").append(originalPrice); sb.append(", sellingPrice=").append(sellingPrice); sb.append(", stockNum=").append(stockNum); sb.append(", tag=").append(tag); sb.append(", goodsSellStatus=").append(goodsSellStatus); sb.append(", createUser=").append(createUser); sb.append(", createTime=").append(createTime); sb.append(", updateUser=").append(updateUser); sb.append(", updateTime=").append(updateTime); sb.append(", goodsDetailContent=").append(goodsDetailContent); sb.append("]"); return sb.toString(); } } ================================================ FILE: src/main/java/ltd/newbee/mall/entity/NewBeeMallOrder.java ================================================ /** * 严肃声明: * 开源版本请务必保留此注释头信息,若删除我方将保留所有法律责任追究! * 本系统已申请软件著作权,受国家版权局知识产权以及国家计算机软件著作权保护! * 可正常分享和学习源码,不得用于违法犯罪活动,违者必究! * Copyright (c) 2019-2020 十三 all rights reserved. * 版权所有,侵权必究! */ package ltd.newbee.mall.entity; import com.fasterxml.jackson.annotation.JsonFormat; import java.util.Date; public class NewBeeMallOrder { private Long orderId; private String orderNo; private Long userId; private Integer totalPrice; private Byte payStatus; private Byte payType; private Date payTime; private Byte orderStatus; private String extraInfo; private String userAddress; private Byte isDeleted; @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private Date createTime; @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private Date updateTime; public Long getOrderId() { return orderId; } public void setOrderId(Long orderId) { this.orderId = orderId; } public String getOrderNo() { return orderNo; } public void setOrderNo(String orderNo) { this.orderNo = orderNo == null ? null : orderNo.trim(); } public Long getUserId() { return userId; } public void setUserId(Long userId) { this.userId = userId; } public Integer getTotalPrice() { return totalPrice; } public void setTotalPrice(Integer totalPrice) { this.totalPrice = totalPrice; } public Byte getPayStatus() { return payStatus; } public void setPayStatus(Byte payStatus) { this.payStatus = payStatus; } public Byte getPayType() { return payType; } public void setPayType(Byte payType) { this.payType = payType; } public Date getPayTime() { return payTime; } public void setPayTime(Date payTime) { this.payTime = payTime; } public Byte getOrderStatus() { return orderStatus; } public void setOrderStatus(Byte orderStatus) { this.orderStatus = orderStatus; } public String getExtraInfo() { return extraInfo; } public void setExtraInfo(String extraInfo) { this.extraInfo = extraInfo == null ? null : extraInfo.trim(); } public String getUserAddress() { return userAddress; } public void setUserAddress(String userAddress) { this.userAddress = userAddress == null ? null : userAddress.trim(); } public Byte getIsDeleted() { return isDeleted; } public void setIsDeleted(Byte isDeleted) { this.isDeleted = isDeleted; } public Date getCreateTime() { return createTime; } public void setCreateTime(Date createTime) { this.createTime = createTime; } public Date getUpdateTime() { return updateTime; } public void setUpdateTime(Date updateTime) { this.updateTime = updateTime; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(getClass().getSimpleName()); sb.append(" ["); sb.append("Hash = ").append(hashCode()); sb.append(", orderId=").append(orderId); sb.append(", orderNo=").append(orderNo); sb.append(", userId=").append(userId); sb.append(", totalPrice=").append(totalPrice); sb.append(", payStatus=").append(payStatus); sb.append(", payType=").append(payType); sb.append(", payTime=").append(payTime); sb.append(", orderStatus=").append(orderStatus); sb.append(", extraInfo=").append(extraInfo); sb.append(", userAddress=").append(userAddress); sb.append(", isDeleted=").append(isDeleted); sb.append(", createTime=").append(createTime); sb.append(", updateTime=").append(updateTime); sb.append("]"); return sb.toString(); } } ================================================ FILE: src/main/java/ltd/newbee/mall/entity/NewBeeMallOrderItem.java ================================================ /** * 严肃声明: * 开源版本请务必保留此注释头信息,若删除我方将保留所有法律责任追究! * 本系统已申请软件著作权,受国家版权局知识产权以及国家计算机软件著作权保护! * 可正常分享和学习源码,不得用于违法犯罪活动,违者必究! * Copyright (c) 2019-2020 十三 all rights reserved. * 版权所有,侵权必究! */ package ltd.newbee.mall.entity; import java.util.Date; public class NewBeeMallOrderItem { private Long orderItemId; private Long orderId; private Long goodsId; private String goodsName; private String goodsCoverImg; private Integer sellingPrice; private Integer goodsCount; private Date createTime; public Long getOrderItemId() { return orderItemId; } public void setOrderItemId(Long orderItemId) { this.orderItemId = orderItemId; } public Long getOrderId() { return orderId; } public void setOrderId(Long orderId) { this.orderId = orderId; } public Long getGoodsId() { return goodsId; } public void setGoodsId(Long goodsId) { this.goodsId = goodsId; } public String getGoodsName() { return goodsName; } public void setGoodsName(String goodsName) { this.goodsName = goodsName == null ? null : goodsName.trim(); } public String getGoodsCoverImg() { return goodsCoverImg; } public void setGoodsCoverImg(String goodsCoverImg) { this.goodsCoverImg = goodsCoverImg == null ? null : goodsCoverImg.trim(); } public Integer getSellingPrice() { return sellingPrice; } public void setSellingPrice(Integer sellingPrice) { this.sellingPrice = sellingPrice; } public Integer getGoodsCount() { return goodsCount; } public void setGoodsCount(Integer goodsCount) { this.goodsCount = goodsCount; } public Date getCreateTime() { return createTime; } public void setCreateTime(Date createTime) { this.createTime = createTime; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(getClass().getSimpleName()); sb.append(" ["); sb.append("Hash = ").append(hashCode()); sb.append(", orderItemId=").append(orderItemId); sb.append(", orderId=").append(orderId); sb.append(", goodsId=").append(goodsId); sb.append(", goodsName=").append(goodsName); sb.append(", goodsCoverImg=").append(goodsCoverImg); sb.append(", sellingPrice=").append(sellingPrice); sb.append(", goodsCount=").append(goodsCount); sb.append(", createTime=").append(createTime); sb.append("]"); return sb.toString(); } } ================================================ FILE: src/main/java/ltd/newbee/mall/entity/NewBeeMallShoppingCartItem.java ================================================ /** * 严肃声明: * 开源版本请务必保留此注释头信息,若删除我方将保留所有法律责任追究! * 本系统已申请软件著作权,受国家版权局知识产权以及国家计算机软件著作权保护! * 可正常分享和学习源码,不得用于违法犯罪活动,违者必究! * Copyright (c) 2019-2020 十三 all rights reserved. * 版权所有,侵权必究! */ package ltd.newbee.mall.entity; import java.util.Date; public class NewBeeMallShoppingCartItem { private Long cartItemId; private Long userId; private Long goodsId; private Integer goodsCount; private Byte isDeleted; private Date createTime; private Date updateTime; public Long getCartItemId() { return cartItemId; } public void setCartItemId(Long cartItemId) { this.cartItemId = cartItemId; } public Long getUserId() { return userId; } public void setUserId(Long userId) { this.userId = userId; } public Long getGoodsId() { return goodsId; } public void setGoodsId(Long goodsId) { this.goodsId = goodsId; } public Integer getGoodsCount() { return goodsCount; } public void setGoodsCount(Integer goodsCount) { this.goodsCount = goodsCount; } public Byte getIsDeleted() { return isDeleted; } public void setIsDeleted(Byte isDeleted) { this.isDeleted = isDeleted; } public Date getCreateTime() { return createTime; } public void setCreateTime(Date createTime) { this.createTime = createTime; } public Date getUpdateTime() { return updateTime; } public void setUpdateTime(Date updateTime) { this.updateTime = updateTime; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(getClass().getSimpleName()); sb.append(" ["); sb.append("Hash = ").append(hashCode()); sb.append(", cartItemId=").append(cartItemId); sb.append(", userId=").append(userId); sb.append(", goodsId=").append(goodsId); sb.append(", goodsCount=").append(goodsCount); sb.append(", isDeleted=").append(isDeleted); sb.append(", createTime=").append(createTime); sb.append(", updateTime=").append(updateTime); sb.append("]"); return sb.toString(); } } ================================================ FILE: src/main/java/ltd/newbee/mall/entity/StockNumDTO.java ================================================ /** * 严肃声明: * 开源版本请务必保留此注释头信息,若删除我方将保留所有法律责任追究! * 本系统已申请软件著作权,受国家版权局知识产权以及国家计算机软件著作权保护! * 可正常分享和学习源码,不得用于违法犯罪活动,违者必究! * Copyright (c) 2019-2020 十三 all rights reserved. * 版权所有,侵权必究! */ package ltd.newbee.mall.entity; /** * 库存修改所需实体 */ public class StockNumDTO { private Long goodsId; private Integer goodsCount; public Long getGoodsId() { return goodsId; } public void setGoodsId(Long goodsId) { this.goodsId = goodsId; } public Integer getGoodsCount() { return goodsCount; } public void setGoodsCount(Integer goodsCount) { this.goodsCount = goodsCount; } } ================================================ FILE: src/main/java/ltd/newbee/mall/interceptor/AdminLoginInterceptor.java ================================================ /** * 严肃声明: * 开源版本请务必保留此注释头信息,若删除我方将保留所有法律责任追究! * 本系统已申请软件著作权,受国家版权局知识产权以及国家计算机软件著作权保护! * 可正常分享和学习源码,不得用于违法犯罪活动,违者必究! * Copyright (c) 2019-2020 十三 all rights reserved. * 版权所有,侵权必究! */ package ltd.newbee.mall.interceptor; import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * 后台系统身份验证拦截器 * * @author 13 * @qq交流群 796794009 * @email 2449207463@qq.com * @link https://github.com/newbee-ltd */ @Component public class AdminLoginInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object o) throws Exception { String requestServletPath = request.getServletPath(); if (requestServletPath.startsWith("/admin") && null == request.getSession().getAttribute("loginUser")) { request.getSession().setAttribute("errorMsg", "请登陆"); response.sendRedirect(request.getContextPath() + "/admin/login"); return false; } else { request.getSession().removeAttribute("errorMsg"); 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 { } } ================================================ FILE: src/main/java/ltd/newbee/mall/interceptor/NewBeeMallCartNumberInterceptor.java ================================================ /** * 严肃声明: * 开源版本请务必保留此注释头信息,若删除我方将保留所有法律责任追究! * 本系统已申请软件著作权,受国家版权局知识产权以及国家计算机软件著作权保护! * 可正常分享和学习源码,不得用于违法犯罪活动,违者必究! * Copyright (c) 2019-2020 十三 all rights reserved. * 版权所有,侵权必究! */ package ltd.newbee.mall.interceptor; import ltd.newbee.mall.common.Constants; import ltd.newbee.mall.controller.vo.NewBeeMallUserVO; import ltd.newbee.mall.dao.NewBeeMallShoppingCartItemMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * newbee-mall购物车数量处理 * * @author 13 * @qq交流群 796794009 * @email 2449207463@qq.com * @link https://github.com/newbee-ltd */ @Component public class NewBeeMallCartNumberInterceptor implements HandlerInterceptor { @Autowired private NewBeeMallShoppingCartItemMapper newBeeMallShoppingCartItemMapper; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object o) throws Exception { //购物车中的数量会更改,但是在这些接口中并没有对session中的数据做修改,这里统一处理一下 if (null != request.getSession() && null != request.getSession().getAttribute(Constants.MALL_USER_SESSION_KEY)) { //如果当前为登陆状态,就查询数据库并设置购物车中的数量值 NewBeeMallUserVO newBeeMallUserVO = (NewBeeMallUserVO) request.getSession().getAttribute(Constants.MALL_USER_SESSION_KEY); //设置购物车中的数量 newBeeMallUserVO.setShopCartItemCount(newBeeMallShoppingCartItemMapper.selectCountByUserId(newBeeMallUserVO.getUserId())); request.getSession().setAttribute(Constants.MALL_USER_SESSION_KEY, newBeeMallUserVO); } 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 { } } ================================================ FILE: src/main/java/ltd/newbee/mall/interceptor/NewBeeMallLoginInterceptor.java ================================================ /** * 严肃声明: * 开源版本请务必保留此注释头信息,若删除我方将保留所有法律责任追究! * 本系统已申请软件著作权,受国家版权局知识产权以及国家计算机软件著作权保护! * 可正常分享和学习源码,不得用于违法犯罪活动,违者必究! * Copyright (c) 2019-2020 十三 all rights reserved. * 版权所有,侵权必究! */ package ltd.newbee.mall.interceptor; import ltd.newbee.mall.common.Constants; import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * newbee-mall系统身份验证拦截器 * * @author 13 * @qq交流群 796794009 * @email 2449207463@qq.com * @link https://github.com/newbee-ltd */ @Component public class NewBeeMallLoginInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object o) throws Exception { if (null == request.getSession().getAttribute(Constants.MALL_USER_SESSION_KEY)) { response.sendRedirect(request.getContextPath() + "/login"); return false; } else { 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 { } } ================================================ FILE: src/main/java/ltd/newbee/mall/service/AdminUserService.java ================================================ /** * 严肃声明: * 开源版本请务必保留此注释头信息,若删除我方将保留所有法律责任追究! * 本系统已申请软件著作权,受国家版权局知识产权以及国家计算机软件著作权保护! * 可正常分享和学习源码,不得用于违法犯罪活动,违者必究! * Copyright (c) 2019-2020 十三 all rights reserved. * 版权所有,侵权必究! */ package ltd.newbee.mall.service; import ltd.newbee.mall.entity.AdminUser; public interface AdminUserService { AdminUser login(String userName, String password); /** * 获取用户信息 * * @param loginUserId * @return */ AdminUser getUserDetailById(Integer loginUserId); /** * 修改当前登录用户的密码 * * @param loginUserId * @param originalPassword * @param newPassword * @return */ Boolean updatePassword(Integer loginUserId, String originalPassword, String newPassword); /** * 修改当前登录用户的名称信息 * * @param loginUserId * @param loginUserName * @param nickName * @return */ Boolean updateName(Integer loginUserId, String loginUserName, String nickName); } ================================================ FILE: src/main/java/ltd/newbee/mall/service/NewBeeMallCarouselService.java ================================================ /** * 严肃声明: * 开源版本请务必保留此注释头信息,若删除我方将保留所有法律责任追究! * 本系统已申请软件著作权,受国家版权局知识产权以及国家计算机软件著作权保护! * 可正常分享和学习源码,不得用于违法犯罪活动,违者必究! * Copyright (c) 2019-2020 十三 all rights reserved. * 版权所有,侵权必究! */ package ltd.newbee.mall.service; import ltd.newbee.mall.controller.vo.NewBeeMallIndexCarouselVO; import ltd.newbee.mall.entity.Carousel; import ltd.newbee.mall.util.PageQueryUtil; import ltd.newbee.mall.util.PageResult; import java.util.List; public interface NewBeeMallCarouselService { /** * 后台分页 * * @param pageUtil * @return */ PageResult getCarouselPage(PageQueryUtil pageUtil); String saveCarousel(Carousel carousel); String updateCarousel(Carousel carousel); Carousel getCarouselById(Integer id); Boolean deleteBatch(Integer[] ids); /** * 返回固定数量的轮播图对象(首页调用) * * @param number * @return */ List getCarouselsForIndex(int number); } ================================================ FILE: src/main/java/ltd/newbee/mall/service/NewBeeMallCategoryService.java ================================================ /** * 严肃声明: * 开源版本请务必保留此注释头信息,若删除我方将保留所有法律责任追究! * 本系统已申请软件著作权,受国家版权局知识产权以及国家计算机软件著作权保护! * 可正常分享和学习源码,不得用于违法犯罪活动,违者必究! * Copyright (c) 2019-2020 十三 all rights reserved. * 版权所有,侵权必究! */ package ltd.newbee.mall.service; import ltd.newbee.mall.controller.vo.NewBeeMallIndexCategoryVO; import ltd.newbee.mall.controller.vo.SearchPageCategoryVO; import ltd.newbee.mall.entity.GoodsCategory; import ltd.newbee.mall.util.PageQueryUtil; import ltd.newbee.mall.util.PageResult; import java.util.List; public interface NewBeeMallCategoryService { /** * 后台分页 * * @param pageUtil * @return */ PageResult getCategorisPage(PageQueryUtil pageUtil); String saveCategory(GoodsCategory goodsCategory); String updateGoodsCategory(GoodsCategory goodsCategory); GoodsCategory getGoodsCategoryById(Long id); Boolean deleteBatch(Integer[] ids); /** * 返回分类数据(首页调用) * * @return */ List getCategoriesForIndex(); /** * 返回分类数据(搜索页调用) * * @param categoryId * @return */ SearchPageCategoryVO getCategoriesForSearch(Long categoryId); /** * 根据parentId和level获取分类列表 * * @param parentIds * @param categoryLevel * @return */ List selectByLevelAndParentIdsAndNumber(List parentIds, int categoryLevel); } ================================================ FILE: src/main/java/ltd/newbee/mall/service/NewBeeMallGoodsService.java ================================================ /** * 严肃声明: * 开源版本请务必保留此注释头信息,若删除我方将保留所有法律责任追究! * 本系统已申请软件著作权,受国家版权局知识产权以及国家计算机软件著作权保护! * 可正常分享和学习源码,不得用于违法犯罪活动,违者必究! * Copyright (c) 2019-2020 十三 all rights reserved. * 版权所有,侵权必究! */ package ltd.newbee.mall.service; import ltd.newbee.mall.entity.NewBeeMallGoods; import ltd.newbee.mall.util.PageQueryUtil; import ltd.newbee.mall.util.PageResult; import java.util.List; public interface NewBeeMallGoodsService { /** * 后台分页 * * @param pageUtil * @return */ PageResult getNewBeeMallGoodsPage(PageQueryUtil pageUtil); /** * 添加商品 * * @param goods * @return */ String saveNewBeeMallGoods(NewBeeMallGoods goods); /** * 批量新增商品数据 * * @param newBeeMallGoodsList * @return */ void batchSaveNewBeeMallGoods(List newBeeMallGoodsList); /** * 修改商品信息 * * @param goods * @return */ String updateNewBeeMallGoods(NewBeeMallGoods goods); /** * 获取商品详情 * * @param id * @return */ NewBeeMallGoods getNewBeeMallGoodsById(Long id); /** * 批量修改销售状态(上架下架) * * @param ids * @return */ Boolean batchUpdateSellStatus(Long[] ids,int sellStatus); /** * 商品搜索 * * @param pageUtil * @return */ PageResult searchNewBeeMallGoods(PageQueryUtil pageUtil); } ================================================ FILE: src/main/java/ltd/newbee/mall/service/NewBeeMallIndexConfigService.java ================================================ /** * 严肃声明: * 开源版本请务必保留此注释头信息,若删除我方将保留所有法律责任追究! * 本系统已申请软件著作权,受国家版权局知识产权以及国家计算机软件著作权保护! * 可正常分享和学习源码,不得用于违法犯罪活动,违者必究! * Copyright (c) 2019-2020 十三 all rights reserved. * 版权所有,侵权必究! */ package ltd.newbee.mall.service; import ltd.newbee.mall.controller.vo.NewBeeMallIndexConfigGoodsVO; import ltd.newbee.mall.entity.IndexConfig; import ltd.newbee.mall.util.PageQueryUtil; import ltd.newbee.mall.util.PageResult; import java.util.List; public interface NewBeeMallIndexConfigService { /** * 后台分页 * * @param pageUtil * @return */ PageResult getConfigsPage(PageQueryUtil pageUtil); String saveIndexConfig(IndexConfig indexConfig); String updateIndexConfig(IndexConfig indexConfig); IndexConfig getIndexConfigById(Long id); /** * 返回固定数量的首页配置商品对象(首页调用) * * @param number * @return */ List getConfigGoodsesForIndex(int configType, int number); Boolean deleteBatch(Long[] ids); } ================================================ FILE: src/main/java/ltd/newbee/mall/service/NewBeeMallOrderService.java ================================================ /** * 严肃声明: * 开源版本请务必保留此注释头信息,若删除我方将保留所有法律责任追究! * 本系统已申请软件著作权,受国家版权局知识产权以及国家计算机软件著作权保护! * 可正常分享和学习源码,不得用于违法犯罪活动,违者必究! * Copyright (c) 2019-2020 十三 all rights reserved. * 版权所有,侵权必究! */ package ltd.newbee.mall.service; import ltd.newbee.mall.controller.vo.NewBeeMallOrderDetailVO; import ltd.newbee.mall.controller.vo.NewBeeMallOrderItemVO; import ltd.newbee.mall.controller.vo.NewBeeMallShoppingCartItemVO; import ltd.newbee.mall.controller.vo.NewBeeMallUserVO; import ltd.newbee.mall.entity.NewBeeMallOrder; import ltd.newbee.mall.util.PageQueryUtil; import ltd.newbee.mall.util.PageResult; import java.util.List; public interface NewBeeMallOrderService { /** * 后台分页 * * @param pageUtil * @return */ PageResult getNewBeeMallOrdersPage(PageQueryUtil pageUtil); /** * 订单信息修改 * * @param newBeeMallOrder * @return */ String updateOrderInfo(NewBeeMallOrder newBeeMallOrder); /** * 配货 * * @param ids * @return */ String checkDone(Long[] ids); /** * 出库 * * @param ids * @return */ String checkOut(Long[] ids); /** * 关闭订单 * * @param ids * @return */ String closeOrder(Long[] ids); /** * 保存订单 * * @param user * @param myShoppingCartItems * @return */ String saveOrder(NewBeeMallUserVO user, List myShoppingCartItems); /** * 获取订单详情 * * @param orderNo * @param userId * @return */ NewBeeMallOrderDetailVO getOrderDetailByOrderNo(String orderNo, Long userId); /** * 获取订单详情 * * @param orderNo * @return */ NewBeeMallOrder getNewBeeMallOrderByOrderNo(String orderNo); /** * 我的订单列表 * * @param pageUtil * @return */ PageResult getMyOrders(PageQueryUtil pageUtil); /** * 手动取消订单 * * @param orderNo * @param userId * @return */ String cancelOrder(String orderNo, Long userId); /** * 确认收货 * * @param orderNo * @param userId * @return */ String finishOrder(String orderNo, Long userId); String paySuccess(String orderNo, int payType); List getOrderItems(Long id); } ================================================ FILE: src/main/java/ltd/newbee/mall/service/NewBeeMallShoppingCartService.java ================================================ /** * 严肃声明: * 开源版本请务必保留此注释头信息,若删除我方将保留所有法律责任追究! * 本系统已申请软件著作权,受国家版权局知识产权以及国家计算机软件著作权保护! * 可正常分享和学习源码,不得用于违法犯罪活动,违者必究! * Copyright (c) 2019-2020 十三 all rights reserved. * 版权所有,侵权必究! */ package ltd.newbee.mall.service; import ltd.newbee.mall.controller.vo.NewBeeMallShoppingCartItemVO; import ltd.newbee.mall.entity.NewBeeMallShoppingCartItem; import java.util.List; public interface NewBeeMallShoppingCartService { /** * 保存商品至购物车中 * * @param newBeeMallShoppingCartItem * @return */ String saveNewBeeMallCartItem(NewBeeMallShoppingCartItem newBeeMallShoppingCartItem); /** * 修改购物车中的属性 * * @param newBeeMallShoppingCartItem * @return */ String updateNewBeeMallCartItem(NewBeeMallShoppingCartItem newBeeMallShoppingCartItem); /** * 获取购物项详情 * * @param newBeeMallShoppingCartItemId * @return */ NewBeeMallShoppingCartItem getNewBeeMallCartItemById(Long newBeeMallShoppingCartItemId); /** * 删除购物车中的商品 * * * @param shoppingCartItemId * @param userId * @return */ Boolean deleteById(Long shoppingCartItemId, Long userId); /** * 获取我的购物车中的列表数据 * * @param newBeeMallUserId * @return */ List getMyShoppingCartItems(Long newBeeMallUserId); } ================================================ FILE: src/main/java/ltd/newbee/mall/service/NewBeeMallUserService.java ================================================ /** * 严肃声明: * 开源版本请务必保留此注释头信息,若删除我方将保留所有法律责任追究! * 本系统已申请软件著作权,受国家版权局知识产权以及国家计算机软件著作权保护! * 可正常分享和学习源码,不得用于违法犯罪活动,违者必究! * Copyright (c) 2019-2020 十三 all rights reserved. * 版权所有,侵权必究! */ package ltd.newbee.mall.service; import ltd.newbee.mall.controller.vo.NewBeeMallUserVO; import ltd.newbee.mall.entity.MallUser; import ltd.newbee.mall.util.PageQueryUtil; import ltd.newbee.mall.util.PageResult; import javax.servlet.http.HttpSession; public interface NewBeeMallUserService { /** * 后台分页 * * @param pageUtil * @return */ PageResult getNewBeeMallUsersPage(PageQueryUtil pageUtil); /** * 用户注册 * * @param loginName * @param password * @return */ String register(String loginName, String password); /** * 登录 * * @param loginName * @param passwordMD5 * @param httpSession * @return */ String login(String loginName, String passwordMD5, HttpSession httpSession); /** * 用户信息修改并返回最新的用户信息 * * @param mallUser * @return */ NewBeeMallUserVO updateUserInfo(MallUser mallUser, HttpSession httpSession); /** * 用户禁用与解除禁用(0-未锁定 1-已锁定) * * @param ids * @param lockStatus * @return */ Boolean lockUsers(Integer[] ids, int lockStatus); } ================================================ FILE: src/main/java/ltd/newbee/mall/service/impl/AdminUserServiceImpl.java ================================================ /** * 严肃声明: * 开源版本请务必保留此注释头信息,若删除我方将保留所有法律责任追究! * 本系统已申请软件著作权,受国家版权局知识产权以及国家计算机软件著作权保护! * 可正常分享和学习源码,不得用于违法犯罪活动,违者必究! * Copyright (c) 2019-2020 十三 all rights reserved. * 版权所有,侵权必究! */ package ltd.newbee.mall.service.impl; import ltd.newbee.mall.entity.AdminUser; import ltd.newbee.mall.dao.AdminUserMapper; import ltd.newbee.mall.service.AdminUserService; import ltd.newbee.mall.util.MD5Util; import org.springframework.stereotype.Service; import javax.annotation.Resource; @Service public class AdminUserServiceImpl implements AdminUserService { @Resource private AdminUserMapper adminUserMapper; @Override public AdminUser login(String userName, String password) { String passwordMd5 = MD5Util.MD5Encode(password, "UTF-8"); return adminUserMapper.login(userName, passwordMd5); } @Override public AdminUser getUserDetailById(Integer loginUserId) { return adminUserMapper.selectByPrimaryKey(loginUserId); } @Override public Boolean updatePassword(Integer loginUserId, String originalPassword, String newPassword) { AdminUser adminUser = adminUserMapper.selectByPrimaryKey(loginUserId); //当前用户非空才可以进行更改 if (adminUser != null) { String originalPasswordMd5 = MD5Util.MD5Encode(originalPassword, "UTF-8"); String newPasswordMd5 = MD5Util.MD5Encode(newPassword, "UTF-8"); //比较原密码是否正确 if (originalPasswordMd5.equals(adminUser.getLoginPassword())) { //设置新密码并修改 adminUser.setLoginPassword(newPasswordMd5); if (adminUserMapper.updateByPrimaryKeySelective(adminUser) > 0) { //修改成功则返回true return true; } } } return false; } @Override public Boolean updateName(Integer loginUserId, String loginUserName, String nickName) { AdminUser adminUser = adminUserMapper.selectByPrimaryKey(loginUserId); //当前用户非空才可以进行更改 if (adminUser != null) { //设置新名称并修改 adminUser.setLoginUserName(loginUserName); adminUser.setNickName(nickName); if (adminUserMapper.updateByPrimaryKeySelective(adminUser) > 0) { //修改成功则返回true return true; } } return false; } } ================================================ FILE: src/main/java/ltd/newbee/mall/service/impl/NewBeeMallCarouselServiceImpl.java ================================================ /** * 严肃声明: * 开源版本请务必保留此注释头信息,若删除我方将保留所有法律责任追究! * 本系统已申请软件著作权,受国家版权局知识产权以及国家计算机软件著作权保护! * 可正常分享和学习源码,不得用于违法犯罪活动,违者必究! * Copyright (c) 2019-2020 十三 all rights reserved. * 版权所有,侵权必究! */ package ltd.newbee.mall.service.impl; import ltd.newbee.mall.common.ServiceResultEnum; import ltd.newbee.mall.controller.vo.NewBeeMallIndexCarouselVO; import ltd.newbee.mall.dao.CarouselMapper; import ltd.newbee.mall.entity.Carousel; import ltd.newbee.mall.service.NewBeeMallCarouselService; import ltd.newbee.mall.util.BeanUtil; import ltd.newbee.mall.util.PageQueryUtil; import ltd.newbee.mall.util.PageResult; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; import java.util.ArrayList; import java.util.Date; import java.util.List; @Service public class NewBeeMallCarouselServiceImpl implements NewBeeMallCarouselService { @Autowired private CarouselMapper carouselMapper; @Override public PageResult getCarouselPage(PageQueryUtil pageUtil) { List carousels = carouselMapper.findCarouselList(pageUtil); int total = carouselMapper.getTotalCarousels(pageUtil); PageResult pageResult = new PageResult(carousels, total, pageUtil.getLimit(), pageUtil.getPage()); return pageResult; } @Override public String saveCarousel(Carousel carousel) { if (carouselMapper.insertSelective(carousel) > 0) { return ServiceResultEnum.SUCCESS.getResult(); } return ServiceResultEnum.DB_ERROR.getResult(); } @Override public String updateCarousel(Carousel carousel) { Carousel temp = carouselMapper.selectByPrimaryKey(carousel.getCarouselId()); if (temp == null) { return ServiceResultEnum.DATA_NOT_EXIST.getResult(); } temp.setCarouselRank(carousel.getCarouselRank()); temp.setRedirectUrl(carousel.getRedirectUrl()); temp.setCarouselUrl(carousel.getCarouselUrl()); temp.setUpdateTime(new Date()); if (carouselMapper.updateByPrimaryKeySelective(temp) > 0) { return ServiceResultEnum.SUCCESS.getResult(); } return ServiceResultEnum.DB_ERROR.getResult(); } @Override public Carousel getCarouselById(Integer id) { return carouselMapper.selectByPrimaryKey(id); } @Override public Boolean deleteBatch(Integer[] ids) { if (ids.length < 1) { return false; } //删除数据 return carouselMapper.deleteBatch(ids) > 0; } @Override public List getCarouselsForIndex(int number) { List newBeeMallIndexCarouselVOS = new ArrayList<>(number); List carousels = carouselMapper.findCarouselsByNum(number); if (!CollectionUtils.isEmpty(carousels)) { newBeeMallIndexCarouselVOS = BeanUtil.copyList(carousels, NewBeeMallIndexCarouselVO.class); } return newBeeMallIndexCarouselVOS; } } ================================================ FILE: src/main/java/ltd/newbee/mall/service/impl/NewBeeMallCategoryServiceImpl.java ================================================ /** * 严肃声明: * 开源版本请务必保留此注释头信息,若删除我方将保留所有法律责任追究! * 本系统已申请软件著作权,受国家版权局知识产权以及国家计算机软件著作权保护! * 可正常分享和学习源码,不得用于违法犯罪活动,违者必究! * Copyright (c) 2019-2020 十三 all rights reserved. * 版权所有,侵权必究! */ package ltd.newbee.mall.service.impl; import ltd.newbee.mall.common.Constants; import ltd.newbee.mall.common.NewBeeMallCategoryLevelEnum; import ltd.newbee.mall.common.ServiceResultEnum; import ltd.newbee.mall.controller.vo.NewBeeMallIndexCategoryVO; import ltd.newbee.mall.controller.vo.SearchPageCategoryVO; import ltd.newbee.mall.controller.vo.SecondLevelCategoryVO; import ltd.newbee.mall.controller.vo.ThirdLevelCategoryVO; import ltd.newbee.mall.dao.GoodsCategoryMapper; import ltd.newbee.mall.entity.GoodsCategory; import ltd.newbee.mall.service.NewBeeMallCategoryService; import ltd.newbee.mall.util.BeanUtil; import ltd.newbee.mall.util.PageQueryUtil; import ltd.newbee.mall.util.PageResult; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; import java.util.*; import java.util.stream.Collectors; import static java.util.stream.Collectors.groupingBy; @Service public class NewBeeMallCategoryServiceImpl implements NewBeeMallCategoryService { @Autowired private GoodsCategoryMapper goodsCategoryMapper; @Override public PageResult getCategorisPage(PageQueryUtil pageUtil) { List goodsCategories = goodsCategoryMapper.findGoodsCategoryList(pageUtil); int total = goodsCategoryMapper.getTotalGoodsCategories(pageUtil); PageResult pageResult = new PageResult(goodsCategories, total, pageUtil.getLimit(), pageUtil.getPage()); return pageResult; } @Override public String saveCategory(GoodsCategory goodsCategory) { GoodsCategory temp = goodsCategoryMapper.selectByLevelAndName(goodsCategory.getCategoryLevel(), goodsCategory.getCategoryName()); if (temp != null) { return ServiceResultEnum.SAME_CATEGORY_EXIST.getResult(); } if (goodsCategoryMapper.insertSelective(goodsCategory) > 0) { return ServiceResultEnum.SUCCESS.getResult(); } return ServiceResultEnum.DB_ERROR.getResult(); } @Override public String updateGoodsCategory(GoodsCategory goodsCategory) { GoodsCategory temp = goodsCategoryMapper.selectByPrimaryKey(goodsCategory.getCategoryId()); if (temp == null) { return ServiceResultEnum.DATA_NOT_EXIST.getResult(); } GoodsCategory temp2 = goodsCategoryMapper.selectByLevelAndName(goodsCategory.getCategoryLevel(), goodsCategory.getCategoryName()); if (temp2 != null && !temp2.getCategoryId().equals(goodsCategory.getCategoryId())) { //同名且不同id 不能继续修改 return ServiceResultEnum.SAME_CATEGORY_EXIST.getResult(); } goodsCategory.setUpdateTime(new Date()); if (goodsCategoryMapper.updateByPrimaryKeySelective(goodsCategory) > 0) { return ServiceResultEnum.SUCCESS.getResult(); } return ServiceResultEnum.DB_ERROR.getResult(); } @Override public GoodsCategory getGoodsCategoryById(Long id) { return goodsCategoryMapper.selectByPrimaryKey(id); } @Override public Boolean deleteBatch(Integer[] ids) { if (ids.length < 1) { return false; } //删除分类数据 return goodsCategoryMapper.deleteBatch(ids) > 0; } @Override public List getCategoriesForIndex() { List newBeeMallIndexCategoryVOS = new ArrayList<>(); //获取一级分类的固定数量的数据 List firstLevelCategories = goodsCategoryMapper.selectByLevelAndParentIdsAndNumber(Collections.singletonList(0L), NewBeeMallCategoryLevelEnum.LEVEL_ONE.getLevel(), Constants.INDEX_CATEGORY_NUMBER); if (!CollectionUtils.isEmpty(firstLevelCategories)) { List firstLevelCategoryIds = firstLevelCategories.stream().map(GoodsCategory::getCategoryId).collect(Collectors.toList()); //获取二级分类的数据 List secondLevelCategories = goodsCategoryMapper.selectByLevelAndParentIdsAndNumber(firstLevelCategoryIds, NewBeeMallCategoryLevelEnum.LEVEL_TWO.getLevel(), 0); if (!CollectionUtils.isEmpty(secondLevelCategories)) { List secondLevelCategoryIds = secondLevelCategories.stream().map(GoodsCategory::getCategoryId).collect(Collectors.toList()); //获取三级分类的数据 List thirdLevelCategories = goodsCategoryMapper.selectByLevelAndParentIdsAndNumber(secondLevelCategoryIds, NewBeeMallCategoryLevelEnum.LEVEL_THREE.getLevel(), 0); if (!CollectionUtils.isEmpty(thirdLevelCategories)) { //根据 parentId 将 thirdLevelCategories 分组 Map> thirdLevelCategoryMap = thirdLevelCategories.stream().collect(groupingBy(GoodsCategory::getParentId)); List secondLevelCategoryVOS = new ArrayList<>(); //处理二级分类 for (GoodsCategory secondLevelCategory : secondLevelCategories) { SecondLevelCategoryVO secondLevelCategoryVO = new SecondLevelCategoryVO(); BeanUtil.copyProperties(secondLevelCategory, secondLevelCategoryVO); //如果该二级分类下有数据则放入 secondLevelCategoryVOS 对象中 if (thirdLevelCategoryMap.containsKey(secondLevelCategory.getCategoryId())) { //根据二级分类的id取出thirdLevelCategoryMap分组中的三级分类list List tempGoodsCategories = thirdLevelCategoryMap.get(secondLevelCategory.getCategoryId()); secondLevelCategoryVO.setThirdLevelCategoryVOS((BeanUtil.copyList(tempGoodsCategories, ThirdLevelCategoryVO.class))); secondLevelCategoryVOS.add(secondLevelCategoryVO); } } //处理一级分类 if (!CollectionUtils.isEmpty(secondLevelCategoryVOS)) { //根据 parentId 将 thirdLevelCategories 分组 Map> secondLevelCategoryVOMap = secondLevelCategoryVOS.stream().collect(groupingBy(SecondLevelCategoryVO::getParentId)); for (GoodsCategory firstCategory : firstLevelCategories) { NewBeeMallIndexCategoryVO newBeeMallIndexCategoryVO = new NewBeeMallIndexCategoryVO(); BeanUtil.copyProperties(firstCategory, newBeeMallIndexCategoryVO); //如果该一级分类下有数据则放入 newBeeMallIndexCategoryVOS 对象中 if (secondLevelCategoryVOMap.containsKey(firstCategory.getCategoryId())) { //根据一级分类的id取出secondLevelCategoryVOMap分组中的二级级分类list List tempGoodsCategories = secondLevelCategoryVOMap.get(firstCategory.getCategoryId()); newBeeMallIndexCategoryVO.setSecondLevelCategoryVOS(tempGoodsCategories); newBeeMallIndexCategoryVOS.add(newBeeMallIndexCategoryVO); } } } } } return newBeeMallIndexCategoryVOS; } else { return null; } } @Override public SearchPageCategoryVO getCategoriesForSearch(Long categoryId) { SearchPageCategoryVO searchPageCategoryVO = new SearchPageCategoryVO(); GoodsCategory thirdLevelGoodsCategory = goodsCategoryMapper.selectByPrimaryKey(categoryId); if (thirdLevelGoodsCategory != null && thirdLevelGoodsCategory.getCategoryLevel() == NewBeeMallCategoryLevelEnum.LEVEL_THREE.getLevel()) { //获取当前三级分类的二级分类 GoodsCategory secondLevelGoodsCategory = goodsCategoryMapper.selectByPrimaryKey(thirdLevelGoodsCategory.getParentId()); if (secondLevelGoodsCategory != null && secondLevelGoodsCategory.getCategoryLevel() == NewBeeMallCategoryLevelEnum.LEVEL_TWO.getLevel()) { //获取当前二级分类下的三级分类List List thirdLevelCategories = goodsCategoryMapper.selectByLevelAndParentIdsAndNumber(Collections.singletonList(secondLevelGoodsCategory.getCategoryId()), NewBeeMallCategoryLevelEnum.LEVEL_THREE.getLevel(), Constants.SEARCH_CATEGORY_NUMBER); searchPageCategoryVO.setCurrentCategoryName(thirdLevelGoodsCategory.getCategoryName()); searchPageCategoryVO.setSecondLevelCategoryName(secondLevelGoodsCategory.getCategoryName()); searchPageCategoryVO.setThirdLevelCategoryList(thirdLevelCategories); return searchPageCategoryVO; } } return null; } @Override public List selectByLevelAndParentIdsAndNumber(List parentIds, int categoryLevel) { return goodsCategoryMapper.selectByLevelAndParentIdsAndNumber(parentIds, categoryLevel, 0);//0代表查询所有 } } ================================================ FILE: src/main/java/ltd/newbee/mall/service/impl/NewBeeMallGoodsServiceImpl.java ================================================ /** * 严肃声明: * 开源版本请务必保留此注释头信息,若删除我方将保留所有法律责任追究! * 本系统已申请软件著作权,受国家版权局知识产权以及国家计算机软件著作权保护! * 可正常分享和学习源码,不得用于违法犯罪活动,违者必究! * Copyright (c) 2019-2020 十三 all rights reserved. * 版权所有,侵权必究! */ package ltd.newbee.mall.service.impl; import ltd.newbee.mall.common.NewBeeMallCategoryLevelEnum; import ltd.newbee.mall.common.NewBeeMallException; import ltd.newbee.mall.common.ServiceResultEnum; import ltd.newbee.mall.controller.vo.NewBeeMallSearchGoodsVO; import ltd.newbee.mall.dao.GoodsCategoryMapper; import ltd.newbee.mall.dao.NewBeeMallGoodsMapper; import ltd.newbee.mall.entity.GoodsCategory; import ltd.newbee.mall.entity.NewBeeMallGoods; import ltd.newbee.mall.service.NewBeeMallGoodsService; import ltd.newbee.mall.util.BeanUtil; import ltd.newbee.mall.util.NewBeeMallUtils; import ltd.newbee.mall.util.PageQueryUtil; import ltd.newbee.mall.util.PageResult; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; import java.util.ArrayList; import java.util.Date; import java.util.List; @Service public class NewBeeMallGoodsServiceImpl implements NewBeeMallGoodsService { @Autowired private NewBeeMallGoodsMapper goodsMapper; @Autowired private GoodsCategoryMapper goodsCategoryMapper; @Override public PageResult getNewBeeMallGoodsPage(PageQueryUtil pageUtil) { List goodsList = goodsMapper.findNewBeeMallGoodsList(pageUtil); int total = goodsMapper.getTotalNewBeeMallGoods(pageUtil); PageResult pageResult = new PageResult(goodsList, total, pageUtil.getLimit(), pageUtil.getPage()); return pageResult; } @Override public String saveNewBeeMallGoods(NewBeeMallGoods goods) { GoodsCategory goodsCategory = goodsCategoryMapper.selectByPrimaryKey(goods.getGoodsCategoryId()); // 分类不存在或者不是三级分类,则该参数字段异常 if (goodsCategory == null || goodsCategory.getCategoryLevel().intValue() != NewBeeMallCategoryLevelEnum.LEVEL_THREE.getLevel()) { return ServiceResultEnum.GOODS_CATEGORY_ERROR.getResult(); } if (goodsMapper.selectByCategoryIdAndName(goods.getGoodsName(), goods.getGoodsCategoryId()) != null) { return ServiceResultEnum.SAME_GOODS_EXIST.getResult(); } goods.setGoodsName(NewBeeMallUtils.cleanString(goods.getGoodsName())); goods.setGoodsIntro(NewBeeMallUtils.cleanString(goods.getGoodsIntro())); goods.setTag(NewBeeMallUtils.cleanString(goods.getTag())); if (goodsMapper.insertSelective(goods) > 0) { return ServiceResultEnum.SUCCESS.getResult(); } return ServiceResultEnum.DB_ERROR.getResult(); } @Override public void batchSaveNewBeeMallGoods(List newBeeMallGoodsList) { if (!CollectionUtils.isEmpty(newBeeMallGoodsList)) { goodsMapper.batchInsert(newBeeMallGoodsList); } } @Override public String updateNewBeeMallGoods(NewBeeMallGoods goods) { GoodsCategory goodsCategory = goodsCategoryMapper.selectByPrimaryKey(goods.getGoodsCategoryId()); // 分类不存在或者不是三级分类,则该参数字段异常 if (goodsCategory == null || goodsCategory.getCategoryLevel().intValue() != NewBeeMallCategoryLevelEnum.LEVEL_THREE.getLevel()) { return ServiceResultEnum.GOODS_CATEGORY_ERROR.getResult(); } NewBeeMallGoods temp = goodsMapper.selectByPrimaryKey(goods.getGoodsId()); if (temp == null) { return ServiceResultEnum.DATA_NOT_EXIST.getResult(); } NewBeeMallGoods temp2 = goodsMapper.selectByCategoryIdAndName(goods.getGoodsName(), goods.getGoodsCategoryId()); if (temp2 != null && !temp2.getGoodsId().equals(goods.getGoodsId())) { //name和分类id相同且不同id 不能继续修改 return ServiceResultEnum.SAME_GOODS_EXIST.getResult(); } goods.setGoodsName(NewBeeMallUtils.cleanString(goods.getGoodsName())); goods.setGoodsIntro(NewBeeMallUtils.cleanString(goods.getGoodsIntro())); goods.setTag(NewBeeMallUtils.cleanString(goods.getTag())); goods.setUpdateTime(new Date()); if (goodsMapper.updateByPrimaryKeySelective(goods) > 0) { return ServiceResultEnum.SUCCESS.getResult(); } return ServiceResultEnum.DB_ERROR.getResult(); } @Override public NewBeeMallGoods getNewBeeMallGoodsById(Long id) { NewBeeMallGoods newBeeMallGoods = goodsMapper.selectByPrimaryKey(id); if (newBeeMallGoods == null) { NewBeeMallException.fail(ServiceResultEnum.GOODS_NOT_EXIST.getResult()); } return newBeeMallGoods; } @Override public Boolean batchUpdateSellStatus(Long[] ids, int sellStatus) { return goodsMapper.batchUpdateSellStatus(ids, sellStatus) > 0; } @Override public PageResult searchNewBeeMallGoods(PageQueryUtil pageUtil) { List goodsList = goodsMapper.findNewBeeMallGoodsListBySearch(pageUtil); int total = goodsMapper.getTotalNewBeeMallGoodsBySearch(pageUtil); List newBeeMallSearchGoodsVOS = new ArrayList<>(); if (!CollectionUtils.isEmpty(goodsList)) { newBeeMallSearchGoodsVOS = BeanUtil.copyList(goodsList, NewBeeMallSearchGoodsVO.class); for (NewBeeMallSearchGoodsVO newBeeMallSearchGoodsVO : newBeeMallSearchGoodsVOS) { String goodsName = newBeeMallSearchGoodsVO.getGoodsName(); String goodsIntro = newBeeMallSearchGoodsVO.getGoodsIntro(); // 字符串过长导致文字超出的问题 if (goodsName.length() > 28) { goodsName = goodsName.substring(0, 28) + "..."; newBeeMallSearchGoodsVO.setGoodsName(goodsName); } if (goodsIntro.length() > 30) { goodsIntro = goodsIntro.substring(0, 30) + "..."; newBeeMallSearchGoodsVO.setGoodsIntro(goodsIntro); } } } PageResult pageResult = new PageResult(newBeeMallSearchGoodsVOS, total, pageUtil.getLimit(), pageUtil.getPage()); return pageResult; } } ================================================ FILE: src/main/java/ltd/newbee/mall/service/impl/NewBeeMallIndexConfigServiceImpl.java ================================================ /** * 严肃声明: * 开源版本请务必保留此注释头信息,若删除我方将保留所有法律责任追究! * 本系统已申请软件著作权,受国家版权局知识产权以及国家计算机软件著作权保护! * 可正常分享和学习源码,不得用于违法犯罪活动,违者必究! * Copyright (c) 2019-2020 十三 all rights reserved. * 版权所有,侵权必究! */ package ltd.newbee.mall.service.impl; import ltd.newbee.mall.common.ServiceResultEnum; import ltd.newbee.mall.controller.vo.NewBeeMallIndexConfigGoodsVO; import ltd.newbee.mall.dao.IndexConfigMapper; import ltd.newbee.mall.dao.NewBeeMallGoodsMapper; import ltd.newbee.mall.entity.IndexConfig; import ltd.newbee.mall.entity.NewBeeMallGoods; import ltd.newbee.mall.service.NewBeeMallIndexConfigService; import ltd.newbee.mall.util.BeanUtil; import ltd.newbee.mall.util.PageQueryUtil; import ltd.newbee.mall.util.PageResult; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.stream.Collectors; @Service public class NewBeeMallIndexConfigServiceImpl implements NewBeeMallIndexConfigService { @Autowired private IndexConfigMapper indexConfigMapper; @Autowired private NewBeeMallGoodsMapper goodsMapper; @Override public PageResult getConfigsPage(PageQueryUtil pageUtil) { List indexConfigs = indexConfigMapper.findIndexConfigList(pageUtil); int total = indexConfigMapper.getTotalIndexConfigs(pageUtil); PageResult pageResult = new PageResult(indexConfigs, total, pageUtil.getLimit(), pageUtil.getPage()); return pageResult; } @Override public String saveIndexConfig(IndexConfig indexConfig) { if (goodsMapper.selectByPrimaryKey(indexConfig.getGoodsId()) == null) { return ServiceResultEnum.GOODS_NOT_EXIST.getResult(); } if (indexConfigMapper.selectByTypeAndGoodsId(indexConfig.getConfigType(), indexConfig.getGoodsId()) != null) { return ServiceResultEnum.SAME_INDEX_CONFIG_EXIST.getResult(); } if (indexConfigMapper.insertSelective(indexConfig) > 0) { return ServiceResultEnum.SUCCESS.getResult(); } return ServiceResultEnum.DB_ERROR.getResult(); } @Override public String updateIndexConfig(IndexConfig indexConfig) { if (goodsMapper.selectByPrimaryKey(indexConfig.getGoodsId()) == null) { return ServiceResultEnum.GOODS_NOT_EXIST.getResult(); } IndexConfig temp = indexConfigMapper.selectByPrimaryKey(indexConfig.getConfigId()); if (temp == null) { return ServiceResultEnum.DATA_NOT_EXIST.getResult(); } IndexConfig temp2 = indexConfigMapper.selectByTypeAndGoodsId(indexConfig.getConfigType(), indexConfig.getGoodsId()); if (temp2 != null && !temp2.getConfigId().equals(indexConfig.getConfigId())) { //goodsId相同且不同id 不能继续修改 return ServiceResultEnum.SAME_INDEX_CONFIG_EXIST.getResult(); } indexConfig.setUpdateTime(new Date()); if (indexConfigMapper.updateByPrimaryKeySelective(indexConfig) > 0) { return ServiceResultEnum.SUCCESS.getResult(); } return ServiceResultEnum.DB_ERROR.getResult(); } @Override public IndexConfig getIndexConfigById(Long id) { return null; } @Override public List getConfigGoodsesForIndex(int configType, int number) { List newBeeMallIndexConfigGoodsVOS = new ArrayList<>(number); List indexConfigs = indexConfigMapper.findIndexConfigsByTypeAndNum(configType, number); if (!CollectionUtils.isEmpty(indexConfigs)) { //取出所有的goodsId List goodsIds = indexConfigs.stream().map(IndexConfig::getGoodsId).collect(Collectors.toList()); List newBeeMallGoods = goodsMapper.selectByPrimaryKeys(goodsIds); newBeeMallIndexConfigGoodsVOS = BeanUtil.copyList(newBeeMallGoods, NewBeeMallIndexConfigGoodsVO.class); for (NewBeeMallIndexConfigGoodsVO newBeeMallIndexConfigGoodsVO : newBeeMallIndexConfigGoodsVOS) { String goodsName = newBeeMallIndexConfigGoodsVO.getGoodsName(); String goodsIntro = newBeeMallIndexConfigGoodsVO.getGoodsIntro(); // 字符串过长导致文字超出的问题 if (goodsName.length() > 30) { goodsName = goodsName.substring(0, 30) + "..."; newBeeMallIndexConfigGoodsVO.setGoodsName(goodsName); } if (goodsIntro.length() > 22) { goodsIntro = goodsIntro.substring(0, 22) + "..."; newBeeMallIndexConfigGoodsVO.setGoodsIntro(goodsIntro); } } } return newBeeMallIndexConfigGoodsVOS; } @Override public Boolean deleteBatch(Long[] ids) { if (ids.length < 1) { return false; } //删除数据 return indexConfigMapper.deleteBatch(ids) > 0; } } ================================================ FILE: src/main/java/ltd/newbee/mall/service/impl/NewBeeMallOrderServiceImpl.java ================================================ /** * 严肃声明: * 开源版本请务必保留此注释头信息,若删除我方将保留所有法律责任追究! * 本系统已申请软件著作权,受国家版权局知识产权以及国家计算机软件著作权保护! * 可正常分享和学习源码,不得用于违法犯罪活动,违者必究! * Copyright (c) 2019-2020 十三 all rights reserved. * 版权所有,侵权必究! */ package ltd.newbee.mall.service.impl; import ltd.newbee.mall.common.*; import ltd.newbee.mall.controller.vo.*; import ltd.newbee.mall.dao.NewBeeMallGoodsMapper; import ltd.newbee.mall.dao.NewBeeMallOrderItemMapper; import ltd.newbee.mall.dao.NewBeeMallOrderMapper; import ltd.newbee.mall.dao.NewBeeMallShoppingCartItemMapper; import ltd.newbee.mall.entity.NewBeeMallGoods; import ltd.newbee.mall.entity.NewBeeMallOrder; import ltd.newbee.mall.entity.NewBeeMallOrderItem; import ltd.newbee.mall.entity.StockNumDTO; import ltd.newbee.mall.service.NewBeeMallOrderService; import ltd.newbee.mall.util.BeanUtil; import ltd.newbee.mall.util.NumberUtil; import ltd.newbee.mall.util.PageQueryUtil; import ltd.newbee.mall.util.PageResult; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; import java.util.*; import java.util.function.Function; import java.util.stream.Collectors; import static java.util.stream.Collectors.groupingBy; @Service public class NewBeeMallOrderServiceImpl implements NewBeeMallOrderService { @Autowired private NewBeeMallOrderMapper newBeeMallOrderMapper; @Autowired private NewBeeMallOrderItemMapper newBeeMallOrderItemMapper; @Autowired private NewBeeMallShoppingCartItemMapper newBeeMallShoppingCartItemMapper; @Autowired private NewBeeMallGoodsMapper newBeeMallGoodsMapper; @Override public PageResult getNewBeeMallOrdersPage(PageQueryUtil pageUtil) { List newBeeMallOrders = newBeeMallOrderMapper.findNewBeeMallOrderList(pageUtil); int total = newBeeMallOrderMapper.getTotalNewBeeMallOrders(pageUtil); PageResult pageResult = new PageResult(newBeeMallOrders, total, pageUtil.getLimit(), pageUtil.getPage()); return pageResult; } @Override @Transactional public String updateOrderInfo(NewBeeMallOrder newBeeMallOrder) { NewBeeMallOrder temp = newBeeMallOrderMapper.selectByPrimaryKey(newBeeMallOrder.getOrderId()); //不为空且orderStatus>=0且状态为出库之前可以修改部分信息 if (temp != null && temp.getOrderStatus() >= 0 && temp.getOrderStatus() < 3) { temp.setTotalPrice(newBeeMallOrder.getTotalPrice()); temp.setUserAddress(newBeeMallOrder.getUserAddress()); temp.setUpdateTime(new Date()); if (newBeeMallOrderMapper.updateByPrimaryKeySelective(temp) > 0) { return ServiceResultEnum.SUCCESS.getResult(); } return ServiceResultEnum.DB_ERROR.getResult(); } return ServiceResultEnum.DATA_NOT_EXIST.getResult(); } @Override @Transactional public String checkDone(Long[] ids) { //查询所有的订单 判断状态 修改状态和更新时间 List orders = newBeeMallOrderMapper.selectByPrimaryKeys(Arrays.asList(ids)); String errorOrderNos = ""; if (!CollectionUtils.isEmpty(orders)) { for (NewBeeMallOrder newBeeMallOrder : orders) { if (newBeeMallOrder.getIsDeleted() == 1) { errorOrderNos += newBeeMallOrder.getOrderNo() + " "; continue; } if (newBeeMallOrder.getOrderStatus() != 1) { errorOrderNos += newBeeMallOrder.getOrderNo() + " "; } } if (!StringUtils.hasText(errorOrderNos)) { //订单状态正常 可以执行配货完成操作 修改订单状态和更新时间 if (newBeeMallOrderMapper.checkDone(Arrays.asList(ids)) > 0) { return ServiceResultEnum.SUCCESS.getResult(); } else { return ServiceResultEnum.DB_ERROR.getResult(); } } else { //订单此时不可执行出库操作 if (errorOrderNos.length() > 0 && errorOrderNos.length() < 100) { return errorOrderNos + "订单的状态不是支付成功无法执行出库操作"; } else { return "你选择了太多状态不是支付成功的订单,无法执行配货完成操作"; } } } //未查询到数据 返回错误提示 return ServiceResultEnum.DATA_NOT_EXIST.getResult(); } @Override @Transactional public String checkOut(Long[] ids) { //查询所有的订单 判断状态 修改状态和更新时间 List orders = newBeeMallOrderMapper.selectByPrimaryKeys(Arrays.asList(ids)); String errorOrderNos = ""; if (!CollectionUtils.isEmpty(orders)) { for (NewBeeMallOrder newBeeMallOrder : orders) { if (newBeeMallOrder.getIsDeleted() == 1) { errorOrderNos += newBeeMallOrder.getOrderNo() + " "; continue; } if (newBeeMallOrder.getOrderStatus() != 1 && newBeeMallOrder.getOrderStatus() != 2) { errorOrderNos += newBeeMallOrder.getOrderNo() + " "; } } if (!StringUtils.hasText(errorOrderNos)) { //订单状态正常 可以执行出库操作 修改订单状态和更新时间 if (newBeeMallOrderMapper.checkOut(Arrays.asList(ids)) > 0) { return ServiceResultEnum.SUCCESS.getResult(); } else { return ServiceResultEnum.DB_ERROR.getResult(); } } else { //订单此时不可执行出库操作 if (errorOrderNos.length() > 0 && errorOrderNos.length() < 100) { return errorOrderNos + "订单的状态不是支付成功或配货完成无法执行出库操作"; } else { return "你选择了太多状态不是支付成功或配货完成的订单,无法执行出库操作"; } } } //未查询到数据 返回错误提示 return ServiceResultEnum.DATA_NOT_EXIST.getResult(); } @Override @Transactional public String closeOrder(Long[] ids) { //查询所有的订单 判断状态 修改状态和更新时间 List orders = newBeeMallOrderMapper.selectByPrimaryKeys(Arrays.asList(ids)); String errorOrderNos = ""; if (!CollectionUtils.isEmpty(orders)) { for (NewBeeMallOrder newBeeMallOrder : orders) { // isDeleted=1 一定为已关闭订单 if (newBeeMallOrder.getIsDeleted() == 1) { errorOrderNos += newBeeMallOrder.getOrderNo() + " "; continue; } //已关闭或者已完成无法关闭订单 if (newBeeMallOrder.getOrderStatus() == 4 || newBeeMallOrder.getOrderStatus() < 0) { errorOrderNos += newBeeMallOrder.getOrderNo() + " "; } } if (!StringUtils.hasText(errorOrderNos)) { //订单状态正常 可以执行关闭操作 修改订单状态和更新时间&&恢复库存 if (newBeeMallOrderMapper.closeOrder(Arrays.asList(ids), NewBeeMallOrderStatusEnum.ORDER_CLOSED_BY_JUDGE.getOrderStatus()) > 0 && recoverStockNum(Arrays.asList(ids))) { return ServiceResultEnum.SUCCESS.getResult(); } else { return ServiceResultEnum.DB_ERROR.getResult(); } } else { //订单此时不可执行关闭操作 if (errorOrderNos.length() > 0 && errorOrderNos.length() < 100) { return errorOrderNos + "订单不能执行关闭操作"; } else { return "你选择的订单不能执行关闭操作"; } } } //未查询到数据 返回错误提示 return ServiceResultEnum.DATA_NOT_EXIST.getResult(); } @Override @Transactional public String saveOrder(NewBeeMallUserVO user, List myShoppingCartItems) { List itemIdList = myShoppingCartItems.stream().map(NewBeeMallShoppingCartItemVO::getCartItemId).collect(Collectors.toList()); List goodsIds = myShoppingCartItems.stream().map(NewBeeMallShoppingCartItemVO::getGoodsId).collect(Collectors.toList()); List newBeeMallGoods = newBeeMallGoodsMapper.selectByPrimaryKeys(goodsIds); //检查是否包含已下架商品 List goodsListNotSelling = newBeeMallGoods.stream() .filter(newBeeMallGoodsTemp -> newBeeMallGoodsTemp.getGoodsSellStatus() != Constants.SELL_STATUS_UP) .collect(Collectors.toList()); if (!CollectionUtils.isEmpty(goodsListNotSelling)) { //goodsListNotSelling 对象非空则表示有下架商品 NewBeeMallException.fail(goodsListNotSelling.get(0).getGoodsName() + "已下架,无法生成订单"); } Map newBeeMallGoodsMap = newBeeMallGoods.stream().collect(Collectors.toMap(NewBeeMallGoods::getGoodsId, Function.identity(), (entity1, entity2) -> entity1)); //判断商品库存 for (NewBeeMallShoppingCartItemVO shoppingCartItemVO : myShoppingCartItems) { //查出的商品中不存在购物车中的这条关联商品数据,直接返回错误提醒 if (!newBeeMallGoodsMap.containsKey(shoppingCartItemVO.getGoodsId())) { NewBeeMallException.fail(ServiceResultEnum.SHOPPING_ITEM_ERROR.getResult()); } //存在数量大于库存的情况,直接返回错误提醒 if (shoppingCartItemVO.getGoodsCount() > newBeeMallGoodsMap.get(shoppingCartItemVO.getGoodsId()).getStockNum()) { NewBeeMallException.fail(ServiceResultEnum.SHOPPING_ITEM_COUNT_ERROR.getResult()); } } //删除购物项 if (!CollectionUtils.isEmpty(itemIdList) && !CollectionUtils.isEmpty(goodsIds) && !CollectionUtils.isEmpty(newBeeMallGoods)) { if (newBeeMallShoppingCartItemMapper.deleteBatch(itemIdList) > 0) { List stockNumDTOS = BeanUtil.copyList(myShoppingCartItems, StockNumDTO.class); int updateStockNumResult = newBeeMallGoodsMapper.updateStockNum(stockNumDTOS); if (updateStockNumResult < 1) { NewBeeMallException.fail(ServiceResultEnum.SHOPPING_ITEM_COUNT_ERROR.getResult()); } //生成订单号 String orderNo = NumberUtil.genOrderNo(); int priceTotal = 0; //保存订单 NewBeeMallOrder newBeeMallOrder = new NewBeeMallOrder(); newBeeMallOrder.setOrderNo(orderNo); newBeeMallOrder.setUserId(user.getUserId()); newBeeMallOrder.setUserAddress(user.getAddress()); //总价 for (NewBeeMallShoppingCartItemVO newBeeMallShoppingCartItemVO : myShoppingCartItems) { priceTotal += newBeeMallShoppingCartItemVO.getGoodsCount() * newBeeMallShoppingCartItemVO.getSellingPrice(); } if (priceTotal < 1) { NewBeeMallException.fail(ServiceResultEnum.ORDER_PRICE_ERROR.getResult()); } newBeeMallOrder.setTotalPrice(priceTotal); //订单body字段,用来作为生成支付单描述信息,暂时未接入第三方支付接口,故该字段暂时设为空字符串 String extraInfo = ""; newBeeMallOrder.setExtraInfo(extraInfo); //生成订单项并保存订单项纪录 if (newBeeMallOrderMapper.insertSelective(newBeeMallOrder) > 0) { //生成所有的订单项快照,并保存至数据库 List newBeeMallOrderItems = new ArrayList<>(); for (NewBeeMallShoppingCartItemVO newBeeMallShoppingCartItemVO : myShoppingCartItems) { NewBeeMallOrderItem newBeeMallOrderItem = new NewBeeMallOrderItem(); //使用BeanUtil工具类将newBeeMallShoppingCartItemVO中的属性复制到newBeeMallOrderItem对象中 BeanUtil.copyProperties(newBeeMallShoppingCartItemVO, newBeeMallOrderItem); //NewBeeMallOrderMapper文件insert()方法中使用了useGeneratedKeys因此orderId可以获取到 newBeeMallOrderItem.setOrderId(newBeeMallOrder.getOrderId()); newBeeMallOrderItems.add(newBeeMallOrderItem); } //保存至数据库 if (newBeeMallOrderItemMapper.insertBatch(newBeeMallOrderItems) > 0) { //所有操作成功后,将订单号返回,以供Controller方法跳转到订单详情 return orderNo; } NewBeeMallException.fail(ServiceResultEnum.ORDER_PRICE_ERROR.getResult()); } NewBeeMallException.fail(ServiceResultEnum.DB_ERROR.getResult()); } NewBeeMallException.fail(ServiceResultEnum.DB_ERROR.getResult()); } NewBeeMallException.fail(ServiceResultEnum.SHOPPING_ITEM_ERROR.getResult()); return ServiceResultEnum.SHOPPING_ITEM_ERROR.getResult(); } @Override public NewBeeMallOrderDetailVO getOrderDetailByOrderNo(String orderNo, Long userId) { NewBeeMallOrder newBeeMallOrder = newBeeMallOrderMapper.selectByOrderNo(orderNo); if (newBeeMallOrder == null) { NewBeeMallException.fail(ServiceResultEnum.ORDER_NOT_EXIST_ERROR.getResult()); } //验证是否是当前userId下的订单,否则报错 if (!userId.equals(newBeeMallOrder.getUserId())) { NewBeeMallException.fail(ServiceResultEnum.NO_PERMISSION_ERROR.getResult()); } List orderItems = newBeeMallOrderItemMapper.selectByOrderId(newBeeMallOrder.getOrderId()); //获取订单项数据 if (CollectionUtils.isEmpty(orderItems)) { NewBeeMallException.fail(ServiceResultEnum.ORDER_ITEM_NOT_EXIST_ERROR.getResult()); } List newBeeMallOrderItemVOS = BeanUtil.copyList(orderItems, NewBeeMallOrderItemVO.class); NewBeeMallOrderDetailVO newBeeMallOrderDetailVO = new NewBeeMallOrderDetailVO(); BeanUtil.copyProperties(newBeeMallOrder, newBeeMallOrderDetailVO); newBeeMallOrderDetailVO.setOrderStatusString(NewBeeMallOrderStatusEnum.getNewBeeMallOrderStatusEnumByStatus(newBeeMallOrderDetailVO.getOrderStatus()).getName()); newBeeMallOrderDetailVO.setPayTypeString(PayTypeEnum.getPayTypeEnumByType(newBeeMallOrderDetailVO.getPayType()).getName()); newBeeMallOrderDetailVO.setNewBeeMallOrderItemVOS(newBeeMallOrderItemVOS); return newBeeMallOrderDetailVO; } @Override public NewBeeMallOrder getNewBeeMallOrderByOrderNo(String orderNo) { return newBeeMallOrderMapper.selectByOrderNo(orderNo); } @Override public PageResult getMyOrders(PageQueryUtil pageUtil) { int total = newBeeMallOrderMapper.getTotalNewBeeMallOrders(pageUtil); List newBeeMallOrders = newBeeMallOrderMapper.findNewBeeMallOrderList(pageUtil); List orderListVOS = new ArrayList<>(); if (total > 0) { //数据转换 将实体类转成vo orderListVOS = BeanUtil.copyList(newBeeMallOrders, NewBeeMallOrderListVO.class); //设置订单状态中文显示值 for (NewBeeMallOrderListVO newBeeMallOrderListVO : orderListVOS) { newBeeMallOrderListVO.setOrderStatusString(NewBeeMallOrderStatusEnum.getNewBeeMallOrderStatusEnumByStatus(newBeeMallOrderListVO.getOrderStatus()).getName()); } List orderIds = newBeeMallOrders.stream().map(NewBeeMallOrder::getOrderId).collect(Collectors.toList()); if (!CollectionUtils.isEmpty(orderIds)) { List orderItems = newBeeMallOrderItemMapper.selectByOrderIds(orderIds); Map> itemByOrderIdMap = orderItems.stream().collect(groupingBy(NewBeeMallOrderItem::getOrderId)); for (NewBeeMallOrderListVO newBeeMallOrderListVO : orderListVOS) { //封装每个订单列表对象的订单项数据 if (itemByOrderIdMap.containsKey(newBeeMallOrderListVO.getOrderId())) { List orderItemListTemp = itemByOrderIdMap.get(newBeeMallOrderListVO.getOrderId()); //将NewBeeMallOrderItem对象列表转换成NewBeeMallOrderItemVO对象列表 List newBeeMallOrderItemVOS = BeanUtil.copyList(orderItemListTemp, NewBeeMallOrderItemVO.class); newBeeMallOrderListVO.setNewBeeMallOrderItemVOS(newBeeMallOrderItemVOS); } } } } PageResult pageResult = new PageResult(orderListVOS, total, pageUtil.getLimit(), pageUtil.getPage()); return pageResult; } @Override @Transactional public String cancelOrder(String orderNo, Long userId) { NewBeeMallOrder newBeeMallOrder = newBeeMallOrderMapper.selectByOrderNo(orderNo); if (newBeeMallOrder != null) { //验证是否是当前userId下的订单,否则报错 if (!userId.equals(newBeeMallOrder.getUserId())) { NewBeeMallException.fail(ServiceResultEnum.NO_PERMISSION_ERROR.getResult()); } //订单状态判断 if (newBeeMallOrder.getOrderStatus().intValue() == NewBeeMallOrderStatusEnum.ORDER_SUCCESS.getOrderStatus() || newBeeMallOrder.getOrderStatus().intValue() == NewBeeMallOrderStatusEnum.ORDER_CLOSED_BY_MALLUSER.getOrderStatus() || newBeeMallOrder.getOrderStatus().intValue() == NewBeeMallOrderStatusEnum.ORDER_CLOSED_BY_EXPIRED.getOrderStatus() || newBeeMallOrder.getOrderStatus().intValue() == NewBeeMallOrderStatusEnum.ORDER_CLOSED_BY_JUDGE.getOrderStatus()) { return ServiceResultEnum.ORDER_STATUS_ERROR.getResult(); } //修改订单状态&&恢复库存 if (newBeeMallOrderMapper.closeOrder(Collections.singletonList(newBeeMallOrder.getOrderId()), NewBeeMallOrderStatusEnum.ORDER_CLOSED_BY_MALLUSER.getOrderStatus()) > 0 && recoverStockNum(Collections.singletonList(newBeeMallOrder.getOrderId()))) { return ServiceResultEnum.SUCCESS.getResult(); } else { return ServiceResultEnum.DB_ERROR.getResult(); } } return ServiceResultEnum.ORDER_NOT_EXIST_ERROR.getResult(); } @Override public String finishOrder(String orderNo, Long userId) { NewBeeMallOrder newBeeMallOrder = newBeeMallOrderMapper.selectByOrderNo(orderNo); if (newBeeMallOrder != null) { //验证是否是当前userId下的订单,否则报错 if (!userId.equals(newBeeMallOrder.getUserId())) { return ServiceResultEnum.NO_PERMISSION_ERROR.getResult(); } //订单状态判断 非出库状态下不进行修改操作 if (newBeeMallOrder.getOrderStatus().intValue() != NewBeeMallOrderStatusEnum.ORDER_EXPRESS.getOrderStatus()) { return ServiceResultEnum.ORDER_STATUS_ERROR.getResult(); } newBeeMallOrder.setOrderStatus((byte) NewBeeMallOrderStatusEnum.ORDER_SUCCESS.getOrderStatus()); newBeeMallOrder.setUpdateTime(new Date()); if (newBeeMallOrderMapper.updateByPrimaryKeySelective(newBeeMallOrder) > 0) { return ServiceResultEnum.SUCCESS.getResult(); } else { return ServiceResultEnum.DB_ERROR.getResult(); } } return ServiceResultEnum.ORDER_NOT_EXIST_ERROR.getResult(); } @Override public String paySuccess(String orderNo, int payType) { NewBeeMallOrder newBeeMallOrder = newBeeMallOrderMapper.selectByOrderNo(orderNo); if (newBeeMallOrder != null) { //订单状态判断 非待支付状态下不进行修改操作 if (newBeeMallOrder.getOrderStatus().intValue() != NewBeeMallOrderStatusEnum.ORDER_PRE_PAY.getOrderStatus()) { return ServiceResultEnum.ORDER_STATUS_ERROR.getResult(); } newBeeMallOrder.setOrderStatus((byte) NewBeeMallOrderStatusEnum.ORDER_PAID.getOrderStatus()); newBeeMallOrder.setPayType((byte) payType); newBeeMallOrder.setPayStatus((byte) PayStatusEnum.PAY_SUCCESS.getPayStatus()); newBeeMallOrder.setPayTime(new Date()); newBeeMallOrder.setUpdateTime(new Date()); if (newBeeMallOrderMapper.updateByPrimaryKeySelective(newBeeMallOrder) > 0) { return ServiceResultEnum.SUCCESS.getResult(); } else { return ServiceResultEnum.DB_ERROR.getResult(); } } return ServiceResultEnum.ORDER_NOT_EXIST_ERROR.getResult(); } @Override public List getOrderItems(Long id) { NewBeeMallOrder newBeeMallOrder = newBeeMallOrderMapper.selectByPrimaryKey(id); if (newBeeMallOrder != null) { List orderItems = newBeeMallOrderItemMapper.selectByOrderId(newBeeMallOrder.getOrderId()); //获取订单项数据 if (!CollectionUtils.isEmpty(orderItems)) { List newBeeMallOrderItemVOS = BeanUtil.copyList(orderItems, NewBeeMallOrderItemVO.class); return newBeeMallOrderItemVOS; } } return null; } /** * 恢复库存 * @param orderIds * @return */ public Boolean recoverStockNum(List orderIds) { //查询对应的订单项 List newBeeMallOrderItems = newBeeMallOrderItemMapper.selectByOrderIds(orderIds); //获取对应的商品id和商品数量并赋值到StockNumDTO对象中 List stockNumDTOS = BeanUtil.copyList(newBeeMallOrderItems, StockNumDTO.class); //执行恢复库存的操作 int updateStockNumResult = newBeeMallGoodsMapper.recoverStockNum(stockNumDTOS); if (updateStockNumResult < 1) { NewBeeMallException.fail(ServiceResultEnum.CLOSE_ORDER_ERROR.getResult()); return false; } else { return true; } } } ================================================ FILE: src/main/java/ltd/newbee/mall/service/impl/NewBeeMallShoppingCartServiceImpl.java ================================================ /** * 严肃声明: * 开源版本请务必保留此注释头信息,若删除我方将保留所有法律责任追究! * 本系统已申请软件著作权,受国家版权局知识产权以及国家计算机软件著作权保护! * 可正常分享和学习源码,不得用于违法犯罪活动,违者必究! * Copyright (c) 2019-2020 十三 all rights reserved. * 版权所有,侵权必究! */ package ltd.newbee.mall.service.impl; import ltd.newbee.mall.common.Constants; import ltd.newbee.mall.common.ServiceResultEnum; import ltd.newbee.mall.controller.vo.NewBeeMallShoppingCartItemVO; import ltd.newbee.mall.dao.NewBeeMallGoodsMapper; import ltd.newbee.mall.dao.NewBeeMallShoppingCartItemMapper; import ltd.newbee.mall.entity.NewBeeMallGoods; import ltd.newbee.mall.entity.NewBeeMallShoppingCartItem; import ltd.newbee.mall.service.NewBeeMallShoppingCartService; import ltd.newbee.mall.util.BeanUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; import java.util.*; import java.util.function.Function; import java.util.stream.Collectors; @Service public class NewBeeMallShoppingCartServiceImpl implements NewBeeMallShoppingCartService { @Autowired private NewBeeMallShoppingCartItemMapper newBeeMallShoppingCartItemMapper; @Autowired private NewBeeMallGoodsMapper newBeeMallGoodsMapper; @Override public String saveNewBeeMallCartItem(NewBeeMallShoppingCartItem newBeeMallShoppingCartItem) { NewBeeMallShoppingCartItem temp = newBeeMallShoppingCartItemMapper.selectByUserIdAndGoodsId(newBeeMallShoppingCartItem.getUserId(), newBeeMallShoppingCartItem.getGoodsId()); if (temp != null) { //已存在则修改该记录 temp.setGoodsCount(newBeeMallShoppingCartItem.getGoodsCount()); return updateNewBeeMallCartItem(temp); } NewBeeMallGoods newBeeMallGoods = newBeeMallGoodsMapper.selectByPrimaryKey(newBeeMallShoppingCartItem.getGoodsId()); //商品为空 if (newBeeMallGoods == null) { return ServiceResultEnum.GOODS_NOT_EXIST.getResult(); } int totalItem = newBeeMallShoppingCartItemMapper.selectCountByUserId(newBeeMallShoppingCartItem.getUserId()) + 1; //超出单个商品的最大数量 if (newBeeMallShoppingCartItem.getGoodsCount() > Constants.SHOPPING_CART_ITEM_LIMIT_NUMBER) { return ServiceResultEnum.SHOPPING_CART_ITEM_LIMIT_NUMBER_ERROR.getResult(); } //超出最大数量 if (totalItem > Constants.SHOPPING_CART_ITEM_TOTAL_NUMBER) { return ServiceResultEnum.SHOPPING_CART_ITEM_TOTAL_NUMBER_ERROR.getResult(); } //保存记录 if (newBeeMallShoppingCartItemMapper.insertSelective(newBeeMallShoppingCartItem) > 0) { return ServiceResultEnum.SUCCESS.getResult(); } return ServiceResultEnum.DB_ERROR.getResult(); } @Override public String updateNewBeeMallCartItem(NewBeeMallShoppingCartItem newBeeMallShoppingCartItem) { NewBeeMallShoppingCartItem newBeeMallShoppingCartItemUpdate = newBeeMallShoppingCartItemMapper.selectByPrimaryKey(newBeeMallShoppingCartItem.getCartItemId()); if (newBeeMallShoppingCartItemUpdate == null) { return ServiceResultEnum.DATA_NOT_EXIST.getResult(); } //超出单个商品的最大数量 if (newBeeMallShoppingCartItem.getGoodsCount() > Constants.SHOPPING_CART_ITEM_LIMIT_NUMBER) { return ServiceResultEnum.SHOPPING_CART_ITEM_LIMIT_NUMBER_ERROR.getResult(); } //当前登录账号的userId与待修改的cartItem中userId不同,返回错误 if (!newBeeMallShoppingCartItemUpdate.getUserId().equals(newBeeMallShoppingCartItem.getUserId())) { return ServiceResultEnum.NO_PERMISSION_ERROR.getResult(); } //数值相同,则不执行数据操作 if (newBeeMallShoppingCartItem.getGoodsCount().equals(newBeeMallShoppingCartItemUpdate.getGoodsCount())) { return ServiceResultEnum.SUCCESS.getResult(); } newBeeMallShoppingCartItemUpdate.setGoodsCount(newBeeMallShoppingCartItem.getGoodsCount()); newBeeMallShoppingCartItemUpdate.setUpdateTime(new Date()); //修改记录 if (newBeeMallShoppingCartItemMapper.updateByPrimaryKeySelective(newBeeMallShoppingCartItemUpdate) > 0) { return ServiceResultEnum.SUCCESS.getResult(); } return ServiceResultEnum.DB_ERROR.getResult(); } @Override public NewBeeMallShoppingCartItem getNewBeeMallCartItemById(Long newBeeMallShoppingCartItemId) { return newBeeMallShoppingCartItemMapper.selectByPrimaryKey(newBeeMallShoppingCartItemId); } @Override public Boolean deleteById(Long shoppingCartItemId, Long userId) { NewBeeMallShoppingCartItem newBeeMallShoppingCartItem = newBeeMallShoppingCartItemMapper.selectByPrimaryKey(shoppingCartItemId); if (newBeeMallShoppingCartItem == null) { return false; } //userId不同不能删除 if (!userId.equals(newBeeMallShoppingCartItem.getUserId())) { return false; } return newBeeMallShoppingCartItemMapper.deleteByPrimaryKey(shoppingCartItemId) > 0; } @Override public List getMyShoppingCartItems(Long newBeeMallUserId) { List newBeeMallShoppingCartItemVOS = new ArrayList<>(); List newBeeMallShoppingCartItems = newBeeMallShoppingCartItemMapper.selectByUserId(newBeeMallUserId, Constants.SHOPPING_CART_ITEM_TOTAL_NUMBER); if (!CollectionUtils.isEmpty(newBeeMallShoppingCartItems)) { //查询商品信息并做数据转换 List newBeeMallGoodsIds = newBeeMallShoppingCartItems.stream().map(NewBeeMallShoppingCartItem::getGoodsId).collect(Collectors.toList()); List newBeeMallGoods = newBeeMallGoodsMapper.selectByPrimaryKeys(newBeeMallGoodsIds); Map newBeeMallGoodsMap = new HashMap<>(); if (!CollectionUtils.isEmpty(newBeeMallGoods)) { newBeeMallGoodsMap = newBeeMallGoods.stream().collect(Collectors.toMap(NewBeeMallGoods::getGoodsId, Function.identity(), (entity1, entity2) -> entity1)); } for (NewBeeMallShoppingCartItem newBeeMallShoppingCartItem : newBeeMallShoppingCartItems) { NewBeeMallShoppingCartItemVO newBeeMallShoppingCartItemVO = new NewBeeMallShoppingCartItemVO(); BeanUtil.copyProperties(newBeeMallShoppingCartItem, newBeeMallShoppingCartItemVO); if (newBeeMallGoodsMap.containsKey(newBeeMallShoppingCartItem.getGoodsId())) { NewBeeMallGoods newBeeMallGoodsTemp = newBeeMallGoodsMap.get(newBeeMallShoppingCartItem.getGoodsId()); newBeeMallShoppingCartItemVO.setGoodsCoverImg(newBeeMallGoodsTemp.getGoodsCoverImg()); String goodsName = newBeeMallGoodsTemp.getGoodsName(); // 字符串过长导致文字超出的问题 if (goodsName.length() > 28) { goodsName = goodsName.substring(0, 28) + "..."; } newBeeMallShoppingCartItemVO.setGoodsName(goodsName); newBeeMallShoppingCartItemVO.setSellingPrice(newBeeMallGoodsTemp.getSellingPrice()); newBeeMallShoppingCartItemVOS.add(newBeeMallShoppingCartItemVO); } } } return newBeeMallShoppingCartItemVOS; } } ================================================ FILE: src/main/java/ltd/newbee/mall/service/impl/NewBeeMallUserServiceImpl.java ================================================ /** * 严肃声明: * 开源版本请务必保留此注释头信息,若删除我方将保留所有法律责任追究! * 本系统已申请软件著作权,受国家版权局知识产权以及国家计算机软件著作权保护! * 可正常分享和学习源码,不得用于违法犯罪活动,违者必究! * Copyright (c) 2019-2020 十三 all rights reserved. * 版权所有,侵权必究! */ package ltd.newbee.mall.service.impl; import ltd.newbee.mall.common.Constants; import ltd.newbee.mall.common.ServiceResultEnum; import ltd.newbee.mall.controller.vo.NewBeeMallUserVO; import ltd.newbee.mall.dao.MallUserMapper; import ltd.newbee.mall.entity.MallUser; import ltd.newbee.mall.service.NewBeeMallUserService; import ltd.newbee.mall.util.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; import javax.servlet.http.HttpSession; import java.util.List; @Service public class NewBeeMallUserServiceImpl implements NewBeeMallUserService { @Autowired private MallUserMapper mallUserMapper; @Override public PageResult getNewBeeMallUsersPage(PageQueryUtil pageUtil) { List mallUsers = mallUserMapper.findMallUserList(pageUtil); int total = mallUserMapper.getTotalMallUsers(pageUtil); PageResult pageResult = new PageResult(mallUsers, total, pageUtil.getLimit(), pageUtil.getPage()); return pageResult; } @Override public String register(String loginName, String password) { if (mallUserMapper.selectByLoginName(loginName) != null) { return ServiceResultEnum.SAME_LOGIN_NAME_EXIST.getResult(); } MallUser registerUser = new MallUser(); registerUser.setLoginName(loginName); registerUser.setNickName(loginName); String passwordMD5 = MD5Util.MD5Encode(password, "UTF-8"); registerUser.setPasswordMd5(passwordMD5); if (mallUserMapper.insertSelective(registerUser) > 0) { return ServiceResultEnum.SUCCESS.getResult(); } return ServiceResultEnum.DB_ERROR.getResult(); } @Override public String login(String loginName, String passwordMD5, HttpSession httpSession) { MallUser user = mallUserMapper.selectByLoginNameAndPasswd(loginName, passwordMD5); if (user != null && httpSession != null) { if (user.getLockedFlag() == 1) { return ServiceResultEnum.LOGIN_USER_LOCKED.getResult(); } //昵称太长 影响页面展示 if (user.getNickName() != null && user.getNickName().length() > 7) { String tempNickName = user.getNickName().substring(0, 7) + ".."; user.setNickName(tempNickName); } NewBeeMallUserVO newBeeMallUserVO = new NewBeeMallUserVO(); BeanUtil.copyProperties(user, newBeeMallUserVO); //设置购物车中的数量 httpSession.setAttribute(Constants.MALL_USER_SESSION_KEY, newBeeMallUserVO); return ServiceResultEnum.SUCCESS.getResult(); } return ServiceResultEnum.LOGIN_ERROR.getResult(); } @Override public NewBeeMallUserVO updateUserInfo(MallUser mallUser, HttpSession httpSession) { NewBeeMallUserVO userTemp = (NewBeeMallUserVO) httpSession.getAttribute(Constants.MALL_USER_SESSION_KEY); MallUser userFromDB = mallUserMapper.selectByPrimaryKey(userTemp.getUserId()); if (userFromDB != null) { if (StringUtils.hasText(mallUser.getNickName())) { userFromDB.setNickName(NewBeeMallUtils.cleanString(mallUser.getNickName())); } if (StringUtils.hasText(mallUser.getAddress())) { userFromDB.setAddress(NewBeeMallUtils.cleanString(mallUser.getAddress())); } if (StringUtils.hasText(mallUser.getIntroduceSign())) { userFromDB.setIntroduceSign(NewBeeMallUtils.cleanString(mallUser.getIntroduceSign())); } if (mallUserMapper.updateByPrimaryKeySelective(userFromDB) > 0) { NewBeeMallUserVO newBeeMallUserVO = new NewBeeMallUserVO(); BeanUtil.copyProperties(userFromDB, newBeeMallUserVO); httpSession.setAttribute(Constants.MALL_USER_SESSION_KEY, newBeeMallUserVO); return newBeeMallUserVO; } } return null; } @Override public Boolean lockUsers(Integer[] ids, int lockStatus) { if (ids.length < 1) { return false; } return mallUserMapper.lockUserBatch(ids, lockStatus) > 0; } } ================================================ FILE: src/main/java/ltd/newbee/mall/util/BeanUtil.java ================================================ package ltd.newbee.mall.util; import org.springframework.beans.BeanUtils; import org.springframework.beans.BeanWrapper; import org.springframework.beans.BeanWrapperImpl; import org.springframework.beans.PropertyAccessorFactory; import java.beans.PropertyDescriptor; import java.lang.reflect.Field; import java.util.*; public abstract class BeanUtil { public static Object copyProperties(Object source, Object target, String... ignoreProperties) { if (source == null) { return target; } BeanUtils.copyProperties(source, target, ignoreProperties); return target; } public static List copyList(List sources, Class clazz) { return copyList(sources, clazz, null); } public static List copyList(List sources, Class clazz, Callback callback) { List targetList = new ArrayList<>(); if (sources != null) { try { for (Object source : sources) { T target = clazz.newInstance(); copyProperties(source, target); if (callback != null) { callback.set(source, target); } targetList.add(target); } } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } return targetList; } public static Map toMap(Object bean, String... ignoreProperties) { Map map = new LinkedHashMap<>(); List ignoreList = new ArrayList<>(Arrays.asList(ignoreProperties)); ignoreList.add("class"); BeanWrapper beanWrapper = PropertyAccessorFactory.forBeanPropertyAccess(bean); for (PropertyDescriptor pd : beanWrapper.getPropertyDescriptors()) { if (!ignoreList.contains(pd.getName()) && beanWrapper.isReadableProperty(pd.getName())) { Object propertyValue = beanWrapper.getPropertyValue(pd.getName()); map.put(pd.getName(), propertyValue); } } return map; } public static T toBean(Map map, Class beanType) { BeanWrapper beanWrapper = new BeanWrapperImpl(beanType); map.forEach((key, value) -> { if (beanWrapper.isWritableProperty(key)) { beanWrapper.setPropertyValue(key, value); } }); return (T) beanWrapper.getWrappedInstance(); } public static interface Callback { void set(Object source, T target); } //检查Pojo对象是否有null字段 public static boolean checkPojoNullField(Object o, Class clz) { try { Field[] fields = clz.getDeclaredFields(); for (Field field : fields) { field.setAccessible(true); if (field.get(o) == null) { return false; } } if (clz.getSuperclass() != Object.class) { return checkPojoNullField(o, clz.getSuperclass()); } return true; } catch (IllegalAccessException e) { return false; } } } ================================================ FILE: src/main/java/ltd/newbee/mall/util/JsonUtil.java ================================================ package ltd.newbee.mall.util; import java.io.IOException; import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.ObjectMapper; /** * 操作json的封装方法 * use:jackson */ public class JsonUtil { /* * 001.json转换成对象 * @param:传入对象,json字符串 * @return:Object */ public static Object jsonToObj(Class objClass, String jsonStr) throws JsonParseException, JsonMappingException, IOException { ObjectMapper mapper = new ObjectMapper(); return mapper.readValue(jsonStr, objClass); } /* * 002.对象转换成json * @param:传入对象 * @return:json字符串 */ public static String objToJson(Object obj) throws JsonProcessingException { ObjectMapper mapper = new ObjectMapper(); return mapper.writeValueAsString(obj); } } ================================================ FILE: src/main/java/ltd/newbee/mall/util/MD5Util.java ================================================ package ltd.newbee.mall.util; import java.security.MessageDigest; /** * @author 13 * @qq交流群 796794009 * @email 2449207463@qq.com * @link https://github.com/newbee-ltd */ public class MD5Util { 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(); } private static String byteToHexString(byte b) { int n = b; if (n < 0) n += 256; int d1 = n / 16; int d2 = n % 16; return hexDigits[d1] + hexDigits[d2]; } public static String MD5Encode(String origin, String charsetname) { String resultString = null; try { resultString = new String(origin); MessageDigest md = MessageDigest.getInstance("MD5"); if (charsetname == null || "".equals(charsetname)) resultString = byteArrayToHexString(md.digest(resultString .getBytes())); else resultString = byteArrayToHexString(md.digest(resultString .getBytes(charsetname))); } catch (Exception exception) { } return resultString; } private static final String hexDigits[] = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"}; } ================================================ FILE: src/main/java/ltd/newbee/mall/util/NewBeeMallUtils.java ================================================ package ltd.newbee.mall.util; import org.springframework.util.StringUtils; import java.net.URI; /** * @author 13 */ public class NewBeeMallUtils { public static URI getHost(URI uri) { URI effectiveURI = null; try { effectiveURI = new URI(uri.getScheme(), uri.getUserInfo(), uri.getHost(), uri.getPort(), null, null, null); } catch (Throwable var4) { effectiveURI = null; } return effectiveURI; } public static String cleanString(String value) { if (!StringUtils.hasText(value)) { return ""; } value = value.toLowerCase(); value = value.replaceAll("<", "& lt;").replaceAll(">", "& gt;"); value = value.replaceAll("\\(", "& #40;").replaceAll("\\)", "& #41;"); value = value.replaceAll("'", "& #39;"); value = value.replaceAll("onload", "0nl0ad"); value = value.replaceAll("xml", "xm1"); value = value.replaceAll("window", "wind0w"); value = value.replaceAll("click", "cl1ck"); value = value.replaceAll("var", "v0r"); value = value.replaceAll("let", "1et"); value = value.replaceAll("function", "functi0n"); value = value.replaceAll("return", "retu1n"); value = value.replaceAll("$", ""); value = value.replaceAll("document", "d0cument"); value = value.replaceAll("const", "c0nst"); value = value.replaceAll("eval\\((.*)\\)", ""); value = value.replaceAll("[\\\"\\\'][\\s]*javascript:(.*)[\\\"\\\']", "\"\""); value = value.replaceAll("script", "scr1pt"); value = value.replaceAll("insert", "1nsert"); value = value.replaceAll("drop", "dr0p"); value = value.replaceAll("create", "cre0ate"); value = value.replaceAll("update", "upd0ate"); value = value.replaceAll("alter", "a1ter"); value = value.replaceAll("from", "fr0m"); value = value.replaceAll("where", "wh1re"); value = value.replaceAll("database", "data1base"); value = value.replaceAll("table", "tab1e"); value = value.replaceAll("tb", "tb0"); return value; } } ================================================ FILE: src/main/java/ltd/newbee/mall/util/NumberUtil.java ================================================ package ltd.newbee.mall.util; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * @author 13 * @qq交流群 796794009 * @email 2449207463@qq.com * @link https://github.com/newbee-ltd */ public class NumberUtil { private NumberUtil() { } /** * 判断是否为11位电话号码 * * @param phone * @return */ public static boolean isPhone(String phone) { Pattern pattern = Pattern.compile("^((13[0-9])|(14[5,7])|(15[^4,\\D])|(17[0-8])|(18[0-9]))\\d{8}$"); Matcher matcher = pattern.matcher(phone); return matcher.matches(); } /** * 生成指定长度的随机数 * * @param length * @return */ public static int genRandomNum(int length) { int num = 1; double random = Math.random(); if (random < 0.1) { random = random + 0.1; } for (int i = 0; i < length; i++) { num = num * 10; } return (int) ((random * num)); } /** * 生成订单流水号 * * @return */ public static String genOrderNo() { StringBuffer buffer = new StringBuffer(String.valueOf(System.currentTimeMillis())); int num = genRandomNum(4); buffer.append(num); return buffer.toString(); } } ================================================ FILE: src/main/java/ltd/newbee/mall/util/PageQueryUtil.java ================================================ package ltd.newbee.mall.util; import java.util.LinkedHashMap; import java.util.Map; /** * 分页查询参数 * * @author 13 * @qq交流群 796794009 * @email 2449207463@qq.com * @link https://github.com/newbee-ltd */ public class PageQueryUtil extends LinkedHashMap { //当前页码 private int page; //每页条数 private int limit; public PageQueryUtil(Map params) { this.putAll(params); //分页参数 this.page = Integer.parseInt(params.get("page").toString()); this.limit = Integer.parseInt(params.get("limit").toString()); this.put("start", (page - 1) * limit); this.put("page", page); this.put("limit", limit); } public int getPage() { return page; } public void setPage(int page) { this.page = page; } public int getLimit() { return limit; } public void setLimit(int limit) { this.limit = limit; } @Override public String toString() { return "PageUtil{" + "page=" + page + ", limit=" + limit + '}'; } } ================================================ FILE: src/main/java/ltd/newbee/mall/util/PageResult.java ================================================ package ltd.newbee.mall.util; import java.io.Serializable; import java.util.List; /** * 分页工具类 * * @author 13 * @qq交流群 796794009 * @email 2449207463@qq.com * @link https://github.com/newbee-ltd */ public class PageResult implements Serializable { //总记录数 private int totalCount; //每页记录数 private int pageSize; //总页数 private int totalPage; //当前页数 private int currPage; //列表数据 private List list; /** * 分页 * * @param list 列表数据 * @param totalCount 总记录数 * @param pageSize 每页记录数 * @param currPage 当前页数 */ public PageResult(List list, int totalCount, int pageSize, int currPage) { this.list = list; this.totalCount = totalCount; this.pageSize = pageSize; this.currPage = currPage; this.totalPage = (int) Math.ceil((double) totalCount / pageSize); } public int getTotalCount() { return totalCount; } public void setTotalCount(int totalCount) { this.totalCount = totalCount; } public int getPageSize() { return pageSize; } public void setPageSize(int pageSize) { this.pageSize = pageSize; } public int getTotalPage() { return totalPage; } public void setTotalPage(int totalPage) { this.totalPage = totalPage; } public int getCurrPage() { return currPage; } public void setCurrPage(int currPage) { this.currPage = currPage; } public List getList() { return list; } public void setList(List list) { this.list = list; } } ================================================ FILE: src/main/java/ltd/newbee/mall/util/PatternUtil.java ================================================ package ltd.newbee.mall.util; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * 正则工具类 * * @author 13 * @qq交流群 796794009 * @email 2449207463@qq.com * @link https://github.com/newbee-ltd */ public class PatternUtil { /** * 匹配邮箱正则 */ private static final Pattern VALID_EMAIL_ADDRESS_REGEX = Pattern.compile("^[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,6}$", Pattern.CASE_INSENSITIVE); /** * 验证只包含中英文和数字的字符串 * * @param keyword * @return */ public static Boolean validKeyword(String keyword) { String regex = "^[a-zA-Z0-9\u4E00-\u9FA5]+$"; Pattern pattern = Pattern.compile(regex); Matcher match = pattern.matcher(keyword); return match.matches(); } /** * 判断是否是邮箱 * * @param emailStr * @return */ public static boolean isEmail(String emailStr) { Matcher matcher = VALID_EMAIL_ADDRESS_REGEX.matcher(emailStr); return matcher.find(); } /** * 判断是否是网址 * * @param urlString * @return */ public static boolean isURL(String urlString) { String regex = "^([hH][tT]{2}[pP]:/*|[hH][tT]{2}[pP][sS]:/*|[fF][tT][pP]:/*)(([A-Za-z0-9-~]+).)+([A-Za-z0-9-~\\/])+(\\?{0,1}(([A-Za-z0-9-~]+\\={0,1})([A-Za-z0-9-~]*)\\&{0,1})*)$"; Pattern pattern = Pattern.compile(regex); if (pattern.matcher(urlString).matches()) { return true; } else { return false; } } } ================================================ FILE: src/main/java/ltd/newbee/mall/util/Result.java ================================================ package ltd.newbee.mall.util; import java.io.Serializable; /** * @author 13 * @qq交流群 796794009 * @email 2449207463@qq.com * @link https://github.com/newbee-ltd */ public class Result implements Serializable { private static final long serialVersionUID = 1L; private int resultCode; private String message; private T data; public Result() { } public Result(int resultCode, String message) { this.resultCode = resultCode; this.message = message; } public int getResultCode() { return resultCode; } public void setResultCode(int resultCode) { this.resultCode = resultCode; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public T getData() { return data; } public void setData(T data) { this.data = data; } @Override public String toString() { return "Result{" + "resultCode=" + resultCode + ", message='" + message + '\'' + ", data=" + data + '}'; } } ================================================ FILE: src/main/java/ltd/newbee/mall/util/ResultGenerator.java ================================================ package ltd.newbee.mall.util; import org.springframework.util.StringUtils; /** * 响应结果生成工具 * * @author 13 * @qq交流群 796794009 * @email 2449207463@qq.com * @link https://github.com/newbee-ltd */ public class ResultGenerator { private static final String DEFAULT_SUCCESS_MESSAGE = "SUCCESS"; private static final String DEFAULT_FAIL_MESSAGE = "FAIL"; private static final int RESULT_CODE_SUCCESS = 200; private static final int RESULT_CODE_SERVER_ERROR = 500; public static Result genSuccessResult() { Result result = new Result(); result.setResultCode(RESULT_CODE_SUCCESS); result.setMessage(DEFAULT_SUCCESS_MESSAGE); return result; } public static Result genSuccessResult(String message) { Result result = new Result(); result.setResultCode(RESULT_CODE_SUCCESS); result.setMessage(message); return result; } public static Result genSuccessResult(Object data) { Result result = new Result(); result.setResultCode(RESULT_CODE_SUCCESS); result.setMessage(DEFAULT_SUCCESS_MESSAGE); result.setData(data); return result; } public static Result genFailResult(String message) { Result result = new Result(); result.setResultCode(RESULT_CODE_SERVER_ERROR); if (!StringUtils.hasText(message)) { result.setMessage(DEFAULT_FAIL_MESSAGE); } else { result.setMessage(message); } return result; } public static Result genErrorResult(int code, String message) { Result result = new Result(); result.setResultCode(code); result.setMessage(message); return result; } } ================================================ FILE: src/main/java/ltd/newbee/mall/util/SystemUtil.java ================================================ package ltd.newbee.mall.util; import java.math.BigInteger; import java.security.MessageDigest; /** * @author 13 * @qq交流群 796794009 * @email 2449207463@qq.com * @link https://github.com/newbee-ltd */ public class SystemUtil { private SystemUtil() { } /** * 登录或注册成功后,生成保持用户登录状态会话token值 * * @param src:为用户最新一次登录时的now()+user.id+random(4) * @return */ public static String genToken(String src) { if (null == src || "".equals(src)) { return null; } try { MessageDigest md = MessageDigest.getInstance("MD5"); md.update(src.getBytes()); String result = new BigInteger(1, md.digest()).toString(16); if (result.length() == 31) { result = result + "-"; } System.out.println(result); return result; } catch (Exception e) { return null; } } } ================================================ FILE: src/main/resources/application.properties ================================================ # author 13 # qq-group 796794009 # email 2449207463@qq.com # link https://github.com/newbee-ltd server.port=28089 spring.thymeleaf.cache=false spring.datasource.name=newbee-mall-datasource spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/newbee_mall_db?useUnicode=true&serverTimezone=Asia/Shanghai&characterEncoding=utf8&autoReconnect=true&useSSL=false&allowMultiQueries=true spring.datasource.username=root spring.datasource.password=123456 spring.datasource.hikari.minimum-idle=5 spring.datasource.hikari.maximum-pool-size=15 spring.datasource.hikari.auto-commit=true spring.datasource.hikari.idle-timeout=10000 spring.datasource.hikari.pool-name=hikariCP spring.datasource.hikari.max-lifetime=30000 spring.datasource.hikari.connection-timeout=30000 spring.datasource.hikari.connection-test-query=SELECT 1 # mybatis config mybatis.mapper-locations=classpath:mapper/*Mapper.xml ================================================ FILE: src/main/resources/mapper/AdminUserMapper.xml ================================================ admin_user_id, login_user_name, login_password, nick_name, locked insert into tb_newbee_mall_admin_user (admin_user_id, login_user_name, login_password, nick_name, locked) values (#{adminUserId,jdbcType=INTEGER}, #{loginUserName,jdbcType=VARCHAR}, #{loginPassword,jdbcType=VARCHAR}, #{nickName,jdbcType=VARCHAR}, #{locked,jdbcType=TINYINT}) insert into tb_newbee_mall_admin_user admin_user_id, login_user_name, login_password, nick_name, locked, #{adminUserId,jdbcType=INTEGER}, #{loginUserName,jdbcType=VARCHAR}, #{loginPassword,jdbcType=VARCHAR}, #{nickName,jdbcType=VARCHAR}, #{locked,jdbcType=TINYINT}, update tb_newbee_mall_admin_user login_user_name = #{loginUserName,jdbcType=VARCHAR}, login_password = #{loginPassword,jdbcType=VARCHAR}, nick_name = #{nickName,jdbcType=VARCHAR}, locked = #{locked,jdbcType=TINYINT}, where admin_user_id = #{adminUserId,jdbcType=INTEGER} update tb_newbee_mall_admin_user set login_user_name = #{loginUserName,jdbcType=VARCHAR}, login_password = #{loginPassword,jdbcType=VARCHAR}, nick_name = #{nickName,jdbcType=VARCHAR}, locked = #{locked,jdbcType=TINYINT} where admin_user_id = #{adminUserId,jdbcType=INTEGER} ================================================ FILE: src/main/resources/mapper/CarouselMapper.xml ================================================ carousel_id, carousel_url, redirect_url, carousel_rank, is_deleted, create_time, create_user, update_time, update_user delete from tb_newbee_mall_carousel where carousel_id = #{carouselId,jdbcType=INTEGER} insert into tb_newbee_mall_carousel (carousel_id, carousel_url, redirect_url, carousel_rank, is_deleted, create_time, create_user, update_time, update_user ) values (#{carouselId,jdbcType=INTEGER}, #{carouselUrl,jdbcType=VARCHAR}, #{redirectUrl,jdbcType=VARCHAR}, #{carouselRank,jdbcType=INTEGER}, #{isDeleted,jdbcType=TINYINT}, #{createTime,jdbcType=TIMESTAMP}, #{createUser,jdbcType=INTEGER}, #{updateTime,jdbcType=TIMESTAMP}, #{updateUser,jdbcType=INTEGER} ) insert into tb_newbee_mall_carousel carousel_id, carousel_url, redirect_url, carousel_rank, is_deleted, create_time, create_user, update_time, update_user, #{carouselId,jdbcType=INTEGER}, #{carouselUrl,jdbcType=VARCHAR}, #{redirectUrl,jdbcType=VARCHAR}, #{carouselRank,jdbcType=INTEGER}, #{isDeleted,jdbcType=TINYINT}, #{createTime,jdbcType=TIMESTAMP}, #{createUser,jdbcType=INTEGER}, #{updateTime,jdbcType=TIMESTAMP}, #{updateUser,jdbcType=INTEGER}, update tb_newbee_mall_carousel carousel_url = #{carouselUrl,jdbcType=VARCHAR}, redirect_url = #{redirectUrl,jdbcType=VARCHAR}, carousel_rank = #{carouselRank,jdbcType=INTEGER}, is_deleted = #{isDeleted,jdbcType=TINYINT}, create_time = #{createTime,jdbcType=TIMESTAMP}, create_user = #{createUser,jdbcType=INTEGER}, update_time = #{updateTime,jdbcType=TIMESTAMP}, update_user = #{updateUser,jdbcType=INTEGER}, where carousel_id = #{carouselId,jdbcType=INTEGER} update tb_newbee_mall_carousel set carousel_url = #{carouselUrl,jdbcType=VARCHAR}, redirect_url = #{redirectUrl,jdbcType=VARCHAR}, carousel_rank = #{carouselRank,jdbcType=INTEGER}, is_deleted = #{isDeleted,jdbcType=TINYINT}, create_time = #{createTime,jdbcType=TIMESTAMP}, create_user = #{createUser,jdbcType=INTEGER}, update_time = #{updateTime,jdbcType=TIMESTAMP}, update_user = #{updateUser,jdbcType=INTEGER} where carousel_id = #{carouselId,jdbcType=INTEGER} update tb_newbee_mall_carousel set is_deleted=1,update_time=now() where carousel_id in #{id} ================================================ FILE: src/main/resources/mapper/GoodsCategoryMapper.xml ================================================ category_id, category_level, parent_id, category_name, category_rank, is_deleted, create_time, create_user, update_time, update_user update tb_newbee_mall_goods_category set is_deleted=1 where category_id = #{categoryId,jdbcType=BIGINT} and is_deleted=0 update tb_newbee_mall_goods_category set is_deleted=1 where category_id in #{id} insert into tb_newbee_mall_goods_category (category_id, category_level, parent_id, category_name, category_rank, is_deleted, create_time, create_user, update_time, update_user) values (#{categoryId,jdbcType=BIGINT}, #{categoryLevel,jdbcType=TINYINT}, #{parentId,jdbcType=BIGINT}, #{categoryName,jdbcType=VARCHAR}, #{categoryRank,jdbcType=INTEGER}, #{isDeleted,jdbcType=TINYINT}, #{createTime,jdbcType=TIMESTAMP}, #{createUser,jdbcType=INTEGER}, #{updateTime,jdbcType=TIMESTAMP}, #{updateUser,jdbcType=INTEGER}) insert into tb_newbee_mall_goods_category category_id, category_level, parent_id, category_name, category_rank, is_deleted, create_time, create_user, update_time, update_user, #{categoryId,jdbcType=BIGINT}, #{categoryLevel,jdbcType=TINYINT}, #{parentId,jdbcType=BIGINT}, #{categoryName,jdbcType=VARCHAR}, #{categoryRank,jdbcType=INTEGER}, #{isDeleted,jdbcType=TINYINT}, #{createTime,jdbcType=TIMESTAMP}, #{createUser,jdbcType=INTEGER}, #{updateTime,jdbcType=TIMESTAMP}, #{updateUser,jdbcType=INTEGER}, update tb_newbee_mall_goods_category category_level = #{categoryLevel,jdbcType=TINYINT}, parent_id = #{parentId,jdbcType=BIGINT}, category_name = #{categoryName,jdbcType=VARCHAR}, category_rank = #{categoryRank,jdbcType=INTEGER}, is_deleted = #{isDeleted,jdbcType=TINYINT}, create_time = #{createTime,jdbcType=TIMESTAMP}, create_user = #{createUser,jdbcType=INTEGER}, update_time = #{updateTime,jdbcType=TIMESTAMP}, update_user = #{updateUser,jdbcType=INTEGER}, where category_id = #{categoryId,jdbcType=BIGINT} update tb_newbee_mall_goods_category set category_level = #{categoryLevel,jdbcType=TINYINT}, parent_id = #{parentId,jdbcType=BIGINT}, category_name = #{categoryName,jdbcType=VARCHAR}, category_rank = #{categoryRank,jdbcType=INTEGER}, is_deleted = #{isDeleted,jdbcType=TINYINT}, create_time = #{createTime,jdbcType=TIMESTAMP}, create_user = #{createUser,jdbcType=INTEGER}, update_time = #{updateTime,jdbcType=TIMESTAMP}, update_user = #{updateUser,jdbcType=INTEGER} where category_id = #{categoryId,jdbcType=BIGINT} ================================================ FILE: src/main/resources/mapper/IndexConfigMapper.xml ================================================ config_id, config_name, config_type, goods_id, redirect_url, config_rank, is_deleted, create_time, create_user, update_time, update_user update tb_newbee_mall_index_config set is_deleted=1 where config_id = #{configId,jdbcType=BIGINT} and is_deleted=0 update tb_newbee_mall_index_config set is_deleted=1,update_time=now() where is_deleted=0 and config_id in #{id} insert into tb_newbee_mall_index_config (config_id, config_name, config_type, goods_id, redirect_url, config_rank, is_deleted, create_time, create_user, update_time, update_user) values (#{configId,jdbcType=BIGINT}, #{configName,jdbcType=VARCHAR}, #{configType,jdbcType=TINYINT}, #{goodsId,jdbcType=BIGINT}, #{redirectUrl,jdbcType=VARCHAR}, #{configRank,jdbcType=INTEGER}, #{isDeleted,jdbcType=TINYINT}, #{createTime,jdbcType=TIMESTAMP}, #{createUser,jdbcType=INTEGER}, #{updateTime,jdbcType=TIMESTAMP}, #{updateUser,jdbcType=INTEGER}) insert into tb_newbee_mall_index_config config_id, config_name, config_type, goods_id, redirect_url, config_rank, is_deleted, create_time, create_user, update_time, update_user, #{configId,jdbcType=BIGINT}, #{configName,jdbcType=VARCHAR}, #{configType,jdbcType=TINYINT}, #{goodsId,jdbcType=BIGINT}, #{redirectUrl,jdbcType=VARCHAR}, #{configRank,jdbcType=INTEGER}, #{isDeleted,jdbcType=TINYINT}, #{createTime,jdbcType=TIMESTAMP}, #{createUser,jdbcType=INTEGER}, #{updateTime,jdbcType=TIMESTAMP}, #{updateUser,jdbcType=INTEGER}, update tb_newbee_mall_index_config config_name = #{configName,jdbcType=VARCHAR}, config_type = #{configType,jdbcType=TINYINT}, goods_id = #{goodsId,jdbcType=BIGINT}, redirect_url = #{redirectUrl,jdbcType=VARCHAR}, config_rank = #{configRank,jdbcType=INTEGER}, is_deleted = #{isDeleted,jdbcType=TINYINT}, create_time = #{createTime,jdbcType=TIMESTAMP}, create_user = #{createUser,jdbcType=INTEGER}, update_time = #{updateTime,jdbcType=TIMESTAMP}, update_user = #{updateUser,jdbcType=INTEGER}, where config_id = #{configId,jdbcType=BIGINT} update tb_newbee_mall_index_config set config_name = #{configName,jdbcType=VARCHAR}, config_type = #{configType,jdbcType=TINYINT}, goods_id = #{goodsId,jdbcType=BIGINT}, redirect_url = #{redirectUrl,jdbcType=VARCHAR}, config_rank = #{configRank,jdbcType=INTEGER}, is_deleted = #{isDeleted,jdbcType=TINYINT}, create_time = #{createTime,jdbcType=TIMESTAMP}, create_user = #{createUser,jdbcType=INTEGER}, update_time = #{updateTime,jdbcType=TIMESTAMP}, update_user = #{updateUser,jdbcType=INTEGER} where config_id = #{configId,jdbcType=BIGINT} ================================================ FILE: src/main/resources/mapper/MallUserMapper.xml ================================================ user_id, nick_name, login_name, password_md5, introduce_sign, address, is_deleted, locked_flag, create_time update tb_newbee_mall_user set is_deleted = 1 where user_id = #{userId,jdbcType=BIGINT} and is_deleted = 0 insert into tb_newbee_mall_user (user_id, nick_name, login_name, password_md5, introduce_sign, address, is_deleted, locked_flag, create_time ) values (#{userId,jdbcType=BIGINT}, #{nickName,jdbcType=VARCHAR}, #{loginName,jdbcType=VARCHAR}, #{passwordMd5,jdbcType=VARCHAR}, #{introduceSign,jdbcType=VARCHAR}, #{address,jdbcType=VARCHAR}, #{isDeleted,jdbcType=TINYINT}, #{lockedFlag,jdbcType=TINYINT}, #{createTime,jdbcType=TIMESTAMP} ) insert into tb_newbee_mall_user user_id, nick_name, login_name, password_md5, introduce_sign, address, is_deleted, locked_flag, create_time, #{userId,jdbcType=BIGINT}, #{nickName,jdbcType=VARCHAR}, #{loginName,jdbcType=VARCHAR}, #{passwordMd5,jdbcType=VARCHAR}, #{introduceSign,jdbcType=VARCHAR}, #{address,jdbcType=VARCHAR}, #{isDeleted,jdbcType=TINYINT}, #{lockedFlag,jdbcType=TINYINT}, #{createTime,jdbcType=TIMESTAMP}, update tb_newbee_mall_user nick_name = #{nickName,jdbcType=VARCHAR}, login_name = #{loginName,jdbcType=VARCHAR}, password_md5 = #{passwordMd5,jdbcType=VARCHAR}, introduce_sign = #{introduceSign,jdbcType=VARCHAR}, address = #{address,jdbcType=VARCHAR}, is_deleted = #{isDeleted,jdbcType=TINYINT}, locked_flag = #{lockedFlag,jdbcType=TINYINT}, create_time = #{createTime,jdbcType=TIMESTAMP}, where user_id = #{userId,jdbcType=BIGINT} update tb_newbee_mall_user set nick_name = #{nickName,jdbcType=VARCHAR}, login_name = #{loginName,jdbcType=VARCHAR}, password_md5 = #{passwordMd5,jdbcType=VARCHAR}, introduce_sign = #{introduceSign,jdbcType=VARCHAR}, address = #{address,jdbcType=VARCHAR}, is_deleted = #{isDeleted,jdbcType=TINYINT}, locked_flag = #{lockedFlag,jdbcType=TINYINT}, create_time = #{createTime,jdbcType=TIMESTAMP} where user_id = #{userId,jdbcType=BIGINT} update tb_newbee_mall_user set locked_flag=#{lockStatus} where user_id in #{id} ================================================ FILE: src/main/resources/mapper/NewBeeMallGoodsMapper.xml ================================================ goods_id, goods_name, goods_intro,goods_category_id, goods_cover_img, goods_carousel, original_price, selling_price, stock_num, tag, goods_sell_status, create_user, create_time, update_user, update_time goods_detail_content INSERT INTO tb_newbee_mall_goods_info(goods_name, goods_intro, goods_category_id,goods_cover_img, goods_carousel, goods_detail_content,original_price, selling_price, stock_num) VALUES (#{goods.goodsName},#{goods.goodsIntro},#{goods.goodsCategoryId},#{goods.goodsCoverImg},#{goods.goodsCarousel},#{goods.goodsDetailContent},#{goods.originalPrice},#{goods.sellingPrice},#{goods.stockNum}) update tb_newbee_mall_goods_info set stock_num = stock_num-#{stockNumDTO.goodsCount} where goods_id = #{stockNumDTO.goodsId} and stock_num>=#{stockNumDTO.goodsCount} and goods_sell_status = 0; update tb_newbee_mall_goods_info set stock_num = stock_num+#{stockNumDTO.goodsCount} where goods_id = #{stockNumDTO.goodsId} and stock_num>=#{stockNumDTO.goodsCount} and goods_sell_status = 0; update tb_newbee_mall_goods_info set goods_sell_status=#{sellStatus},update_time=now() where goods_id in #{id} delete from tb_newbee_mall_goods_info where goods_id = #{goodsId,jdbcType=BIGINT} insert into tb_newbee_mall_goods_info (goods_id, goods_name, goods_intro, goods_cover_img, goods_carousel, original_price, selling_price, stock_num, tag, goods_sell_status, create_user, create_time, update_user, update_time, goods_detail_content ) values (#{goodsId,jdbcType=BIGINT}, #{goodsName,jdbcType=VARCHAR}, #{goodsIntro,jdbcType=VARCHAR}, #{goodsCoverImg,jdbcType=VARCHAR}, #{goodsCarousel,jdbcType=VARCHAR}, #{originalPrice,jdbcType=INTEGER}, #{sellingPrice,jdbcType=INTEGER}, #{stockNum,jdbcType=INTEGER}, #{tag,jdbcType=VARCHAR}, #{goodsSellStatus,jdbcType=TINYINT}, #{createUser,jdbcType=INTEGER}, #{createTime,jdbcType=TIMESTAMP}, #{updateUser,jdbcType=INTEGER}, #{updateTime,jdbcType=TIMESTAMP}, #{goodsDetailContent,jdbcType=LONGVARCHAR} ) insert into tb_newbee_mall_goods_info goods_id, goods_name, goods_intro, goods_category_id, goods_cover_img, goods_carousel, original_price, selling_price, stock_num, tag, goods_sell_status, create_user, create_time, update_user, update_time, goods_detail_content, #{goodsId,jdbcType=BIGINT}, #{goodsName,jdbcType=VARCHAR}, #{goodsIntro,jdbcType=VARCHAR}, #{goodsCategoryId,jdbcType=BIGINT}, #{goodsCoverImg,jdbcType=VARCHAR}, #{goodsCarousel,jdbcType=VARCHAR}, #{originalPrice,jdbcType=INTEGER}, #{sellingPrice,jdbcType=INTEGER}, #{stockNum,jdbcType=INTEGER}, #{tag,jdbcType=VARCHAR}, #{goodsSellStatus,jdbcType=TINYINT}, #{createUser,jdbcType=INTEGER}, #{createTime,jdbcType=TIMESTAMP}, #{updateUser,jdbcType=INTEGER}, #{updateTime,jdbcType=TIMESTAMP}, #{goodsDetailContent,jdbcType=LONGVARCHAR}, update tb_newbee_mall_goods_info goods_name = #{goodsName,jdbcType=VARCHAR}, goods_intro = #{goodsIntro,jdbcType=VARCHAR}, goods_category_id = #{goodsCategoryId,jdbcType=BIGINT}, goods_cover_img = #{goodsCoverImg,jdbcType=VARCHAR}, goods_carousel = #{goodsCarousel,jdbcType=VARCHAR}, original_price = #{originalPrice,jdbcType=INTEGER}, selling_price = #{sellingPrice,jdbcType=INTEGER}, stock_num = #{stockNum,jdbcType=INTEGER}, tag = #{tag,jdbcType=VARCHAR}, goods_sell_status = #{goodsSellStatus,jdbcType=TINYINT}, create_user = #{createUser,jdbcType=INTEGER}, create_time = #{createTime,jdbcType=TIMESTAMP}, update_user = #{updateUser,jdbcType=INTEGER}, update_time = #{updateTime,jdbcType=TIMESTAMP}, goods_detail_content = #{goodsDetailContent,jdbcType=LONGVARCHAR}, where goods_id = #{goodsId,jdbcType=BIGINT} update tb_newbee_mall_goods_info set goods_name = #{goodsName,jdbcType=VARCHAR}, goods_intro = #{goodsIntro,jdbcType=VARCHAR}, goods_cover_img = #{goodsCoverImg,jdbcType=VARCHAR}, goods_carousel = #{goodsCarousel,jdbcType=VARCHAR}, original_price = #{originalPrice,jdbcType=INTEGER}, selling_price = #{sellingPrice,jdbcType=INTEGER}, stock_num = #{stockNum,jdbcType=INTEGER}, tag = #{tag,jdbcType=VARCHAR}, goods_sell_status = #{goodsSellStatus,jdbcType=TINYINT}, create_user = #{createUser,jdbcType=INTEGER}, create_time = #{createTime,jdbcType=TIMESTAMP}, update_user = #{updateUser,jdbcType=INTEGER}, update_time = #{updateTime,jdbcType=TIMESTAMP}, goods_detail_content = #{goodsDetailContent,jdbcType=LONGVARCHAR} where goods_id = #{goodsId,jdbcType=BIGINT} update tb_newbee_mall_goods_info set goods_name = #{goodsName,jdbcType=VARCHAR}, goods_intro = #{goodsIntro,jdbcType=VARCHAR}, goods_cover_img = #{goodsCoverImg,jdbcType=VARCHAR}, goods_carousel = #{goodsCarousel,jdbcType=VARCHAR}, original_price = #{originalPrice,jdbcType=INTEGER}, selling_price = #{sellingPrice,jdbcType=INTEGER}, stock_num = #{stockNum,jdbcType=INTEGER}, tag = #{tag,jdbcType=VARCHAR}, goods_sell_status = #{goodsSellStatus,jdbcType=TINYINT}, create_user = #{createUser,jdbcType=INTEGER}, create_time = #{createTime,jdbcType=TIMESTAMP}, update_user = #{updateUser,jdbcType=INTEGER}, update_time = #{updateTime,jdbcType=TIMESTAMP} where goods_id = #{goodsId,jdbcType=BIGINT} ================================================ FILE: src/main/resources/mapper/NewBeeMallOrderItemMapper.xml ================================================ order_item_id, order_id, goods_id, goods_name, goods_cover_img, selling_price, goods_count, create_time delete from tb_newbee_mall_order_item where order_item_id = #{orderItemId,jdbcType=BIGINT} insert into tb_newbee_mall_order_item (order_id, goods_id, goods_name, goods_cover_img, selling_price, goods_count) values (#{orderItem.orderId,jdbcType=BIGINT}, #{orderItem.goodsId,jdbcType=BIGINT}, #{orderItem.goodsName,jdbcType=VARCHAR}, #{orderItem.goodsCoverImg,jdbcType=VARCHAR}, #{orderItem.sellingPrice,jdbcType=INTEGER}, #{orderItem.goodsCount,jdbcType=INTEGER}) insert into tb_newbee_mall_order_item (order_item_id, order_id, goods_id, goods_name, goods_cover_img, selling_price, goods_count, create_time) values (#{orderItemId,jdbcType=BIGINT}, #{orderId,jdbcType=BIGINT}, #{goodsId,jdbcType=BIGINT}, #{goodsName,jdbcType=VARCHAR}, #{goodsCoverImg,jdbcType=VARCHAR}, #{sellingPrice,jdbcType=INTEGER}, #{goodsCount,jdbcType=INTEGER}, #{createTime,jdbcType=TIMESTAMP}) insert into tb_newbee_mall_order_item order_item_id, order_id, goods_id, goods_name, goods_cover_img, selling_price, goods_count, create_time, #{orderItemId,jdbcType=BIGINT}, #{orderId,jdbcType=BIGINT}, #{goodsId,jdbcType=BIGINT}, #{goodsName,jdbcType=VARCHAR}, #{goodsCoverImg,jdbcType=VARCHAR}, #{sellingPrice,jdbcType=INTEGER}, #{goodsCount,jdbcType=INTEGER}, #{createTime,jdbcType=TIMESTAMP}, update tb_newbee_mall_order_item order_id = #{orderId,jdbcType=BIGINT}, goods_id = #{goodsId,jdbcType=BIGINT}, goods_name = #{goodsName,jdbcType=VARCHAR}, goods_cover_img = #{goodsCoverImg,jdbcType=VARCHAR}, selling_price = #{sellingPrice,jdbcType=INTEGER}, goods_count = #{goodsCount,jdbcType=INTEGER}, create_time = #{createTime,jdbcType=TIMESTAMP}, where order_item_id = #{orderItemId,jdbcType=BIGINT} update tb_newbee_mall_order_item set order_id = #{orderId,jdbcType=BIGINT}, goods_id = #{goodsId,jdbcType=BIGINT}, goods_name = #{goodsName,jdbcType=VARCHAR}, goods_cover_img = #{goodsCoverImg,jdbcType=VARCHAR}, selling_price = #{sellingPrice,jdbcType=INTEGER}, goods_count = #{goodsCount,jdbcType=INTEGER}, create_time = #{createTime,jdbcType=TIMESTAMP} where order_item_id = #{orderItemId,jdbcType=BIGINT} ================================================ FILE: src/main/resources/mapper/NewBeeMallOrderMapper.xml ================================================ order_id, order_no, user_id, total_price, pay_status, pay_type, pay_time, order_status, extra_info, user_name, user_phone, user_address, is_deleted, create_time, update_time update tb_newbee_mall_order set is_deleted=1 where order_id = #{orderId,jdbcType=BIGINT} and is_deleted=0 insert into tb_newbee_mall_order (order_id, order_no, user_id, total_price, pay_status, pay_type, pay_time, order_status, extra_info, user_address,is_deleted, create_time, update_time ) values (#{orderId,jdbcType=BIGINT}, #{orderNo,jdbcType=VARCHAR}, #{userId,jdbcType=BIGINT}, #{totalPrice,jdbcType=INTEGER}, #{payStatus,jdbcType=TINYINT}, #{payType,jdbcType=TINYINT}, #{payTime,jdbcType=TIMESTAMP}, #{orderStatus,jdbcType=TINYINT}, #{extraInfo,jdbcType=VARCHAR}, #{userAddress,jdbcType=VARCHAR},#{isDeleted,jdbcType=TINYINT}, #{createTime,jdbcType=TIMESTAMP}, #{updateTime,jdbcType=TIMESTAMP} ) insert into tb_newbee_mall_order order_id, order_no, user_id, total_price, pay_status, pay_type, pay_time, order_status, extra_info, user_address, is_deleted, create_time, update_time, #{orderId,jdbcType=BIGINT}, #{orderNo,jdbcType=VARCHAR}, #{userId,jdbcType=BIGINT}, #{totalPrice,jdbcType=INTEGER}, #{payStatus,jdbcType=TINYINT}, #{payType,jdbcType=TINYINT}, #{payTime,jdbcType=TIMESTAMP}, #{orderStatus,jdbcType=TINYINT}, #{extraInfo,jdbcType=VARCHAR}, #{userAddress,jdbcType=VARCHAR}, #{isDeleted,jdbcType=TINYINT}, #{createTime,jdbcType=TIMESTAMP}, #{updateTime,jdbcType=TIMESTAMP}, update tb_newbee_mall_order set order_status = 2,update_time = now() where order_id in #{item} update tb_newbee_mall_order set order_status = 3,update_time = now() where order_id in #{item} update tb_newbee_mall_order set order_status = #{orderStatus},update_time = now() where order_id in #{item} update tb_newbee_mall_order order_no = #{orderNo,jdbcType=VARCHAR}, user_id = #{userId,jdbcType=BIGINT}, total_price = #{totalPrice,jdbcType=INTEGER}, pay_status = #{payStatus,jdbcType=TINYINT}, pay_type = #{payType,jdbcType=TINYINT}, pay_time = #{payTime,jdbcType=TIMESTAMP}, order_status = #{orderStatus,jdbcType=TINYINT}, extra_info = #{extraInfo,jdbcType=VARCHAR}, user_address = #{userAddress,jdbcType=VARCHAR}, is_deleted = #{isDeleted,jdbcType=TINYINT}, create_time = #{createTime,jdbcType=TIMESTAMP}, update_time = #{updateTime,jdbcType=TIMESTAMP}, where order_id = #{orderId,jdbcType=BIGINT} update tb_newbee_mall_order set order_no = #{orderNo,jdbcType=VARCHAR}, user_id = #{userId,jdbcType=BIGINT}, total_price = #{totalPrice,jdbcType=INTEGER}, pay_status = #{payStatus,jdbcType=TINYINT}, pay_type = #{payType,jdbcType=TINYINT}, pay_time = #{payTime,jdbcType=TIMESTAMP}, order_status = #{orderStatus,jdbcType=TINYINT}, extra_info = #{extraInfo,jdbcType=VARCHAR}, user_address = #{userAddress,jdbcType=VARCHAR}, is_deleted = #{isDeleted,jdbcType=TINYINT}, create_time = #{createTime,jdbcType=TIMESTAMP}, update_time = #{updateTime,jdbcType=TIMESTAMP} where order_id = #{orderId,jdbcType=BIGINT} ================================================ FILE: src/main/resources/mapper/NewBeeMallShoppingCartItemMapper.xml ================================================ cart_item_id, user_id, goods_id, goods_count, is_deleted, create_time, update_time update tb_newbee_mall_shopping_cart_item set is_deleted = 1 where cart_item_id = #{cartItemId,jdbcType=BIGINT} and is_deleted = 0 update tb_newbee_mall_shopping_cart_item set is_deleted=1 where cart_item_id in #{id} insert into tb_newbee_mall_shopping_cart_item (cart_item_id, user_id, goods_id, goods_count, is_deleted, create_time, update_time) values (#{cartItemId,jdbcType=BIGINT}, #{userId,jdbcType=BIGINT}, #{goodsId,jdbcType=BIGINT}, #{goodsCount,jdbcType=INTEGER}, #{isDeleted,jdbcType=TINYINT}, #{createTime,jdbcType=TIMESTAMP}, #{updateTime,jdbcType=TIMESTAMP}) insert into tb_newbee_mall_shopping_cart_item cart_item_id, user_id, goods_id, goods_count, is_deleted, create_time, update_time, #{cartItemId,jdbcType=BIGINT}, #{userId,jdbcType=BIGINT}, #{goodsId,jdbcType=BIGINT}, #{goodsCount,jdbcType=INTEGER}, #{isDeleted,jdbcType=TINYINT}, #{createTime,jdbcType=TIMESTAMP}, #{updateTime,jdbcType=TIMESTAMP}, update tb_newbee_mall_shopping_cart_item user_id = #{userId,jdbcType=BIGINT}, goods_id = #{goodsId,jdbcType=BIGINT}, goods_count = #{goodsCount,jdbcType=INTEGER}, is_deleted = #{isDeleted,jdbcType=TINYINT}, create_time = #{createTime,jdbcType=TIMESTAMP}, update_time = #{updateTime,jdbcType=TIMESTAMP}, where cart_item_id = #{cartItemId,jdbcType=BIGINT} update tb_newbee_mall_shopping_cart_item set user_id = #{userId,jdbcType=BIGINT}, goods_id = #{goodsId,jdbcType=BIGINT}, goods_count = #{goodsCount,jdbcType=INTEGER}, is_deleted = #{isDeleted,jdbcType=TINYINT}, create_time = #{createTime,jdbcType=TIMESTAMP}, update_time = #{updateTime,jdbcType=TIMESTAMP} where cart_item_id = #{cartItemId,jdbcType=BIGINT} ================================================ FILE: src/main/resources/newbee_mall_schema.sql ================================================ -- ---------------------------- -- Copyright (c) 2019-2020 十三 all rights reserved. -- ---------------------------- SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; -- ---------------------------- -- author 13 -- qq交流群 796794009 -- email 2449207463@qq.com -- link https://github.com/newbee-ltd -- Table structure for tb_newbee_mall_admin_user -- ---------------------------- DROP TABLE IF EXISTS `tb_newbee_mall_admin_user`; CREATE TABLE `tb_newbee_mall_admin_user` ( `admin_user_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '管理员id', `login_user_name` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '管理员登陆名称', `login_password` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '管理员登陆密码', `nick_name` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '管理员显示昵称', `locked` tinyint(4) NULL DEFAULT 0 COMMENT '是否锁定 0未锁定 1已锁定无法登陆', PRIMARY KEY (`admin_user_id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; -- ---------------------------- -- author 13 -- qq交流群 796794009 -- email 2449207463@qq.com -- link https://github.com/newbee-ltd -- Records of tb_newbee_mall_admin_user -- ---------------------------- INSERT INTO `tb_newbee_mall_admin_user` VALUES (1, 'admin', 'e10adc3949ba59abbe56e057f20f883e', '十三', 0); INSERT INTO `tb_newbee_mall_admin_user` VALUES (2, 'newbee-admin1', 'e10adc3949ba59abbe56e057f20f883e', '新蜂01', 0); INSERT INTO `tb_newbee_mall_admin_user` VALUES (3, 'newbee-admin2', 'e10adc3949ba59abbe56e057f20f883e', '新蜂02', 0); -- ---------------------------- -- author 13 -- qq交流群 796794009 -- email 2449207463@qq.com -- link https://github.com/newbee-ltd -- Table structure for tb_newbee_mall_carousel -- ---------------------------- DROP TABLE IF EXISTS `tb_newbee_mall_carousel`; CREATE TABLE `tb_newbee_mall_carousel` ( `carousel_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '首页轮播图主键id', `carousel_url` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '轮播图', `redirect_url` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '\'##\'' COMMENT '点击后的跳转地址(默认不跳转)', `carousel_rank` int(11) NOT NULL DEFAULT 0 COMMENT '排序值(字段越大越靠前)', `is_deleted` tinyint(4) NOT NULL DEFAULT 0 COMMENT '删除标识字段(0-未删除 1-已删除)', `create_time` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `create_user` int(11) NOT NULL DEFAULT 0 COMMENT '创建者id', `update_time` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间', `update_user` int(11) NOT NULL DEFAULT 0 COMMENT '修改者id', PRIMARY KEY (`carousel_id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 8 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; -- ---------------------------- -- author 13 -- qq交流群 796794009 -- email 2449207463@qq.com -- link https://github.com/newbee-ltd -- Records of tb_newbee_mall_carousel -- ---------------------------- INSERT INTO `tb_newbee_mall_carousel` VALUES (2, 'https://newbee-mall.oss-cn-beijing.aliyuncs.com/images/banner1.png', 'https://juejin.im/book/5da2f9d4f265da5b81794d48/section/5da2f9d6f265da5b794f2189', 13, 0, '2019-11-29 00:00:00', 0, '2019-11-29 00:00:00', 0); INSERT INTO `tb_newbee_mall_carousel` VALUES (5, 'https://newbee-mall.oss-cn-beijing.aliyuncs.com/images/banner2.png', 'https://juejin.im/book/5da2f9d4f265da5b81794d48/section/5da2f9d6f265da5b794f2189', 0, 0, '2019-11-29 00:00:00', 0, '2019-11-29 00:00:00', 0); -- ---------------------------- -- author 13 -- qq交流群 796794009 -- email 2449207463@qq.com -- link https://github.com/newbee-ltd -- Table structure for tb_newbee_mall_goods_category -- ---------------------------- DROP TABLE IF EXISTS `tb_newbee_mall_goods_category`; CREATE TABLE `tb_newbee_mall_goods_category` ( `category_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '分类id', `category_level` tinyint(4) NOT NULL DEFAULT 0 COMMENT '分类级别(1-一级分类 2-二级分类 3-三级分类)', `parent_id` bigint(20) NOT NULL DEFAULT 0 COMMENT '父分类id', `category_name` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '分类名称', `category_rank` int(11) NOT NULL DEFAULT 0 COMMENT '排序值(字段越大越靠前)', `is_deleted` tinyint(4) NOT NULL DEFAULT 0 COMMENT '删除标识字段(0-未删除 1-已删除)', `create_time` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `create_user` int(11) NOT NULL DEFAULT 0 COMMENT '创建者id', `update_time` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间', `update_user` int(11) NULL DEFAULT 0 COMMENT '修改者id', PRIMARY KEY (`category_id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 107 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; -- ---------------------------- -- author 13 -- qq交流群 796794009 -- email 2449207463@qq.com -- link https://github.com/newbee-ltd -- Records of tb_newbee_mall_goods_category -- ---------------------------- INSERT INTO `tb_newbee_mall_goods_category` VALUES (15, 1, 0, '家电 数码 手机', 100, 0, '2019-09-11 18:45:40', 0, '2019-09-11 18:45:40', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (16, 1, 0, '女装 男装 穿搭', 99, 0, '2019-09-11 18:46:07', 0, '2019-09-11 18:46:07', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (17, 2, 15, '家电', 10, 0, '2019-09-11 18:46:32', 0, '2019-09-11 18:46:32', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (18, 2, 15, '数码', 9, 0, '2019-09-11 18:46:43', 0, '2019-09-11 18:46:43', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (19, 2, 15, '手机', 8, 0, '2019-09-11 18:46:52', 0, '2019-09-11 18:46:52', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (20, 3, 17, '生活电器', 0, 0, '2019-09-11 18:47:38', 0, '2019-09-11 18:47:38', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (21, 3, 17, '厨房电器', 0, 0, '2019-09-11 18:47:49', 0, '2019-09-11 18:47:49', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (22, 3, 17, '扫地机器人', 0, 0, '2019-09-11 18:47:58', 0, '2019-09-11 18:47:58', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (23, 3, 17, '吸尘器', 0, 0, '2019-09-11 18:48:06', 0, '2019-09-11 18:48:06', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (24, 3, 17, '取暖器', 0, 0, '2019-09-11 18:48:12', 0, '2019-09-11 18:48:12', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (25, 3, 17, '豆浆机', 0, 0, '2019-09-11 18:48:26', 0, '2019-09-11 18:48:26', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (26, 3, 17, '暖风机', 0, 0, '2019-09-11 18:48:40', 0, '2019-09-11 18:48:40', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (27, 3, 17, '加湿器', 0, 0, '2019-09-11 18:48:50', 0, '2019-09-11 18:48:50', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (28, 3, 17, '蓝牙音箱', 0, 0, '2019-09-11 18:48:57', 0, '2019-09-11 18:48:57', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (29, 3, 17, '烤箱', 0, 0, '2019-09-11 18:49:09', 0, '2019-09-11 18:49:09', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (30, 3, 17, '卷发器', 0, 0, '2019-09-11 18:49:19', 0, '2019-09-11 18:49:19', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (31, 3, 17, '空气净化器', 0, 0, '2019-09-11 18:49:30', 0, '2019-09-11 18:49:30', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (32, 3, 18, '游戏主机', 0, 0, '2019-09-11 18:49:50', 0, '2019-09-11 18:49:50', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (33, 3, 18, '数码精选', 0, 0, '2019-09-11 18:49:55', 0, '2019-09-11 18:49:55', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (34, 3, 18, '平板电脑', 0, 0, '2019-09-11 18:50:08', 0, '2019-09-11 18:50:08', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (35, 3, 18, '苹果 Apple', 0, 0, '2019-09-11 18:50:24', 0, '2019-09-11 18:50:24', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (36, 3, 18, '电脑主机', 0, 0, '2019-09-11 18:50:36', 0, '2019-09-11 18:50:36', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (37, 3, 18, '数码相机', 0, 0, '2019-09-11 18:50:57', 0, '2019-09-11 18:50:57', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (38, 3, 18, '电玩动漫', 0, 0, '2019-09-11 18:52:15', 0, '2019-09-11 18:52:15', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (39, 3, 18, '单反相机', 0, 0, '2019-09-11 18:52:26', 0, '2019-09-11 18:52:26', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (40, 3, 18, '键盘鼠标', 0, 0, '2019-09-11 18:52:46', 0, '2019-09-11 18:52:46', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (41, 3, 18, '无人机', 0, 0, '2019-09-11 18:53:01', 0, '2019-09-11 18:53:01', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (42, 3, 18, '二手电脑', 0, 0, '2019-09-11 18:53:08', 0, '2019-09-11 18:53:08', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (43, 3, 18, '二手手机', 0, 0, '2019-09-11 18:53:14', 0, '2019-09-11 18:53:14', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (44, 3, 19, 'iPhone 11', 89, 0, '2019-09-11 18:53:49', 0, '2019-09-11 18:54:38', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (45, 3, 19, '荣耀手机', 99, 0, '2019-09-11 18:53:59', 0, '2019-09-18 13:40:59', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (46, 3, 19, '华为手机', 98, 0, '2019-09-11 18:54:20', 0, '2019-09-18 13:40:51', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (47, 3, 19, 'iPhone', 88, 0, '2019-09-11 18:54:49', 0, '2019-09-18 13:40:32', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (48, 3, 19, '华为 Mate 20', 79, 0, '2019-09-11 18:55:03', 0, '2019-09-11 18:55:13', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (49, 3, 19, '华为 P30', 97, 0, '2019-09-11 18:55:22', 0, '2019-09-11 18:55:22', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (50, 3, 19, '华为 P30 Pro', 0, 1, '2019-09-11 18:55:32', 0, '2019-09-11 18:55:32', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (51, 3, 19, '小米手机', 0, 0, '2019-09-11 18:55:52', 0, '2019-09-11 18:55:52', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (52, 3, 19, '红米', 0, 0, '2019-09-11 18:55:58', 0, '2019-09-11 18:55:58', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (53, 3, 19, 'OPPO', 0, 0, '2019-09-11 18:56:06', 0, '2019-09-11 18:56:06', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (54, 3, 19, '一加', 0, 0, '2019-09-11 18:56:12', 0, '2019-09-11 18:56:12', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (55, 3, 19, '小米 MIX', 0, 0, '2019-09-11 18:56:37', 0, '2019-09-11 18:56:37', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (56, 3, 19, 'Reno', 0, 0, '2019-09-11 18:56:49', 0, '2019-09-11 18:56:49', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (57, 3, 19, 'vivo', 0, 0, '2019-09-11 18:57:01', 0, '2019-09-11 18:57:01', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (58, 3, 19, '手机以旧换新', 0, 0, '2019-09-11 18:57:09', 0, '2019-09-11 18:57:09', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (59, 1, 0, '运动 户外 乐器', 97, 0, '2019-09-12 00:08:46', 0, '2019-09-12 00:08:46', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (60, 1, 0, '游戏 动漫 影视', 96, 0, '2019-09-12 00:09:00', 0, '2019-09-12 00:09:00', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (61, 1, 0, '家具 家饰 家纺', 98, 0, '2019-09-12 00:09:27', 0, '2019-09-12 00:09:27', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (62, 1, 0, '美妆 清洁 宠物', 94, 0, '2019-09-12 00:09:51', 0, '2019-09-17 18:22:34', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (63, 1, 0, '工具 装修 建材', 93, 0, '2019-09-12 00:10:07', 0, '2019-09-12 00:10:07', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (64, 1, 0, '珠宝 金饰 眼镜', 92, 0, '2019-09-12 00:10:35', 0, '2019-09-12 00:16:30', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (65, 1, 0, '玩具 孕产 用品', 0, 0, '2019-09-12 00:11:17', 0, '2019-09-12 00:11:17', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (66, 1, 0, '鞋靴 箱包 配件', 91, 0, '2019-09-12 00:11:30', 0, '2019-09-12 00:11:30', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (67, 2, 16, '女装', 10, 0, '2019-09-12 00:15:19', 0, '2019-09-12 00:15:19', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (68, 2, 16, '男装', 9, 0, '2019-09-12 00:15:28', 0, '2019-09-12 00:15:28', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (69, 2, 16, '穿搭', 8, 0, '2019-09-12 00:15:35', 0, '2019-09-12 00:15:35', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (70, 2, 61, '家具', 10, 0, '2019-09-12 00:20:22', 0, '2019-09-12 00:20:22', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (71, 2, 61, '家饰', 9, 0, '2019-09-12 00:20:29', 0, '2019-09-12 00:20:29', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (72, 2, 61, '家纺', 8, 0, '2019-09-12 00:20:35', 0, '2019-09-12 00:20:35', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (73, 2, 59, '运动', 10, 0, '2019-09-12 00:20:49', 0, '2019-09-12 00:20:49', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (74, 2, 59, '户外', 9, 0, '2019-09-12 00:20:58', 0, '2019-09-12 00:20:58', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (75, 2, 59, '乐器', 8, 0, '2019-09-12 00:21:05', 0, '2019-09-12 00:21:05', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (76, 3, 67, '外套', 10, 0, '2019-09-12 00:21:55', 0, '2019-09-12 00:21:55', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (77, 3, 70, '沙发', 10, 0, '2019-09-12 00:22:21', 0, '2019-09-12 00:22:21', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (78, 3, 73, '跑鞋', 10, 0, '2019-09-12 00:22:42', 0, '2019-09-12 00:22:42', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (79, 2, 60, '游戏', 10, 0, '2019-09-12 00:23:13', 0, '2019-09-12 00:23:13', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (80, 2, 60, '动漫', 9, 0, '2019-09-12 00:23:21', 0, '2019-09-12 00:23:21', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (81, 2, 60, '影视', 8, 0, '2019-09-12 00:23:27', 0, '2019-09-12 00:23:27', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (82, 3, 79, 'LOL', 10, 0, '2019-09-12 00:23:44', 0, '2019-09-12 00:23:44', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (83, 2, 62, '美妆', 10, 0, '2019-09-12 00:23:58', 0, '2019-09-17 18:22:44', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (84, 2, 62, '宠物', 9, 0, '2019-09-12 00:24:07', 0, '2019-09-12 00:24:07', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (85, 2, 62, '清洁', 8, 0, '2019-09-12 00:24:15', 0, '2019-09-17 18:22:51', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (86, 3, 83, '口红', 10, 0, '2019-09-12 00:24:38', 0, '2019-09-17 18:23:08', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (87, 2, 63, '工具', 10, 0, '2019-09-12 00:24:56', 0, '2019-09-12 00:24:56', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (88, 2, 63, '装修', 9, 0, '2019-09-12 00:25:05', 0, '2019-09-12 00:25:05', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (89, 2, 63, '建材', 8, 0, '2019-09-12 00:25:12', 0, '2019-09-12 00:25:12', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (90, 3, 87, '转换器', 10, 0, '2019-09-12 00:25:45', 0, '2019-09-12 00:25:45', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (91, 2, 64, '珠宝', 10, 0, '2019-09-12 00:26:10', 0, '2019-09-12 00:26:10', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (92, 2, 64, '金饰', 9, 0, '2019-09-12 00:26:18', 0, '2019-09-12 00:26:18', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (93, 2, 64, '眼镜', 8, 0, '2019-09-12 00:26:25', 0, '2019-09-12 00:26:25', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (94, 3, 91, '钻石', 10, 0, '2019-09-12 00:26:40', 0, '2019-09-12 00:26:40', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (95, 2, 66, '鞋靴', 10, 0, '2019-09-12 00:27:09', 0, '2019-09-12 00:27:09', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (96, 2, 66, '箱包', 9, 0, '2019-09-12 00:27:17', 0, '2019-09-12 00:27:17', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (97, 2, 66, '配件', 8, 0, '2019-09-12 00:27:23', 0, '2019-09-12 00:27:23', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (98, 3, 95, '休闲鞋', 10, 0, '2019-09-12 00:27:48', 0, '2019-09-12 00:27:48', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (99, 3, 83, '气垫', 0, 0, '2019-09-17 18:24:23', 0, '2019-09-17 18:24:23', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (100, 3, 83, '美白', 0, 0, '2019-09-17 18:24:36', 0, '2019-09-17 18:24:36', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (101, 3, 83, '隔离霜', 0, 0, '2019-09-17 18:27:04', 0, '2019-09-17 18:27:04', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (102, 3, 83, '粉底', 0, 0, '2019-09-17 18:27:19', 0, '2019-09-17 18:27:19', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (103, 3, 83, '腮红', 0, 0, '2019-09-17 18:27:24', 0, '2019-09-17 18:27:24', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (104, 3, 83, '睫毛膏', 0, 0, '2019-09-17 18:27:47', 0, '2019-09-17 18:27:47', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (105, 3, 83, '香水', 0, 0, '2019-09-17 18:28:16', 0, '2019-09-17 18:28:16', 0); INSERT INTO `tb_newbee_mall_goods_category` VALUES (106, 3, 83, '面膜', 0, 0, '2019-09-17 18:28:21', 0, '2019-09-17 18:28:21', 0); -- ---------------------------- -- author 13 -- qq交流群 796794009 -- email 2449207463@qq.com -- link https://github.com/newbee-ltd -- Table structure for tb_newbee_mall_goods_info -- ---------------------------- DROP TABLE IF EXISTS `tb_newbee_mall_goods_info`; CREATE TABLE `tb_newbee_mall_goods_info` ( `goods_id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '商品表主键id', `goods_name` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '商品名', `goods_intro` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '商品简介', `goods_category_id` bigint(20) NOT NULL DEFAULT 0 COMMENT '关联分类id', `goods_cover_img` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '/admin/dist/img/no-img.png' COMMENT '商品主图', `goods_carousel` varchar(500) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '/admin/dist/img/no-img.png' COMMENT '商品轮播图', `goods_detail_content` text CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '商品详情', `original_price` int(11) NOT NULL DEFAULT 1 COMMENT '商品价格', `selling_price` int(11) NOT NULL DEFAULT 1 COMMENT '商品实际售价', `stock_num` int(11) NOT NULL DEFAULT 0 COMMENT '商品库存数量', `tag` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '商品标签', `goods_sell_status` tinyint(4) NOT NULL DEFAULT 0 COMMENT '商品上架状态 0-下架 1-上架', `create_user` int(11) NOT NULL DEFAULT 0 COMMENT '添加者主键id', `create_time` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '商品添加时间', `update_user` int(11) NOT NULL DEFAULT 0 COMMENT '修改者主键id', `update_time` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '商品修改时间', PRIMARY KEY (`goods_id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 10896 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; -- ---------------------------- -- author 13 -- qq交流群 796794009 -- email 2449207463@qq.com -- link https://github.com/newbee-ltd -- Records of tb_newbee_mall_goods_info -- ---------------------------- INSERT INTO `tb_newbee_mall_goods_info` (`goods_id`, `goods_name`, `goods_intro`, `goods_category_id`, `goods_cover_img`, `goods_carousel`, `goods_detail_content`, `original_price`, `selling_price`, `stock_num`, `tag`, `goods_sell_status`, `create_user`, `create_time`, `update_user`, `update_time`) VALUES (10003,'无印良品 MUJI 基础润肤化妆水','滋润型 400ml',0,'/goods-img/87446ec4-e534-4b49-9f7d-9bea34665284.jpg','/goods-img/87446ec4-e534-4b49-9f7d-9bea34665284.jpg','

商品介绍加载中...

',100,100,1000,'',1,0,'2019-09-18 13:18:47',0,'2020-10-13 10:41:59'), (10004,'无印良品 MUJI 柔和洁面泡沫','120g',0,'/goods-img/45854bdd-2ca5-423c-a609-3d336d9322b4.jpg','/goods-img/45854bdd-2ca5-423c-a609-3d336d9322b4.jpg','

商品介绍加载中...

',45,45,999,'',0,0,'2019-09-18 13:18:47',0,'2020-10-13 10:41:59'), (10005,'无印良品 MUJI 基础润肤乳液','高保湿型 200ml',0,'/goods-img/7614ce78-0ebc-4275-a7cc-d16ad5f5f6ed.jpg','/goods-img/7614ce78-0ebc-4275-a7cc-d16ad5f5f6ed.jpg','

商品介绍加载中...

',83,83,998,'',0,0,'2019-09-18 13:18:47',0,'2020-10-13 10:41:59'), (10006,'无印良品 MUJI 基础润肤乳液','滋润型 400ml',0,'/goods-img/ef75879d-3d3e-4bab-888d-1e4036491e11.jpg','/goods-img/ef75879d-3d3e-4bab-888d-1e4036491e11.jpg','

商品介绍加载中...

',100,100,1000,'',0,0,'2019-09-18 13:18:47',0,'2020-10-13 10:41:59'), (10007,'无印良品 MUJI 基础润肤化妆水','高保湿型 400ml',0,'/goods-img/558422d1-640e-442d-a073-2b2bdd95c4ed.jpg','/goods-img/558422d1-640e-442d-a073-2b2bdd95c4ed.jpg','

商品介绍加载中...

',127,127,1000,'',0,0,'2019-09-18 13:18:47',0,'2020-10-13 10:41:59'), (10008,'无印良品 MUJI 基础润肤化妆水','清爽型 200ml',0,'/goods-img/89660409-78b7-4d47-ae12-f94b3ce9664b.png','/goods-img/89660409-78b7-4d47-ae12-f94b3ce9664b.png','

商品介绍加载中...

',70,70,1000,'',0,0,'2019-09-18 13:18:47',0,'2020-10-13 10:41:59'), (10009,'无印良品 MUJI 男式','无侧缝法兰绒 睡衣 海军蓝 L',0,'/goods-img/f172c500-21d0-42e3-95ce-aa9b84a2ef49.jpg','/goods-img/f172c500-21d0-42e3-95ce-aa9b84a2ef49.jpg','

商品介绍加载中...

',398,199,1000,'',0,0,'2019-09-18 13:18:47',0,'2020-10-13 10:41:59'), (10010,'无印良品 MUJI 基础润肤洁面泡沫','200ml',0,'/goods-img/f87bdee1-ed48-4b49-b701-cc44f26a2699.jpg','/goods-img/f87bdee1-ed48-4b49-b701-cc44f26a2699.jpg','

商品介绍加载中...

',83,83,1000,'',0,0,'2019-09-18 13:18:47',0,'2020-10-13 10:41:59'), (10011,'无印良品 MUJI 平衡高保湿化妆水','新蜂精选',0,'/goods-img/16230038-bf86-4d4e-a11f-954b9ee4bab2.jpg','/goods-img/16230038-bf86-4d4e-a11f-954b9ee4bab2.jpg','

商品介绍加载中...

',130,65,1000,'',0,0,'2019-09-18 13:18:47',0,'2020-10-13 10:41:59'), (10012,'无印良品 MUJI 凝胶墨水圆珠笔','蓝黑色',0,'/goods-img/a952ecce-32e7-474e-9c1b-943962e0a580.jpg','/goods-img/a952ecce-32e7-474e-9c1b-943962e0a580.jpg','

商品介绍加载中...

',8,5,1000,'',0,0,'2019-09-18 13:18:47',0,'2020-10-13 10:41:59'), (10013,'无印良品 MUJI 平衡保湿乳霜','50g',0,'/goods-img/904c8aa1-0257-49e8-ad89-f48d2462db21.jpg','/goods-img/904c8aa1-0257-49e8-ad89-f48d2462db21.jpg','

商品介绍加载中...

',130,65,1000,'',0,0,'2019-09-18 13:18:47',0,'2020-10-13 10:41:59'), (10014,'无印良品 MUJI 基础润肤乳液','清爽型 200ml',0,'/goods-img/d66b6e0e-48d4-4503-8dd6-43b3c71f52a4.png','/goods-img/d66b6e0e-48d4-4503-8dd6-43b3c71f52a4.png','

商品介绍加载中...

',70,70,1000,'',0,0,'2019-09-18 13:18:47',0,'2020-10-13 10:41:59'), (10015,'无印良品 MUJI 平衡洁面泡沫','100g',0,'/goods-img/d0d8f6d1-1f2d-49f8-9099-0cdd94833581.jpg','/goods-img/d0d8f6d1-1f2d-49f8-9099-0cdd94833581.jpg','

商品介绍加载中...

',85,42,1000,'',0,0,'2019-09-18 13:18:47',0,'2020-10-13 10:41:59'), (10016,'无印良品 MUJI 基础润肤乳液','滋润型 200ml',0,'/goods-img/e553f566-5dc4-4648-be58-fd7112a47b10.jpg','/goods-img/e553f566-5dc4-4648-be58-fd7112a47b10.jpg','

商品介绍加载中...

',61,61,1000,'',0,0,'2019-09-18 13:18:47',0,'2020-10-13 10:41:59'), (10017,'无印良品 MUJI 便携式香薰机','新蜂精选',0,'/goods-img/a9c0d929-6f0b-4bc7-819c-e5015f447a9e.jpg','/goods-img/a9c0d929-6f0b-4bc7-819c-e5015f447a9e.jpg','

商品介绍加载中...

',200,200,1000,'',0,0,'2019-09-18 13:18:47',0,'2020-10-13 10:41:59'), (10018,'无印良品 MUJI 女式','粗棉线条纹长袖T恤 白色*横条 L',0,'/goods-img/38d5f694-2236-415d-80c8-4a1695e92d4e.jpg','/goods-img/38d5f694-2236-415d-80c8-4a1695e92d4e.jpg','

商品介绍加载中...

',198,70,1000,'',0,0,'2019-09-18 13:18:47',0,'2020-10-13 10:41:59'), (10019,'无印良品(MUJI) 聚丙烯化妆盒 1/2','半透明约150x220x86mm',0,'/goods-img/f6832ed7-cb01-48ab-987f-cd437b21be80.jpg','/goods-img/f6832ed7-cb01-48ab-987f-cd437b21be80.jpg','

商品介绍加载中...

',30,30,1000,'',1,0,'2019-09-18 13:18:47',0,'2020-10-13 17:05:01'), (10020,'无印良品 MUJI 聚丙烯','笔盒 大/约184*64*25㎜',0,'/goods-img/6c7f7a0d-4d73-406e-adcc-6f666ce4e2c9.jpg','/goods-img/6c7f7a0d-4d73-406e-adcc-6f666ce4e2c9.jpg','

商品介绍加载中...

',18,18,1000,'',0,0,'2019-09-18 13:18:47',0,'2020-10-13 10:41:59'), (10021,'无印良品(MUJI) 无针订书机 其他','新蜂精选',0,'/goods-img/cf19de8b-e94e-4513-aecd-a0b5c976b738.jpg','/goods-img/cf19de8b-e94e-4513-aecd-a0b5c976b738.jpg','

商品介绍加载中...

',52,52,1000,'',0,0,'2019-09-18 13:18:47',0,'2020-10-13 10:41:59'), (10022,'无印良品 MUJI 塑料橡皮','黑色 小',0,'/goods-img/d4f3299d-d526-4a81-ae9f-3b53e735075e.jpg','/goods-img/d4f3299d-d526-4a81-ae9f-3b53e735075e.jpg','

商品介绍加载中...

',4,4,1000,'',0,0,'2019-09-18 13:18:47',0,'2020-10-13 10:41:59'), (10023,'无印良品 MUJI 大容量基础乳液/高保湿型','400ml',0,'/goods-img/ea92b50a-67ba-4279-a71a-4e52e6a3219c.jpg','/goods-img/ea92b50a-67ba-4279-a71a-4e52e6a3219c.jpg','

商品介绍加载中...

',140,140,1000,'',0,0,'2019-09-18 13:18:47',0,'2020-10-13 10:41:59'), (10024,'无印良品 MUJI 基础润肤化妆水','滋润型 400ml',0,'/goods-img/beb26b1b-7a73-48c2-a9f7-727ad92401f6.jpg','/goods-img/beb26b1b-7a73-48c2-a9f7-727ad92401f6.jpg','

商品介绍加载中...

',100,100,1000,'',0,0,'2019-09-18 13:18:52',0,'2020-10-13 10:41:59'), (10025,'无印良品 MUJI 柔和洁面泡沫','120g',0,'/goods-img/bf1dc4d1-acc2-40c8-8091-1c6f35988643.jpg','/goods-img/bf1dc4d1-acc2-40c8-8091-1c6f35988643.jpg','

商品介绍加载中...

',45,45,1000,'',0,0,'2019-09-18 13:18:52',0,'2020-10-13 10:41:59'), (10026,'无印良品 MUJI 基础润肤乳液','高保湿型 200ml',0,'/goods-img/4059caa9-e0b3-4ac3-a494-b9e4c47e0185.jpg','/goods-img/4059caa9-e0b3-4ac3-a494-b9e4c47e0185.jpg','

商品介绍加载中...

',83,83,1000,'',0,0,'2019-09-18 13:18:52',0,'2020-10-13 10:41:59'), (10027,'无印良品 MUJI 基础润肤乳液','滋润型 400ml',0,'/goods-img/a4a4c981-da0f-4228-bcc7-97d970dc619c.jpg','/goods-img/a4a4c981-da0f-4228-bcc7-97d970dc619c.jpg','

商品介绍加载中...

',100,100,1000,'',0,0,'2019-09-18 13:18:52',0,'2020-10-13 10:41:59'), (10028,'无印良品 MUJI 基础润肤化妆水','高保湿型 400ml',0,'/goods-img/98b5c5b5-cc75-4dfb-8ec4-0a7f42af6183.jpg','/goods-img/98b5c5b5-cc75-4dfb-8ec4-0a7f42af6183.jpg','

商品介绍加载中...

',127,127,1000,'',0,0,'2019-09-18 13:18:52',0,'2020-10-13 10:41:59'), (10029,'无印良品 MUJI 基础润肤化妆水','清爽型 200ml',0,'/goods-img/71d1f469-b77b-473a-a31a-78fc97859b3a.png','/goods-img/71d1f469-b77b-473a-a31a-78fc97859b3a.png','

商品介绍加载中...

',70,70,1000,'',0,0,'2019-09-18 13:18:52',0,'2020-10-13 10:41:59'), (10030,'无印良品 MUJI 男式','无侧缝法兰绒 睡衣 海军蓝 L',0,'/goods-img/68bfbfd9-bc28-429a-ab2c-7fa62205ed7e.jpg','/goods-img/68bfbfd9-bc28-429a-ab2c-7fa62205ed7e.jpg','

商品介绍加载中...

',398,199,1000,'',0,0,'2019-09-18 13:18:52',0,'2020-10-13 10:41:59'), (10031,'无印良品 MUJI 基础润肤洁面泡沫','200ml',0,'/goods-img/679eb5a8-7689-4620-b072-63daeb8eb73a.jpg','/goods-img/679eb5a8-7689-4620-b072-63daeb8eb73a.jpg','

商品介绍加载中...

',83,83,1000,'',0,0,'2019-09-18 13:18:52',0,'2020-10-13 10:41:59'), (10032,'无印良品 MUJI 平衡高保湿化妆水','新蜂精选',0,'/goods-img/eb13afc6-8898-4a50-9f93-06dd2593c313.jpg','/goods-img/eb13afc6-8898-4a50-9f93-06dd2593c313.jpg','

商品介绍加载中...

',130,65,1000,'',0,0,'2019-09-18 13:18:52',0,'2020-10-13 10:41:59'), (10033,'无印良品 MUJI 凝胶墨水圆珠笔','蓝黑色',0,'/goods-img/85a893fe-c971-4f0b-aa0f-4c24b65b1c75.jpg','/goods-img/85a893fe-c971-4f0b-aa0f-4c24b65b1c75.jpg','

商品介绍加载中...

',8,5,1000,'',0,0,'2019-09-18 13:18:52',0,'2020-10-13 10:41:59'), (10034,'无印良品 MUJI 平衡保湿乳霜','50g',0,'/goods-img/65aed381-cde0-44ed-b345-5ebf1d74a13b.jpg','/goods-img/65aed381-cde0-44ed-b345-5ebf1d74a13b.jpg','

商品介绍加载中...

',130,65,1000,'',0,0,'2019-09-18 13:18:52',0,'2020-10-13 10:41:59'), (10035,'无印良品 MUJI 基础润肤乳液','清爽型 200ml',0,'/goods-img/1e09e1ed-435b-4f08-84d0-d88308a315ee.png','/goods-img/1e09e1ed-435b-4f08-84d0-d88308a315ee.png','

商品介绍加载中...

',70,70,1000,'',0,0,'2019-09-18 13:18:52',0,'2020-10-13 10:41:59'), (10036,'无印良品 MUJI 平衡洁面泡沫','100g',0,'/goods-img/dbc2ea2a-ee03-4366-a35e-6ebe66d02399.jpg','/goods-img/dbc2ea2a-ee03-4366-a35e-6ebe66d02399.jpg','

商品介绍加载中...

',85,42,1000,'',0,0,'2019-09-18 13:18:52',0,'2020-10-13 10:41:59'), (10037,'无印良品 MUJI 基础润肤乳液','滋润型 200ml',0,'/goods-img/9389914c-2860-4a75-b603-53ed5a4e0509.jpg','/goods-img/9389914c-2860-4a75-b603-53ed5a4e0509.jpg','

商品介绍加载中...

',61,61,1000,'',0,0,'2019-09-18 13:18:52',0,'2020-10-13 10:41:59'), (10038,'无印良品 MUJI 便携式香薰机','新蜂精选',0,'/goods-img/6ab010e2-5f1e-4512-bd22-4c2550915d4c.jpg','/goods-img/6ab010e2-5f1e-4512-bd22-4c2550915d4c.jpg','

商品介绍加载中...

',200,200,1000,'',0,0,'2019-09-18 13:18:52',0,'2020-10-13 10:41:59'), (10039,'无印良品 MUJI 女式','粗棉线条纹长袖T恤 白色*横条 L',0,'/goods-img/fab00903-7ff6-40ee-a9bc-3fbc2f0f0ffc.jpg','/goods-img/fab00903-7ff6-40ee-a9bc-3fbc2f0f0ffc.jpg','

商品介绍加载中...

',198,70,1000,'',0,0,'2019-09-18 13:18:52',0,'2020-10-13 10:41:59'), (10040,'无印良品(MUJI) 聚丙烯化妆盒 1/2','半透明约150x220x86mm',0,'/goods-img/ab725751-adb8-452a-86dd-cb3d21da794e.jpg','/goods-img/ab725751-adb8-452a-86dd-cb3d21da794e.jpg','

商品介绍加载中...

',30,30,1000,'',0,0,'2019-09-18 13:18:52',0,'2020-10-13 10:41:59'), (10041,'无印良品 MUJI 聚丙烯','笔盒 大/约184*64*25㎜',0,'/goods-img/9f623290-928c-498f-89e6-171372b394f2.jpg','/goods-img/9f623290-928c-498f-89e6-171372b394f2.jpg','

商品介绍加载中...

',18,18,1000,'',0,0,'2019-09-18 13:18:52',0,'2020-10-13 10:41:59'), (10042,'无印良品(MUJI) 无针订书机 其他','新蜂精选',0,'/goods-img/a7221688-3c37-4ac0-b07e-d8bde1525d1e.jpg','/goods-img/a7221688-3c37-4ac0-b07e-d8bde1525d1e.jpg','

商品介绍加载中...

',52,52,1000,'',0,0,'2019-09-18 13:18:52',0,'2020-10-13 10:41:59'), (10043,'无印良品 MUJI 塑料橡皮','黑色 小',0,'/goods-img/75e26af4-8f15-43f2-9407-50d641f82acb.jpg','/goods-img/75e26af4-8f15-43f2-9407-50d641f82acb.jpg','

商品介绍加载中...

',4,4,1000,'',0,0,'2019-09-18 13:18:52',0,'2020-10-13 10:41:59'), (10044,'无印良品 MUJI 大容量基础乳液/高保湿型','400ml',0,'/goods-img/69d55773-1b43-497b-af18-90f2cec7c93a.jpg','/goods-img/69d55773-1b43-497b-af18-90f2cec7c93a.jpg','

商品介绍加载中...

',140,140,1000,'',0,0,'2019-09-18 13:18:52',0,'2020-10-13 10:41:59'), (10045,'无印良品 MUJI 毛笔','黑色',0,'/goods-img/419ddb3c-1793-49c1-8953-77409a5d5bce.jpg','/goods-img/419ddb3c-1793-49c1-8953-77409a5d5bce.jpg','

商品介绍加载中...

',20,20,1000,'',0,0,'2019-09-18 13:19:02',0,'2020-10-13 10:41:59'), (10046,'无印良品 MUJI 塑料橡皮','白色 小',0,'/goods-img/e53cc7af-f81c-4752-aec8-007e807b2fc1.jpg','/goods-img/e53cc7af-f81c-4752-aec8-007e807b2fc1.jpg','

商品介绍加载中...

',4,4,1000,'',0,0,'2019-09-18 13:19:02',0,'2020-10-13 10:41:59'), (10047,'无印良品 MUJI 男式','无侧缝法兰绒 睡衣 深海军蓝X格子 L',0,'/goods-img/481e8994-20cb-4f6c-8b77-4eb8509eb3b9.jpg','/goods-img/481e8994-20cb-4f6c-8b77-4eb8509eb3b9.jpg','

商品介绍加载中...

',398,199,1000,'',0,0,'2019-09-18 13:19:02',0,'2020-10-13 10:41:59'), (10048,'无印良品 MUJI 荧光笔','蓝色',0,'/goods-img/012ebf2d-8c96-4641-8782-eab01c85d98f.jpg','/goods-img/012ebf2d-8c96-4641-8782-eab01c85d98f.jpg','

商品介绍加载中...

',10,10,1000,'',0,0,'2019-09-18 13:19:02',0,'2020-10-13 10:41:59'), (10049,'无印良品(MUJI) 钢制指甲刀 小','新蜂精选',0,'/goods-img/2c150720-4b3a-4d9e-9ce6-77eb4998e1f1.jpg','/goods-img/2c150720-4b3a-4d9e-9ce6-77eb4998e1f1.jpg','

商品介绍加载中...

',42,42,1000,'',0,0,'2019-09-18 13:19:02',0,'2020-10-13 10:41:59'), (10050,'无印良品 MUJI 长条诗笺型笔记表格','白色 40枚 14行',0,'/goods-img/e7d2ea3f-6703-4fcc-bbb4-ad9ef43a0ae2.jpg','/goods-img/e7d2ea3f-6703-4fcc-bbb4-ad9ef43a0ae2.jpg','

商品介绍加载中...

',10,10,1000,'',0,0,'2019-09-18 13:19:02',0,'2020-10-13 10:41:59'), (10051,'无印良品 MUJI PET喷雾小分装瓶100ml','新蜂精选',0,'/goods-img/0ec8c4a7-aedc-464d-9e23-d3e4acafdc73.jpg','/goods-img/0ec8c4a7-aedc-464d-9e23-d3e4acafdc73.jpg','

商品介绍加载中...

',30,30,1000,'',0,0,'2019-09-18 13:19:02',0,'2020-10-13 10:41:59'), (10052,'无印良品 MUJI 塑料橡皮','黑色 大',0,'/goods-img/ce8ff43c-e8b4-4c52-9de1-c983c97068f6.jpg','/goods-img/ce8ff43c-e8b4-4c52-9de1-c983c97068f6.jpg','

商品介绍加载中...

',7,7,1000,'',0,0,'2019-09-18 13:19:02',0,'2020-10-13 10:41:59'), (10053,'无印良品 MUJI 荧光笔','黄色',0,'/goods-img/79b38a89-b02a-4fd1-80c4-5cb426028536.jpg','/goods-img/79b38a89-b02a-4fd1-80c4-5cb426028536.jpg','

商品介绍加载中...

',10,10,1000,'',0,0,'2019-09-18 13:19:02',0,'2020-10-13 10:41:59'), (10054,'无印良品 MUJI 遮瑕膏','棒状 自然色',0,'/goods-img/ffa69c8e-f57f-4ef4-a2a0-3695d538d6c5.jpg','/goods-img/ffa69c8e-f57f-4ef4-a2a0-3695d538d6c5.jpg','

商品介绍加载中...

',42,42,1000,'',0,0,'2019-09-18 13:19:02',0,'2020-10-13 10:41:59'), (10055,'无印良品 MUJI 马桶刷/附盒子','白色',0,'/goods-img/9dd1cdfb-e7f9-4d3c-98df-933e2bc3f9a8.jpg','/goods-img/9dd1cdfb-e7f9-4d3c-98df-933e2bc3f9a8.jpg','

商品介绍加载中...

',70,70,1000,'',0,0,'2019-09-18 13:19:02',0,'2020-10-13 10:41:59'), (10056,'无印良品 MUJI 耐热玻璃_壶_大','透明',0,'/goods-img/0bc4f5ac-d601-421d-8131-81958a195705.jpg','/goods-img/0bc4f5ac-d601-421d-8131-81958a195705.jpg','

商品介绍加载中...

',150,150,1000,'',0,0,'2019-09-18 13:19:02',0,'2020-10-13 10:41:59'), (10057,'无印良品 MUJI 女式','平纹短袖衬衫 藏青色 M',0,'/goods-img/76b6a573-12a0-4c63-b2ae-e7193aff0fc8.jpg','/goods-img/76b6a573-12a0-4c63-b2ae-e7193aff0fc8.jpg','

商品介绍加载中...

',198,59,1000,'',0,0,'2019-09-18 13:19:02',0,'2020-10-13 10:41:59'), (10058,'无印良品 MUJI 基础润肤化妆水','清爽型 50ml',0,'/goods-img/af7f9b21-d782-4bad-8b1a-d86bbc4d224e.png','/goods-img/af7f9b21-d782-4bad-8b1a-d86bbc4d224e.png','

商品介绍加载中...

',28,22,1000,'',0,0,'2019-09-18 13:19:02',0,'2020-10-13 10:41:59'), (10059,'无印良品 MUJI 男式','无侧缝法兰绒 睡衣 炭灰色 M',0,'/goods-img/26e0c424-f22d-4d3d-9bd6-a7958a346ff9.jpg','/goods-img/26e0c424-f22d-4d3d-9bd6-a7958a346ff9.jpg','

商品介绍加载中...

',398,199,1000,'',0,0,'2019-09-18 13:19:02',0,'2020-10-13 10:41:59'), (10060,'无印良品(MUJI) PET分裝瓶','新蜂精选',0,'/goods-img/24bf1630-0339-4c22-ad19-37152c561e71.jpg','/goods-img/24bf1630-0339-4c22-ad19-37152c561e71.jpg','

商品介绍加载中...

',15,15,1000,'',0,0,'2019-09-18 13:19:02',0,'2020-10-13 10:41:59'), (10061,'无印良品 MUJI 女式','无侧缝法兰绒 睡衣 灰色 M',0,'/goods-img/e8e26306-0521-4843-9e07-70ebd2fa6405.jpg','/goods-img/e8e26306-0521-4843-9e07-70ebd2fa6405.jpg','

商品介绍加载中...

',398,199,1000,'',0,0,'2019-09-18 13:19:02',0,'2020-10-13 10:41:59'), (10062,'无印良品(MUJI) PE分裝瓶','新蜂精选',0,'/goods-img/9b3af7c2-57f5-48a7-bea5-603b2d145000.jpg','/goods-img/9b3af7c2-57f5-48a7-bea5-603b2d145000.jpg','

商品介绍加载中...

',10,10,1000,'',0,0,'2019-09-18 13:19:02',0,'2020-10-13 10:41:59'), (10063,'无印良品 MUJI 基础润肤化妆水','滋润型 200ml',0,'/goods-img/7577f3e0-f48b-47a9-96b7-de405a6aaf95.png','/goods-img/7577f3e0-f48b-47a9-96b7-de405a6aaf95.png','

商品介绍加载中...

',70,70,1000,'',0,0,'2019-09-18 13:19:02',0,'2020-10-13 10:41:59'), (10064,'无印良品 MUJI 男式','干爽 凉感珠地网眼编织V领短袖T恤 黑色 L',0,'/goods-img/cce2af31-07ea-4744-8d01-16dd01d68e5b.jpg','/goods-img/cce2af31-07ea-4744-8d01-16dd01d68e5b.jpg','

商品介绍加载中...

',98,29,1000,'',0,0,'2019-09-18 13:19:02',0,'2020-10-13 10:41:59'), (10065,'无印良品(MUJI) 聚丙烯化妆盒 半透明约150x220x169mm','新蜂精选',0,'/goods-img/6dc279ac-fef0-401c-8604-b18dc9a9f7ab.jpg','/goods-img/6dc279ac-fef0-401c-8604-b18dc9a9f7ab.jpg','

商品介绍加载中...

',40,40,1000,'',0,0,'2019-09-18 13:19:02',0,'2020-10-13 10:41:59'), (10066,'无印良品(MUJI) 散粉小 自然色','新蜂精选',0,'/goods-img/94764fac-f4ad-4ee8-8d26-21af0c09ea76.jpg','/goods-img/94764fac-f4ad-4ee8-8d26-21af0c09ea76.jpg','

商品介绍加载中...

',60,60,1000,'',0,0,'2019-09-18 13:19:02',0,'2020-10-13 10:41:59'), (10067,'无印良品 MUJI 毛笔','黑色',0,'/goods-img/9cd07460-8c0b-49e5-9741-5015a3576e8e.jpg','/goods-img/9cd07460-8c0b-49e5-9741-5015a3576e8e.jpg','

商品介绍加载中...

',20,20,1000,'',0,0,'2019-09-18 13:19:07',0,'2020-10-13 10:41:59'), (10068,'无印良品 MUJI 塑料橡皮','白色 小',0,'/goods-img/70529ced-527a-4b46-aafa-874107ff9ea5.jpg','/goods-img/70529ced-527a-4b46-aafa-874107ff9ea5.jpg','

商品介绍加载中...

',4,4,1000,'',0,0,'2019-09-18 13:19:07',0,'2020-10-13 10:41:59'), (10069,'无印良品 MUJI 男式','无侧缝法兰绒 睡衣 深海军蓝X格子 L',0,'/goods-img/174ec60d-7d2b-4043-a7a6-7383c3de1a11.jpg','/goods-img/174ec60d-7d2b-4043-a7a6-7383c3de1a11.jpg','

商品介绍加载中...

',398,199,1000,'',0,0,'2019-09-18 13:19:07',0,'2020-10-13 10:41:59'), (10070,'无印良品 MUJI 荧光笔','蓝色',0,'/goods-img/eef29d44-17f5-41dd-b0ba-c6f63d7bdac3.jpg','/goods-img/eef29d44-17f5-41dd-b0ba-c6f63d7bdac3.jpg','

商品介绍加载中...

',10,10,1000,'',0,0,'2019-09-18 13:19:07',0,'2020-10-13 10:41:59'), (10071,'无印良品(MUJI) 钢制指甲刀 小','新蜂精选',0,'/goods-img/f9964432-a9b7-45c2-ac6d-680130c2d7a7.jpg','/goods-img/f9964432-a9b7-45c2-ac6d-680130c2d7a7.jpg','

商品介绍加载中...

',42,42,1000,'',0,0,'2019-09-18 13:19:07',0,'2020-10-13 10:41:59'), (10072,'无印良品 MUJI 长条诗笺型笔记表格','白色 40枚 14行',0,'/goods-img/da1e4523-adb4-48e4-afa5-313346187690.jpg','/goods-img/da1e4523-adb4-48e4-afa5-313346187690.jpg','

商品介绍加载中...

',10,10,1000,'',0,0,'2019-09-18 13:19:07',0,'2020-10-13 10:41:59'), (10073,'无印良品 MUJI PET喷雾小分装瓶100ml','新蜂精选',0,'/goods-img/7f1eec3d-d8e5-4a18-a1a9-b81876dcaaf5.jpg','/goods-img/7f1eec3d-d8e5-4a18-a1a9-b81876dcaaf5.jpg','

商品介绍加载中...

',30,30,1000,'',0,0,'2019-09-18 13:19:07',0,'2020-10-13 10:41:59'), (10074,'无印良品 MUJI 塑料橡皮','黑色 大',0,'/goods-img/1ca16211-2b80-4006-ab60-e1a3cab4218c.jpg','/goods-img/1ca16211-2b80-4006-ab60-e1a3cab4218c.jpg','

商品介绍加载中...

',7,7,1000,'',0,0,'2019-09-18 13:19:07',0,'2020-10-13 10:41:59'), (10075,'无印良品 MUJI 荧光笔','黄色',0,'/goods-img/56eec806-2af3-4136-a9bf-2333455339e7.jpg','/goods-img/56eec806-2af3-4136-a9bf-2333455339e7.jpg','

商品介绍加载中...

',10,10,1000,'',0,0,'2019-09-18 13:19:07',0,'2020-10-13 10:41:59'), (10076,'无印良品 MUJI 遮瑕膏','棒状 自然色',0,'/goods-img/593b65a7-feae-45aa-837e-47d58bb27474.jpg','/goods-img/593b65a7-feae-45aa-837e-47d58bb27474.jpg','

商品介绍加载中...

',42,42,1000,'',0,0,'2019-09-18 13:19:07',0,'2020-10-13 10:41:59'), (10077,'无印良品 MUJI 马桶刷/附盒子','白色',0,'/goods-img/a9983f71-d818-459d-ad59-bbdd26bb533b.jpg','/goods-img/a9983f71-d818-459d-ad59-bbdd26bb533b.jpg','

商品介绍加载中...

',70,70,1000,'',0,0,'2019-09-18 13:19:07',0,'2020-10-13 10:41:59'), (10078,'无印良品 MUJI 耐热玻璃_壶_大','透明',0,'/goods-img/7f89c29e-d888-4ee0-92af-ca713a7871a4.jpg','/goods-img/7f89c29e-d888-4ee0-92af-ca713a7871a4.jpg','

商品介绍加载中...

',150,150,1000,'',0,0,'2019-09-18 13:19:07',0,'2020-10-13 10:41:59'), (10079,'无印良品 MUJI 女式','平纹短袖衬衫 藏青色 M',0,'/goods-img/0b1e57bf-b4fd-40df-9832-4749d7d69db9.jpg','/goods-img/0b1e57bf-b4fd-40df-9832-4749d7d69db9.jpg','

商品介绍加载中...

',198,59,1000,'',0,0,'2019-09-18 13:19:07',0,'2020-10-13 10:41:59'), (10080,'无印良品 MUJI 基础润肤化妆水','清爽型 50ml',0,'/goods-img/9b4af7cf-235a-4742-bdc3-9e8e656f245c.png','/goods-img/9b4af7cf-235a-4742-bdc3-9e8e656f245c.png','

商品介绍加载中...

',28,22,1000,'',0,0,'2019-09-18 13:19:07',0,'2020-10-13 10:41:59'), (10081,'无印良品 MUJI 男式','无侧缝法兰绒 睡衣 炭灰色 M',0,'/goods-img/8ddfc2de-3da3-4fad-86aa-7c570cb55212.jpg','/goods-img/8ddfc2de-3da3-4fad-86aa-7c570cb55212.jpg','

商品介绍加载中...

',398,199,1000,'',0,0,'2019-09-18 13:19:07',0,'2020-10-13 10:41:59'), (10082,'无印良品(MUJI) PET分裝瓶','新蜂精选',0,'/goods-img/e62d04e9-3ae2-431c-8538-becda89e0e84.jpg','/goods-img/e62d04e9-3ae2-431c-8538-becda89e0e84.jpg','

商品介绍加载中...

',15,15,1000,'',0,0,'2019-09-18 13:19:07',0,'2020-10-13 10:41:59'), (10083,'无印良品 MUJI 女式','无侧缝法兰绒 睡衣 灰色 M',0,'/goods-img/3078143f-1cdd-4f66-951b-2cf08af8c826.jpg','/goods-img/3078143f-1cdd-4f66-951b-2cf08af8c826.jpg','

商品介绍加载中...

',398,199,1000,'',0,0,'2019-09-18 13:19:07',0,'2020-10-13 10:41:59'), (10084,'无印良品(MUJI) PE分裝瓶','新蜂精选',0,'/goods-img/97aa8872-26df-473a-b0d7-f5021776cb52.jpg','/goods-img/97aa8872-26df-473a-b0d7-f5021776cb52.jpg','

商品介绍加载中...

',10,10,1000,'',0,0,'2019-09-18 13:19:07',0,'2020-10-13 10:41:59'), (10085,'无印良品 MUJI 基础润肤化妆水','滋润型 200ml',0,'/goods-img/954da201-0cbb-45d1-9cd1-17ce4d24cfb4.png','/goods-img/954da201-0cbb-45d1-9cd1-17ce4d24cfb4.png','

商品介绍加载中...

',70,70,1000,'',0,0,'2019-09-18 13:19:07',0,'2020-10-13 10:41:59'), (10086,'无印良品 MUJI 男式','干爽 凉感珠地网眼编织V领短袖T恤 黑色 L',0,'/goods-img/b584ea09-7aae-422e-8435-fdc38c948434.jpg','/goods-img/b584ea09-7aae-422e-8435-fdc38c948434.jpg','

商品介绍加载中...

',98,29,1000,'',0,0,'2019-09-18 13:19:07',0,'2020-10-13 10:41:59'), (10087,'无印良品(MUJI) 聚丙烯化妆盒 半透明约150x220x169mm','新蜂精选',0,'/goods-img/a0a45b44-82c9-4a58-a972-304bed0632bb.jpg','/goods-img/a0a45b44-82c9-4a58-a972-304bed0632bb.jpg','

商品介绍加载中...

',40,40,1000,'',0,0,'2019-09-18 13:19:07',0,'2020-10-13 10:41:59'), (10088,'无印良品(MUJI) 散粉小 自然色','新蜂精选',0,'/goods-img/a1b8ff33-ec01-494e-a1db-fb5158f3c168.jpg','/goods-img/a1b8ff33-ec01-494e-a1db-fb5158f3c168.jpg','

商品介绍加载中...

',60,60,1000,'',0,0,'2019-09-18 13:19:07',0,'2020-10-13 10:41:59'), (10089,'无印良品 MUJI 荧光笔','粉红色',0,'/goods-img/c5d6d952-c81b-436a-a345-feb4c5a20a7d.jpg','/goods-img/c5d6d952-c81b-436a-a345-feb4c5a20a7d.jpg','

商品介绍加载中...

',10,10,1000,'',0,0,'2019-09-18 13:19:17',0,'2020-10-13 10:41:59'), (10090,'无印良品(MUJI) PET小分装瓶100ml','新蜂精选',0,'/goods-img/2ffe59f3-559f-4e6f-810d-1b6fa4ac04e1.jpg','/goods-img/2ffe59f3-559f-4e6f-810d-1b6fa4ac04e1.jpg','

商品介绍加载中...

',30,30,1000,'',0,0,'2019-09-18 13:19:17',0,'2020-10-13 10:41:59'), (10091,'无印良品 MUJI 基础润肤洁面乳','150ml',0,'/goods-img/1f24d75a-0468-471a-a608-bd6788f4c1a1.jpg','/goods-img/1f24d75a-0468-471a-a608-bd6788f4c1a1.jpg','

商品介绍加载中...

',74,74,1000,'',0,0,'2019-09-18 13:19:17',0,'2020-10-13 10:41:59'), (10092,'无印良品 MUJI 基础润肤乳霜','其他 50g',0,'/goods-img/86e027b3-8868-4fa5-971b-49e827027e3e.jpg','/goods-img/86e027b3-8868-4fa5-971b-49e827027e3e.jpg','

商品介绍加载中...

',100,100,1000,'',0,0,'2019-09-18 13:19:17',0,'2020-10-13 10:41:59'), (10093,'无印良品 MUJI 基础润肤洁面泡沫(替换装)','180ml',0,'/goods-img/1aea34fa-f45e-4c3c-b73c-da1f92492c95.jpg','/goods-img/1aea34fa-f45e-4c3c-b73c-da1f92492c95.jpg','

商品介绍加载中...

',69,69,1000,'',0,0,'2019-09-18 13:19:17',0,'2020-10-13 10:41:59'), (10094,'无印良品 MUJI 保湿洁面啫喱','100g',0,'/goods-img/838fc0cb-b98f-4dca-bd68-581138b21a30.jpg','/goods-img/838fc0cb-b98f-4dca-bd68-581138b21a30.jpg','

商品介绍加载中...

',100,50,1000,'',0,0,'2019-09-18 13:19:17',0,'2020-10-13 10:41:59'), (10095,'无印良品 MUJI 小型超声波香薰机','其他',0,'/goods-img/30f05c92-a303-4b94-bb5e-22f3c65f3c37.jpg','/goods-img/30f05c92-a303-4b94-bb5e-22f3c65f3c37.jpg','

商品介绍加载中...

',250,250,1000,'',0,0,'2019-09-18 13:19:17',0,'2020-10-13 10:41:59'), (10096,'无印良品 MUJI 修正带','其他',0,'/goods-img/759427b3-b723-4917-b565-c0ae2003bf02.jpg','/goods-img/759427b3-b723-4917-b565-c0ae2003bf02.jpg','

商品介绍加载中...

',25,25,1000,'',0,0,'2019-09-18 13:19:17',0,'2020-10-13 10:41:59'), (10097,'无印良品 MUJI 聚丙烯','笔盒 小/约170*51*20㎜',0,'/goods-img/734f1604-e687-4cd1-8573-bb00e680e94e.jpg','/goods-img/734f1604-e687-4cd1-8573-bb00e680e94e.jpg','

商品介绍加载中...

',12,12,1000,'',0,0,'2019-09-18 13:19:17',0,'2020-10-13 10:41:59'), (10098,'无印良品 MUJI 乳液','50ml',0,'/goods-img/4eed1033-7728-477c-a29d-589bfd3ae3ce.jpg','/goods-img/4eed1033-7728-477c-a29d-589bfd3ae3ce.jpg','

商品介绍加载中...

',55,27,1000,'',0,0,'2019-09-18 13:19:17',0,'2020-10-13 10:41:59'), (10099,'无印良品 MUJI 男式','棉水洗 平纹短袖衬衫 白色 L',0,'/goods-img/d3fa11f3-6cfa-4958-b09c-584a62137b4b.jpg','/goods-img/d3fa11f3-6cfa-4958-b09c-584a62137b4b.jpg','

商品介绍加载中...

',178,89,1000,'',0,0,'2019-09-18 13:19:17',0,'2020-10-13 10:41:59'), (10100,'无印良品 MUJI 香/绿意','12支装/棒状',0,'/goods-img/829f2d09-1589-4f63-8376-d347c3cec620.jpg','/goods-img/829f2d09-1589-4f63-8376-d347c3cec620.jpg','

商品介绍加载中...

',32,32,1000,'',0,0,'2019-09-18 13:19:17',0,'2020-10-13 10:41:59'), (10101,'无印良品 MUJI 润肤乳霜(高保湿型)50g','50g',0,'/goods-img/1c70ddcb-ca69-40ed-a263-30880b2e2cac.jpg','/goods-img/1c70ddcb-ca69-40ed-a263-30880b2e2cac.jpg','

商品介绍加载中...

',159,159,1000,'',0,0,'2019-09-18 13:19:17',0,'2020-10-13 10:41:59'), (10102,'无印良品 MUJI 柔滑笔芯','黑色',0,'/goods-img/1db10d7c-3429-4ef2-ac41-2991af57f442.jpg','/goods-img/1db10d7c-3429-4ef2-ac41-2991af57f442.jpg','

商品介绍加载中...

',19,19,1000,'',0,0,'2019-09-18 13:19:17',0,'2020-10-13 10:41:59'), (10103,'无印良品 MUJI 铝制','挂钩/吸盘式_2个装 大/约宽4.5x高6cm 2个装',0,'/goods-img/bd0b92b4-c8ca-453a-b572-b3447083bddf.png','/goods-img/bd0b92b4-c8ca-453a-b572-b3447083bddf.png','

商品介绍加载中...

',25,25,1000,'',0,0,'2019-09-18 13:19:17',0,'2020-10-13 10:41:59'), (10104,'无印良品 MUJI 修正带','POM材质 替芯',0,'/goods-img/98ce17e1-890e-4eaf-856a-7fce8ffebc4c.jpg','/goods-img/98ce17e1-890e-4eaf-856a-7fce8ffebc4c.jpg','

商品介绍加载中...

',15,15,999,'',0,0,'2019-09-18 13:19:17',0,'2020-10-13 10:41:59'), (10105,'无印良品 MUJI 压克力记录板夹','A4用/220×310mm',0,'/goods-img/64d4e0b7-cd01-47f6-9081-4c2e7625e4f9.jpg','/goods-img/64d4e0b7-cd01-47f6-9081-4c2e7625e4f9.jpg','

商品介绍加载中...

',35,35,1000,'',0,0,'2019-09-18 13:19:17',0,'2020-10-13 10:41:59'), (10106,'无印良品(MUJI) 自然亲肤粉底液 自然透亮色','新蜂精选',0,'/goods-img/09576fcd-ea01-4b1d-bed4-be96b71f2c4e.jpg','/goods-img/09576fcd-ea01-4b1d-bed4-be96b71f2c4e.jpg','

商品介绍加载中...

',75,75,1000,'',0,0,'2019-09-18 13:19:17',0,'2020-10-13 10:41:59'), (10107,'无印良品 MUJI 荧光笔','粉红色',0,'/goods-img/04a8c325-d296-4f0e-ac6d-8cccba4dc90e.jpg','/goods-img/04a8c325-d296-4f0e-ac6d-8cccba4dc90e.jpg','

商品介绍加载中...

',10,10,1000,'',0,0,'2019-09-18 13:19:22',0,'2020-10-13 10:41:59'), (10108,'无印良品(MUJI) PET小分装瓶100ml','新蜂精选',0,'/goods-img/755a34a3-bc3e-4f04-8943-f79860012e78.jpg','/goods-img/755a34a3-bc3e-4f04-8943-f79860012e78.jpg','

商品介绍加载中...

',30,30,1000,'',0,0,'2019-09-18 13:19:22',0,'2020-10-13 10:41:59'), (10109,'无印良品 MUJI 基础润肤洁面乳','150ml',0,'/goods-img/e6a986ed-9b83-4649-9e72-3cf676c1f90e.jpg','/goods-img/e6a986ed-9b83-4649-9e72-3cf676c1f90e.jpg','

商品介绍加载中...

',74,74,1000,'',0,0,'2019-09-18 13:19:22',0,'2020-10-13 10:41:59'), (10110,'无印良品 MUJI 基础润肤乳霜','其他 50g',0,'/goods-img/30036561-a150-4ea7-9106-29bbea278909.jpg','/goods-img/30036561-a150-4ea7-9106-29bbea278909.jpg','

商品介绍加载中...

',100,100,999,'',0,0,'2019-09-18 13:19:22',0,'2020-10-13 10:41:59'), (10111,'无印良品 MUJI 基础润肤洁面泡沫(替换装)','180ml',0,'/goods-img/aa37202c-68eb-4c84-b02c-171b3d11c0e8.jpg','/goods-img/aa37202c-68eb-4c84-b02c-171b3d11c0e8.jpg','

商品介绍加载中...

',69,69,1000,'',0,0,'2019-09-18 13:19:22',0,'2020-10-13 10:41:59'), (10112,'无印良品 MUJI 保湿洁面啫喱','100g',0,'/goods-img/0f724c0f-8888-4b75-8fe1-dc7dd8f2b7bd.jpg','/goods-img/0f724c0f-8888-4b75-8fe1-dc7dd8f2b7bd.jpg','

商品介绍加载中...

',100,50,1000,'',0,0,'2019-09-18 13:19:22',0,'2020-10-13 10:41:59'), (10113,'无印良品 MUJI 小型超声波香薰机','其他',0,'/goods-img/9608b59d-cbca-4b70-9f05-226fde41c51c.jpg','/goods-img/9608b59d-cbca-4b70-9f05-226fde41c51c.jpg','

商品介绍加载中...

',250,250,998,'呼吸品质生活',0,0,'2019-09-18 13:19:22',0,'2020-10-13 10:41:59'), (10114,'无印良品 MUJI 修正带','其他',0,'/goods-img/d91a71e7-aada-4770-91c5-4da21e4b7ed9.jpg','/goods-img/d91a71e7-aada-4770-91c5-4da21e4b7ed9.jpg','

商品介绍加载中...

',25,25,1000,'',0,0,'2019-09-18 13:19:22',0,'2020-10-13 10:41:59'), (10115,'无印良品 MUJI 聚丙烯','笔盒 小/约170*51*20㎜',0,'/goods-img/d543ba0d-18d8-427a-87ea-99968b319440.jpg','/goods-img/d543ba0d-18d8-427a-87ea-99968b319440.jpg','

商品介绍加载中...

',12,12,1000,'',0,0,'2019-09-18 13:19:22',0,'2020-10-13 10:41:59'), (10116,'无印良品 MUJI 乳液','50ml',0,'/goods-img/cd6d91b0-69b2-4415-8560-4cbd2690cb50.jpg','/goods-img/cd6d91b0-69b2-4415-8560-4cbd2690cb50.jpg','

商品介绍加载中...

',55,27,1000,'',0,0,'2019-09-18 13:19:22',0,'2020-10-13 10:41:59'), (10117,'无印良品 MUJI 男式','棉水洗 平纹短袖衬衫 白色 L',0,'/goods-img/b08c94ac-cba2-4468-b3d0-03d9447f5bf2.jpg','/goods-img/b08c94ac-cba2-4468-b3d0-03d9447f5bf2.jpg','

商品介绍加载中...

',178,89,1000,'',0,0,'2019-09-18 13:19:22',0,'2020-10-13 10:41:59'), (10118,'无印良品 MUJI 香/绿意','12支装/棒状',0,'/goods-img/5a65f952-4141-47f8-8f8e-84120bbf74ea.jpg','/goods-img/5a65f952-4141-47f8-8f8e-84120bbf74ea.jpg','

商品介绍加载中...

',32,32,1000,'',0,0,'2019-09-18 13:19:22',0,'2020-10-13 10:41:59'), (10119,'无印良品 MUJI 润肤乳霜(高保湿型)50g','50g',0,'/goods-img/503ef53e-d4ac-4c4e-83a7-8a03ead0ecc8.jpg','/goods-img/503ef53e-d4ac-4c4e-83a7-8a03ead0ecc8.jpg','

商品介绍加载中...

',159,159,1000,'',0,0,'2019-09-18 13:19:22',0,'2020-10-13 10:41:59'), (10120,'无印良品 MUJI 柔滑笔芯','黑色',0,'/goods-img/aa83ce5b-2db1-4ecf-bc4f-f43c437894d7.jpg','/goods-img/aa83ce5b-2db1-4ecf-bc4f-f43c437894d7.jpg','

商品介绍加载中...

',19,19,1000,'',0,0,'2019-09-18 13:19:22',0,'2020-10-13 10:41:59'), (10121,'无印良品 MUJI 铝制','挂钩/吸盘式_2个装 大/约宽4.5x高6cm 2个装',0,'/goods-img/5c590548-9de3-47a3-8cb9-4d8f040a9635.png','/goods-img/5c590548-9de3-47a3-8cb9-4d8f040a9635.png','

商品介绍加载中...

',25,25,1000,'',0,0,'2019-09-18 13:19:22',0,'2020-10-13 10:41:59'), (10122,'无印良品 MUJI 修正带','POM材质 替芯',0,'/goods-img/93181f0b-c069-4542-be91-a63856cd12d1.jpg','/goods-img/93181f0b-c069-4542-be91-a63856cd12d1.jpg','

商品介绍加载中...

',15,15,1000,'',0,0,'2019-09-18 13:19:22',0,'2020-10-13 10:41:59'), (10123,'无印良品 MUJI 压克力记录板夹','A4用/220×310mm',0,'/goods-img/45be1de3-447b-404b-9df8-ddf07fdc8647.jpg','/goods-img/45be1de3-447b-404b-9df8-ddf07fdc8647.jpg','

商品介绍加载中...

',35,35,1000,'',0,0,'2019-09-18 13:19:22',0,'2020-10-13 10:41:59'), (10124,'无印良品(MUJI) 自然亲肤粉底液 自然透亮色','新蜂精选',0,'/goods-img/7f905827-5765-40bc-a1b8-bedd9f407ced.jpg','/goods-img/7f905827-5765-40bc-a1b8-bedd9f407ced.jpg','

商品介绍加载中...

',75,75,1000,'',0,0,'2019-09-18 13:19:22',0,'2020-10-13 10:41:59'), (10125,'无印良品 MUJI PE小分装盒','透明 30g',0,'/goods-img/1d7f28bb-6597-48de-a6bb-2561697db883.jpg','/goods-img/1d7f28bb-6597-48de-a6bb-2561697db883.jpg','

商品介绍加载中...

',10,10,1000,'',0,0,'2019-09-18 13:19:30',0,'2020-10-13 10:41:59'), (10126,'无印良品 MUJI 保湿化妆液','新蜂精选',0,'/goods-img/53a089a9-e1d1-487e-974e-18bb4df41cf3.jpg','/goods-img/53a089a9-e1d1-487e-974e-18bb4df41cf3.jpg','

商品介绍加载中...

',160,80,1000,'',0,0,'2019-09-18 13:19:30',0,'2020-10-13 10:41:59'), (10127,'无印良品 MUJI 女式','棉弹力 高领T恤 深灰色 M',0,'/goods-img/53a6478b-4fd5-4add-b095-9fd4ad983a7b.jpg','/goods-img/53a6478b-4fd5-4add-b095-9fd4ad983a7b.jpg','

商品介绍加载中...

',128,40,1000,'',0,0,'2019-09-18 13:19:30',0,'2020-10-13 10:41:59'), (10128,'无印良品 MUJI 男式','棉水洗 牛津纽扣领短袖衬衫 白色 L',0,'/goods-img/561e9e6d-b130-468d-8328-36a5ff70cdfa.jpg','/goods-img/561e9e6d-b130-468d-8328-36a5ff70cdfa.jpg','

商品介绍加载中...

',178,89,1000,'',0,0,'2019-09-18 13:19:30',0,'2020-10-13 10:41:59'), (10129,'无印良品 MUJI 基础润肤乳液','高保湿型 50ml',0,'/goods-img/01514263-83b4-4ac7-aee3-5e5a2448414f.jpg','/goods-img/01514263-83b4-4ac7-aee3-5e5a2448414f.jpg','

商品介绍加载中...

',37,29,1000,'',0,0,'2019-09-18 13:19:30',0,'2020-10-13 10:41:59'), (10130,'MUJI 羽毛 靠垫','白色',0,'/goods-img/23e5ee1d-5bb7-4f2a-b4b5-4fbc9ca3c163.jpg','/goods-img/23e5ee1d-5bb7-4f2a-b4b5-4fbc9ca3c163.jpg','

商品介绍加载中...

',65,65,1000,'',0,0,'2019-09-18 13:19:30',0,'2020-10-13 10:41:59'), (10131,'无印良品(MUJI) 可携带用小卷尺 白色','新蜂精选',0,'/goods-img/a4d3a61e-b0d3-4c58-85d6-fddf1de85f66.jpg','/goods-img/a4d3a61e-b0d3-4c58-85d6-fddf1de85f66.jpg','

商品介绍加载中...

',28,28,1000,'',0,0,'2019-09-18 13:19:30',0,'2020-10-13 10:41:59'), (10132,'无印良品 MUJI 笔记本/5mm方格','暗灰色 B5/30张/线装',0,'/goods-img/38c25b00-a4fb-4893-aa8e-34ff76963397.jpg','/goods-img/38c25b00-a4fb-4893-aa8e-34ff76963397.jpg','

商品介绍加载中...

',9,9,1000,'',0,0,'2019-09-18 13:19:30',0,'2020-10-13 10:41:59'), (10133,'无印良品 MUJI 低重心铅笔','白色',0,'/goods-img/dc497882-61ea-4d4f-98fe-d2b2500eda01.jpg','/goods-img/dc497882-61ea-4d4f-98fe-d2b2500eda01.jpg','

商品介绍加载中...

',47,47,1000,'',0,0,'2019-09-18 13:19:30',0,'2020-10-13 10:41:59'), (10134,'无印良品(MUJI) 手动碎纸机','新蜂精选',0,'/goods-img/f6e1ce14-a590-4736-9d36-df5628bc4188.jpg','/goods-img/f6e1ce14-a590-4736-9d36-df5628bc4188.jpg','

商品介绍加载中...

',75,75,1000,'',0,0,'2019-09-18 13:19:30',0,'2020-10-13 10:41:59'), (10135,'无印良品 MUJI 女式','无袖衫 燕麦色 XL',0,'/goods-img/c2e30c9b-ce49-4824-824a-b7d3ae173340.jpg','/goods-img/c2e30c9b-ce49-4824-824a-b7d3ae173340.jpg','

商品介绍加载中...

',178,53,1000,'',0,0,'2019-09-18 13:19:30',0,'2020-10-13 10:41:59'), (10136,'无印良品 MUJI 女式','粗棉线长袖T恤 生成色 L',0,'/goods-img/4b1b98d5-359f-4025-85e3-f357b6e9724a.jpg','/goods-img/4b1b98d5-359f-4025-85e3-f357b6e9724a.jpg','

商品介绍加载中...

',198,70,1000,'',0,0,'2019-09-18 13:19:30',0,'2020-10-13 10:41:59'), (10137,'无印良品 MUJI 塑料浴室座椅/小','原色',0,'/goods-img/37053615-750d-486e-b218-358a7c1adb21.jpg','/goods-img/37053615-750d-486e-b218-358a7c1adb21.jpg','

商品介绍加载中...

',85,85,1000,'',0,0,'2019-09-18 13:19:30',0,'2020-10-13 10:41:59'), (10138,'无印良品(MUJI) 树脂携带型订书机 白色','新蜂精选',0,'/goods-img/21dd6bd9-c4bc-4e17-8fed-23775cebf361.jpg','/goods-img/21dd6bd9-c4bc-4e17-8fed-23775cebf361.jpg','

商品介绍加载中...

',42,42,1000,'',0,0,'2019-09-18 13:19:30',0,'2020-10-13 10:41:59'), (10139,'无印良品 MUJI 基础润肤乳液','滋润型',0,'/goods-img/b8978340-ff72-4b5a-a9d3-4b5610982764.jpg','/goods-img/b8978340-ff72-4b5a-a9d3-4b5610982764.jpg','

商品介绍加载中...

',28,22,1000,'',0,0,'2019-09-18 13:19:30',0,'2020-10-13 10:41:59'), (10140,'无印良品(MUJI) 控色隔离霜30g 浅蓝色','新蜂精选',0,'/goods-img/b2969d29-b073-48f3-aa9a-b8aeb08a98d6.jpg','/goods-img/b2969d29-b073-48f3-aa9a-b8aeb08a98d6.jpg','

商品介绍加载中...

',65,65,1000,'',0,0,'2019-09-18 13:19:30',0,'2020-10-13 10:41:59'), (10141,'无印良品 MUJI 女式','粗棉线条纹长袖T恤 黑*横条 L',0,'/goods-img/a905c374-3411-4ddd-9b84-7ecbc9b50620.jpg','/goods-img/a905c374-3411-4ddd-9b84-7ecbc9b50620.jpg','

商品介绍加载中...

',198,70,1000,'',0,0,'2019-09-18 13:19:30',0,'2020-10-13 10:41:59'), (10142,'无印良品 MUJI PE小分装盒','透明 30g',0,'/goods-img/2750405a-2e01-463d-a059-54644c67f7cc.jpg','/goods-img/2750405a-2e01-463d-a059-54644c67f7cc.jpg','

商品介绍加载中...

',10,10,1000,'',0,0,'2019-09-18 13:19:35',0,'2020-10-13 10:41:59'), (10143,'无印良品 MUJI 保湿化妆液','新蜂精选',0,'/goods-img/17656dd7-c0fb-431d-810a-5eb29d07c011.jpg','/goods-img/17656dd7-c0fb-431d-810a-5eb29d07c011.jpg','

商品介绍加载中...

',160,80,1000,'',0,0,'2019-09-18 13:19:35',0,'2020-10-13 10:41:59'), (10144,'无印良品 MUJI 女式','棉弹力 高领T恤 深灰色 M',0,'/goods-img/780e716a-7be8-4d94-b8b6-833b4d97e148.jpg','/goods-img/780e716a-7be8-4d94-b8b6-833b4d97e148.jpg','

商品介绍加载中...

',128,40,1000,'',0,0,'2019-09-18 13:19:35',0,'2020-10-13 10:41:59'), (10145,'无印良品 MUJI 男式','棉水洗 牛津纽扣领短袖衬衫 白色 L',0,'/goods-img/94f5b471-1148-4320-aa8a-68573706fd91.jpg','/goods-img/94f5b471-1148-4320-aa8a-68573706fd91.jpg','

商品介绍加载中...

',178,89,1000,'',0,0,'2019-09-18 13:19:35',0,'2020-10-13 10:41:59'), (10146,'无印良品 MUJI 基础润肤乳液','高保湿型 50ml',0,'/goods-img/a12dcb9c-bb36-4df9-b517-1578a03fe062.jpg','/goods-img/a12dcb9c-bb36-4df9-b517-1578a03fe062.jpg','

商品介绍加载中...

',37,29,1000,'',0,0,'2019-09-18 13:19:35',0,'2020-10-13 10:41:59'), (10147,'MUJI 羽毛 靠垫','白色',0,'/goods-img/0f701215-b782-40c7-8bbd-97b51be56461.jpg','/goods-img/0f701215-b782-40c7-8bbd-97b51be56461.jpg','

商品介绍加载中...

',65,65,988,'悠享惬意',0,0,'2019-09-18 13:19:35',0,'2020-10-13 10:41:59'), (10148,'无印良品(MUJI) 可携带用小卷尺 白色','新蜂精选',0,'/goods-img/737afa41-1905-4dbc-ab33-95f8489dde5b.jpg','/goods-img/737afa41-1905-4dbc-ab33-95f8489dde5b.jpg','

商品介绍加载中...

',28,28,1000,'',0,0,'2019-09-18 13:19:35',0,'2020-10-13 10:41:59'), (10149,'无印良品 MUJI 笔记本/5mm方格','暗灰色 B5/30张/线装',0,'/goods-img/c6632420-ad7e-451b-a2a9-b02299653db1.jpg','/goods-img/c6632420-ad7e-451b-a2a9-b02299653db1.jpg','

商品介绍加载中...

',9,9,1000,'',0,0,'2019-09-18 13:19:35',0,'2020-10-13 10:41:59'), (10150,'无印良品 MUJI 低重心铅笔','白色',0,'/goods-img/060e3ace-71ca-44a2-9ded-73a05f186fcf.jpg','/goods-img/060e3ace-71ca-44a2-9ded-73a05f186fcf.jpg','

商品介绍加载中...

',47,47,1000,'',0,0,'2019-09-18 13:19:35',0,'2020-10-13 10:41:59'), (10151,'无印良品(MUJI) 手动碎纸机','新蜂精选',0,'/goods-img/58d831e4-07f4-44e2-a994-1a7d585452a1.jpg','/goods-img/58d831e4-07f4-44e2-a994-1a7d585452a1.jpg','

商品介绍加载中...

',75,75,1000,'',0,0,'2019-09-18 13:19:35',0,'2020-10-13 10:41:59'), (10152,'无印良品 MUJI 女式','无袖衫 燕麦色 XL',0,'/goods-img/f2aaadc0-ddda-4736-9826-2dbb2c533ea0.jpg','/goods-img/f2aaadc0-ddda-4736-9826-2dbb2c533ea0.jpg','

商品介绍加载中...

',178,53,1000,'',0,0,'2019-09-18 13:19:35',0,'2020-10-13 10:41:59'), (10153,'无印良品 MUJI 女式','粗棉线长袖T恤 生成色 L',0,'/goods-img/09c87218-d645-48e7-bbd5-54af5e77bf4b.jpg','/goods-img/09c87218-d645-48e7-bbd5-54af5e77bf4b.jpg','

商品介绍加载中...

',198,70,1000,'',0,0,'2019-09-18 13:19:35',0,'2020-10-13 10:41:59'), (10154,'无印良品 MUJI 塑料浴室座椅','原色',0,'/goods-img/15395057-94e9-4545-a8ee-8aee025f40c5.jpg','/goods-img/15395057-94e9-4545-a8ee-8aee025f40c5.jpg','

商品介绍加载中...

',85,85,998,'无印良品',0,0,'2019-09-18 13:19:35',0,'2020-10-13 10:41:59'), (10155,'无印良品(MUJI) 树脂携带型订书机 白色','新蜂精选',0,'/goods-img/3b40971a-3f32-45cf-a99a-aada90ee8e33.jpg','/goods-img/3b40971a-3f32-45cf-a99a-aada90ee8e33.jpg','

商品介绍加载中...

',42,42,1000,'',0,0,'2019-09-18 13:19:35',0,'2020-10-13 10:41:59'), (10156,'无印良品 MUJI 基础润肤乳液','滋润型',0,'/goods-img/f65ef709-8fa8-4a3f-8abd-75a9b0492b14.jpg','/goods-img/f65ef709-8fa8-4a3f-8abd-75a9b0492b14.jpg','

商品介绍加载中...

',28,22,1000,'',0,0,'2019-09-18 13:19:35',0,'2020-10-13 10:41:59'), (10157,'无印良品(MUJI) 控色隔离霜30g 浅蓝色','新蜂精选',0,'/goods-img/66311489-b28b-41c3-ac34-540293df6e42.jpg','/goods-img/66311489-b28b-41c3-ac34-540293df6e42.jpg','

商品介绍加载中...

',65,65,1000,'',0,0,'2019-09-18 13:19:35',0,'2020-10-13 10:41:59'), (10158,'无印良品 女式粗棉线条纹长袖T恤','黑*横条 L',20,'/goods-img/5488564b-8335-4b0c-a5a4-52f3f03ee728.jpg','http://localhost:28089/goods-img/5488564b-8335-4b0c-a5a4-52f3f03ee728.jpg','

商品介绍加载中...

',198,70,985,'无印良品',0,0,'2019-09-18 13:19:35',0,'2020-10-13 10:41:59'), (10159,'Apple AirPods 配充电盒','苹果蓝牙耳机',0,'/goods-img/53c9f268-7cd4-4fac-909c-2dc066625655.jpg','/goods-img/53c9f268-7cd4-4fac-909c-2dc066625655.jpg','

商品介绍加载中...

',1246,1246,987,'',0,0,'2019-09-18 13:21:28',0,'2020-10-13 10:41:59'), (10160,'小米 Redmi AirDots','真无线蓝牙耳机|分体式耳机 |收纳充电盒 |蓝牙5.0 |按键防触控操作',51,'/goods-img/c47403f1-b706-453b-88d8-2bfdee0316be.jpg','/goods-img/c47403f1-b706-453b-88d8-2bfdee0316be.jpg','

商品介绍加载中...

',129,129,998,'为自由发声',0,0,'2019-09-18 13:21:28',0,'2020-10-13 10:41:59'), (10161,'荣耀原装三键线控带麦半入耳式耳机AM116(尊爵版)适用于华为荣耀手机','新蜂精选',0,'/goods-img/183481c3-47ff-4b2e-926f-b02b926ac02c.jpg','/goods-img/183481c3-47ff-4b2e-926f-b02b926ac02c.jpg','

商品介绍加载中...

',69,49,1000,'',0,0,'2019-09-18 13:21:28',0,'2020-10-13 10:41:59'), (10162,'诺基亚(NOKIA)BH-705 银白色 5.0真无线蓝牙耳机迷你运动跑步音乐商务入耳式安卓苹果手机蓝牙耳机','新蜂精选',0,'/goods-img/5e0d089b-fa91-410d-8ff2-9534eb6f627f.jpg','/goods-img/5e0d089b-fa91-410d-8ff2-9534eb6f627f.jpg','

商品介绍加载中...

',499,499,1000,'',0,0,'2019-09-18 13:21:28',0,'2020-10-13 10:41:59'), (10163,'华为耳机原装半入耳式有线mate9p10plus8x荣耀v20v10nova2s9iv9p9play 【标准版】华为AM115 白色-热卖款','新蜂精选',0,'/goods-img/79e2b467-a075-46ef-ab43-aa0535f8e4c9.jpg','/goods-img/79e2b467-a075-46ef-ab43-aa0535f8e4c9.jpg','

商品介绍加载中...

',69,39,999,'',0,0,'2019-09-18 13:21:28',0,'2020-10-13 10:41:59'), (10164,'Beats X 蓝牙无线','入耳式耳机 带麦可通话 -桀骜黑红(十周年版) MRQA2PA/A',0,'/goods-img/911531a4-39a6-4771-b26e-2ba4db1ebcda.jpg','/goods-img/911531a4-39a6-4771-b26e-2ba4db1ebcda.jpg','

商品介绍加载中...

',1168,799,1000,'',0,0,'2019-09-18 13:21:28',0,'2020-10-13 10:41:59'), (10165,'华为( HUAWEI) 华为无线耳机','真无线蓝牙耳机 双耳蓝牙音乐耳机 Freebuds 2 无线耳机 陶瓷白',0,'/goods-img/e70a4f29-2269-466a-984e-01e018206c2e.jpg','/goods-img/e70a4f29-2269-466a-984e-01e018206c2e.jpg','

商品介绍加载中...

',899,799,1000,'',0,0,'2019-09-18 13:21:28',0,'2020-10-13 10:41:59'), (10166,'【自营仓次日达】moloke真无线蓝牙耳机双耳适用于苹果华为小米 运动跑步入耳式oppo迷你商务耳机 【1:1尊享版】自动弹窗+无线充电+可触控(热卖)','新蜂精选',51,'/goods-img/70dc1586-13bd-4b4c-92a9-fe20aa1d531f.jpg','/goods-img/70dc1586-13bd-4b4c-92a9-fe20aa1d531f.jpg','

商品介绍加载中...

',359,199,1000,'',0,0,'2019-09-18 13:21:28',0,'2020-10-13 10:41:59'), (10167,'Beats Powerbeats Pro','完全无线高性能耳机 真无线蓝牙运动耳机 象牙白',0,'/goods-img/04441cd4-81c8-4ad9-a067-9d15422e508f.jpg','/goods-img/04441cd4-81c8-4ad9-a067-9d15422e508f.jpg','

商品介绍加载中...

',1888,1888,1000,'',0,0,'2019-09-18 13:21:28',0,'2020-10-13 10:41:59'), (10168,'纽曼(Newmine)NM-LK06 全兼容线控音乐手机耳机 白色','新蜂精选',0,'/goods-img/ad53ea23-6974-4e44-b62d-eab498ce1d63.jpg','/goods-img/ad53ea23-6974-4e44-b62d-eab498ce1d63.jpg','

商品介绍加载中...

',9,9,1000,'',0,0,'2019-09-18 13:21:28',0,'2020-10-13 10:41:59'), (10169,'索尼(SONY)重低音立体声耳机MDR-XB55AP 黑色','新蜂精选',0,'/goods-img/01e1998d-f183-4e99-b8ba-7715727cf90b.jpg','/goods-img/01e1998d-f183-4e99-b8ba-7715727cf90b.jpg','*黑色实物偏灰,请以实物为准 Bass Booster低音增强器技术可呈现紧实深邃低频。 12 毫米驱动单元和110dB/mW 的高灵敏度,呈现高质感音效。 人体工学设计的倾斜入耳方式,让耳塞能够深入耳朵内部,呈现出色的隔音效果,同时带来舒适的佩戴感和高音质的享受。 耳塞能够深入耳朵内部,呈现出色的隔音效果,同时为您带来舒适的佩戴感和高音质的享受。 采用混合两种硬度硅胶的耳塞套: 核心部分使用硬质材料保持音质,减少因耳塞变形导致的声音失真; 外围部分柔软材料提高了耳塞密闭性,让您能长时间舒适佩戴。 *线控的可用性及操作因智能手机而异 耳机线表面细小沟壑,减少容易引起缠绕的摩擦,使导线不容易纠结在一起,方便欣赏音乐和携带。 防缠绕耳机线 盲点设计 便携袋 防尘滤网 导线滑块 4种尺寸耳塞套 摘下耳机的耳塞套,可见保护单元的网罩,用来防止异物和灰尘堵塞单元,使耳机经久耐听。 在左耳外壳和耳机线的连接处设有浮点,凭手指触摸就能判别左右耳,方便操作。 随机附赠收纳袋一只,保护你心爱的耳机。 利用导线滑块来调整左右耳机线的长度,也能够减少收纳耳机时容易出现的缠线现象 提供4对不同尺寸(SS、S、M、L)的耳塞套(M号出厂时已安装至耳机上),根据你的耳洞大小自由更换,获得良好的隔音效果,佩戴舒适。 ● 立体声耳机 ● 混合硅胶耳塞(SS/S/M/L 每种尺寸2个) *M号出厂时安装至本耳机。 ● 便携袋(×1) *EXTRA BASS 和 EXTRABASS 是索尼公司的商标',229,185,1000,'',0,0,'2019-09-18 13:21:28',0,'2020-10-13 10:41:59'), (10170,'索尼(SONY)WI-1000X Hi-Res颈挂式 入耳式','无线蓝牙耳机 高音质降噪耳机 手机通话 黑色',0,'/goods-img/1631a30b-287c-41da-bbbe-1a9b1b8d1552.jpg','/goods-img/1631a30b-287c-41da-bbbe-1a9b1b8d1552.jpg','

商品介绍加载中...

',2399,1499,1000,'',0,0,'2019-09-18 13:21:28',0,'2020-10-13 10:41:59'), (10171,'小米耳机 圈铁Pro 入耳式有线运动音乐耳机耳麦','新蜂精选',51,'/goods-img/f3d269a4-5317-4b30-b164-1311f6c1f058.jpg','/goods-img/f3d269a4-5317-4b30-b164-1311f6c1f058.jpg','使用双动圈 + 动铁 三单元发声 / 均衡自然声音 高保真石墨烯振膜 / 25 道工序打磨 / 弹力磨砂线材 Pro 小米圈铁耳机 孕育万物的天空和大地,时刻传达着声音的释放与组合,更是寻找灵感的源头,鸟鸣、流水、雷响、风啸不同的声音互相交融,共同演奏出自然的本真。 小米圈铁耳机 Pro 使用双“动圈”单元+“动铁”单元,将三个单元共同融入到同一个耳机中,双“动圈”的醇厚低音,让声音更加扎实稳重,石墨烯材料的加入,则让声音的细节更为丰富。“动铁”的高音透亮,稳定自然,感受三频均衡的本色声音。随着声音的流淌,仿佛置身自然,听见这些细节,让声音一开始就感动内心。 双动圈+动铁,三单元发声,听见更多细节 为了可以真正实现高、中、低三频均衡,小米圈铁耳机 Pro  加入了双“动圈”单元,大动圈负责中低频,小动圈负责高频。在“动铁”单元的配合下,耳机的低频下潜深,中频声音扎实,而高频的细节展现更为丰富。那些刚刚好的声音,听在耳里,都在心里。 三频更均衡,声音更自然 我们听到的绝大多数乐器、人声,都在中低频段。为了让这部分声音更均衡、有感染力,我们都交由采用了石墨烯振膜的双动圈单元来负责,中低频更扎实,兼具丰富细节表现力。 石墨烯是目前自然界已知材料中轻薄、强度更高的材料,对声音的传导速度快,将它用作振膜材质,高频延展性能更好,细节丰富,声音清澈自然,更富穿透力。同时强度又是钢铁的100倍, 可以尽可能还原出电流信号, 真正发出高保真的好声音。 石墨烯振膜,让双动圈更有实力 小米圈铁耳机 Pro 的“动铁”单元依然采用自主研发的 \"衔铁+驱动杆\" 结构,让声音细腻真实,更为稳定,在电容分频器的作用下,让高中低音衔接更好,失真更少。不论当你听何种音乐,细腻的感情都会被准确还原,听每首歌就像读每个故事,时刻感动自己。 动铁单元设计,高频解析好,细节不失真 好的音乐人将情感与生活用真实的方式,转化为音乐传递给每个人,每首歌都是一个故事,铭刻在各自的记忆中,为了让故事更好的表达,小米圈铁耳机 Pro 在科学客观调音的基础上,再次邀请到荣获 4 次格莱美大奖的 Luca Bignardi,为小米圈铁耳机 Pro 进行主观调音,为的就是让每个喜爱音乐的人能够真切的感受到每一个故事,跟随内心,娓娓道来... 多种科学调音,让声音更鲜活,更温暖 当耳机真正为声音服务时,设计将不再只是修饰耳机外观的道具,它将会成为辅助声音的一部分,小米圈铁耳机 Pro 采用圆润的设计风格,45° 斜角入耳设计,在满足舒适的同时更保证了声音的完整呈现。精密金属音腔设计,让音乐沉于耳畔,更有声音质感,弹力 TPE 磨砂线材的选用,让耳机线更为坚固耐用,确保耳机长久使用。一副好耳机,让声音和外表一起美好。 全新的外观设计,和声音一起美好 好的设计需要灵感,而灵感源于生活,为了锁住声音的灵感,小米圈铁耳机 Pro 将耳塞设计成45°斜角式入耳,贴合耳道,满足佩戴舒适感的同时尽可能减少外界声音干扰,毫无保留地听自己爱的音乐。 45°斜角入耳,舒适佩戴 小米圈铁耳机 Pro 的线控麦克风从耳机整体设计风格出发,金属磨砂弹头造型,精致小巧,指压按键圆润舒适,听歌的同时,更能感知指尖上的金属质感。 小米圈铁耳机 Pro 的耳机线材选取 TPE 材质,作为一种具有橡胶的高弹性材质, 触感柔软、耐温等特性,用它做成耳机线,将更为抗拉、耐用并且不易缠绕。让好音乐的陪伴更长久。 小米圈铁耳机 Pro 的耳塞选取奶嘴级硅胶材质,触感柔软顺滑,减少了耳塞对皮肤的刺激,让肌肤倍感亲密,同时提供四对不同尺寸的耳塞套,让佩戴者根据不同需求选择,带上它,向自己喜爱的音乐问好! 用匠心打磨每一件产品,即使过程艰难复杂,也依然充满斗志,小米圈铁耳机 Pro 的诞生过程就是这样。25 道工序打造的金属音腔,每一处细节都精心打磨,一体成型钻石切割、细密 CD 纹雕刻、锆石喷砂、阳极氧化,千锤百炼,不放过每个细节,将金属打磨成入耳的艺术品,这就是小米圈铁耳机 Pro 对音乐执着,对好产品更要执着。 小米圈铁耳机 Pro 是铝合金音腔,采用了 CNC 钻石切割一刀成型工艺,加工精度高达0.01mm,这种工艺在对铝合金加工前都要进行工艺分析,选择合适的刀具及切削用量,将打磨成型,让耳机具有更细腻润泽的手感。 小米圈铁耳机 Pro 运用精密的 CD 纹处理,纹理细至 0.14mm,散发金属光泽,就像耳机的指纹一样。如此的精密打磨,只为让小米圈铁耳机 Pro 更具质感,让金属更光辉熠熠。 选用精细锆石喷砂,赋予小米圈铁耳机 Pro 细致均匀的外观,有效保证了耳机表面硬度,不易刮伤。出厂时,会在小米圈铁耳机 Pro 表层增加阳极处理,保证了美观程度和耐磨性,6μ的阳极厚度,坚固、耐磨,做传达好声音的艺术品。 拥有超过 700 项高于行业标准的苛刻测试,每一种测试都见证了小米圈铁耳机 Pro 的高品质, 从音乐品质到设计创新,再到匠心工艺,集合好耳机的所有亮点,都只为带给用户更好的音乐体验和使用感受,好的声音,一定需要千锤百炼 。',149,149,1000,'',0,0,'2019-09-18 13:21:28',0,'2020-10-13 10:41:59'), (10172,'Bose QuietControl 30','无线耳机 QC30耳塞式蓝牙降噪耳麦',0,'/goods-img/966a8b32-f547-457c-9161-009d3113d584.jpg','/goods-img/966a8b32-f547-457c-9161-009d3113d584.jpg','

商品介绍加载中...

',2498,2498,1000,'',0,0,'2019-09-18 13:21:28',0,'2020-10-13 10:41:59'), (10173,'Beats Solo3 Wireless','头戴式 蓝牙无线耳机 手机耳机 游戏耳机 - 桀骜黑红(十周年版) MRQC2PA/A',0,'/goods-img/72218e28-fc58-4aa0-b3cd-c1f2c764d25e.jpg','/goods-img/72218e28-fc58-4aa0-b3cd-c1f2c764d25e.jpg','

商品介绍加载中...

',2268,1698,1000,'',0,0,'2019-09-18 13:21:28',0,'2020-10-13 10:41:59'), (10174,'索尼(SONY)WH-1000XM3 高解析度无线蓝牙降噪 头戴式耳机(触控面板','智能降噪 长久续航)黑色',0,'/goods-img/4cc6c606-4d69-4f49-b10c-01cedeef813f.jpg','/goods-img/4cc6c606-4d69-4f49-b10c-01cedeef813f.jpg','

商品介绍加载中...

',2899,2599,1000,'',0,0,'2019-09-18 13:21:28',0,'2020-10-13 10:41:59'), (10175,'雷蛇 Razer 北海巨妖标准版X','北海巨妖标准版升级款 头戴式游戏耳机 电竞耳麦 7.1 电脑手机耳机 黑色',0,'/goods-img/7345c467-6c2d-4f30-a73d-83d675d5208c.jpg','/goods-img/7345c467-6c2d-4f30-a73d-83d675d5208c.jpg','产品信息Product Information 产品规格Product Specifications 品牌介绍Brand Introduction 注意事项Warning & Caution 雷蛇产品在出厂时会进行检测,脚贴及USB接口处如有轻微划痕属于正常测试痕迹。 RAZER关于划痕的注意事项: 以上数据图片均为官方测试环境下结果,因使用环境/设备不同会存在一定的差异,仅供参考,数据请以实际为准!  1. 产品实物与外包装上的SN(序列号)必须一致; 2. 产品外包装不能严重破损,盒内的相关配件要齐全,不能有缺失; 3. 不能有明显的人为破损(表面有明显的人为划痕,使用及存在拆卸的痕迹); 4. 防伪标签不得撕开或损毁。 RAZER关于7天无理由退换货的注意事项: ',349,299,1000,'',0,0,'2019-09-18 13:21:28',0,'2020-10-13 10:41:59'), (10176,'森海塞尔(Sennheiser)MomentumTrueWireless 真无线蓝牙hifi发烧入耳式耳机 蓝牙5.0','黑色',0,'/goods-img/efea018e-8ab0-47f9-a3d4-260c8cd2de5f.jpg','/goods-img/efea018e-8ab0-47f9-a3d4-260c8cd2de5f.jpg','聆听带来改变 真     无     线     蓝     牙     HiFi     耳     机 MOMENTUM 真无线 懂你所需 全新的 MOMENTUM 真无线耳机,高品质的声音质量传承 MOMENTUM 品质,成为一款具有重要技术成就的新产品。 这款性能优异的蓝牙耳机融合音频质量、佩戴舒适性和精致设计及工艺。 全新的 MOMENTUM 透明聆听功能 防水防泼溅 电池使用时长 (4+8小时) 精雕细琢 经典优雅 高品质声音质量 智能降噪 智能触控操作 支持蓝牙5.0技术 智能触控操作 支持蓝牙 5.0技术 MOMENTUM真无线耳机采用Sennheiser发烧级别7毫米动圈驱动单元,可确保饱满的立体声效果,带来高保真音质,为苛刻的听者带来出色的高保真度。 高品质声音质量 两侧触摸区域都有单独的控制功能,您可以轻松使用右耳耳机语音访问智能助手(如苹果 Siri或Google智能助手)。 轻轻点击或滑动触摸界面,使用自然语音命令即可播放音乐、接听电话。 支持蓝牙5.0技术及编解码技术(包括AAC、Qualcomm apt-XTM和apt-X低延迟),这款耳机带来出众的连接稳定性和音频流畅性。 真正的无线体验 轻松适配周围环境 MOMENTUM 真无线耳机让你更好地感知外部环境,透明聆听让你能听到周围的环境声,从而更好地感知周围的环境,不需要摘掉耳机就能融入到自然的交谈之中。甚至在嘈杂的环境中,电话呼叫和语音交互也能够通过双话筒波束成形技术得以实现。 智能交互 通过自动开启/关闭和智能暂停功能,可以检测到耳机何时被收起来或者不使用,从而节约能源。 你的世界由你把控 通过双击右耳耳机开/关透明聆听功能 打开透明聆听=接收周围环境音 关闭透明聆听=物理降噪模式,不接收周围音 不需要摘掉耳机就可轻松地与周围人进行交谈。 4种尺寸的耳垫可选,均符合人体工程学设计,防水防泼溅,能够满足用户舒适佩戴的需求。 个性定制舒适体验 MOMENTUM真无线拥有4小时电池续航时间,可通过其带有集成电源的小巧耳机盒进行充电,从而享受长达12(4+8)小时的全天聆听乐趣,并满足未来所需。 镀金充电接触点 可磁性吸附到充电盒上 高保真7毫米动圈驱动单元 带来出色的声音重放 金属镭射表面 具有触控功能 多色 LED指示灯 用于语音信号拾取和透明聆听功能的话筒 舒适的入耳式 硅胶耳垫 便捷充电盒持久续航 注重细节、富于美感,这款小巧、 靓丽而轻盈的耳机是技术与艺术的 结合。它既是声音重放技术的成就,更是你耳畔精美的配饰。 质感黑色外壳,闪烁的金属镭射表面,镀金的充电接触点——时尚与功能融合于标志性的设计之中,带来优雅和实用感。 Sennheiser智能控制 MOMENTUM 真无线耳机提供了更为智能和个性化的体验,可以通过新款Sennheiser智能控制应用进行优化,根据个人喜好,利用内置音频EQ对声音进行微调。免费下载,兼容iOS 版本 11.0 及以上版本和Android 版本 7.0 及以上版本 ,简便直观的控制界面,为您的耳机提供个性化的配置和升级等功能。 APP 下载方法 Android 版本 7.0 及以上版本 打开链接下载APP https://share.weiyun.com/54byqjn iOS 版本 11.0 及以上版本 打开APP Store搜索 Sennheiser smart control 下载APP',2399,2399,1000,'',0,0,'2019-09-18 13:21:28',0,'2020-10-13 10:41:59'), (10177,'Bose SoundSport Free','真无线蓝牙耳机--黑色 运动耳机 防掉落耳塞',0,'/goods-img/b3de8a39-e33c-432f-872f-46f4a1662498.jpg','/goods-img/b3de8a39-e33c-432f-872f-46f4a1662498.jpg','

商品介绍加载中...

',1699,1699,1000,'',0,0,'2019-09-18 13:21:28',0,'2020-10-13 10:41:59'), (10178,'华为原装降噪有线手机耳机Mate9 10P9P10Plus荣耀V9V10PlayNova2s9i8x 【送耳机收纳包】AM115半入耳式耳机-经典热卖款','新蜂精选',0,'/goods-img/d6565a7e-473b-4933-93c5-e646495c8c4c.jpg','/goods-img/d6565a7e-473b-4933-93c5-e646495c8c4c.jpg','

商品介绍加载中...

',99,39,1000,'',0,0,'2019-09-18 13:21:28',0,'2020-10-13 10:41:59'), (10179,'Apple 采用Lightning/闪电接头的 EarPods','耳机',0,'/goods-img/bf6ccbc4-d0d0-4fbb-b975-4becb9cb38f4.jpg','/goods-img/bf6ccbc4-d0d0-4fbb-b975-4becb9cb38f4.jpg','

商品介绍加载中...

',223,223,1000,'',0,0,'2019-09-18 13:21:28',0,'2020-10-13 10:41:59'), (10180,'Apple AirPods 配充电盒','苹果蓝牙耳机',0,'/goods-img/64768a8d-0664-4b29-88c9-2626578ffbd1.jpg','/goods-img/64768a8d-0664-4b29-88c9-2626578ffbd1.jpg','

商品介绍加载中...

',1246,1246,995,'妙出新境界',0,0,'2019-09-18 13:21:35',0,'2020-10-13 10:41:59'), (10181,'小米 Redmi AirDots','真无线蓝牙耳机|分体式耳机 |收纳充电盒 |蓝牙5.0 |按键防触控操作',51,'/goods-img/36d0fe8f-aa28-423c-81e7-82cab31b7598.jpg','/goods-img/36d0fe8f-aa28-423c-81e7-82cab31b7598.jpg','

商品介绍加载中...

',129,129,1000,'',0,0,'2019-09-18 13:21:35',0,'2020-10-13 10:41:59'), (10182,'荣耀原装三键线控带麦半入耳式耳机AM116(尊爵版)适用于华为荣耀手机','新蜂精选',0,'/goods-img/6113a562-f3f1-408c-9b0d-78a84407caf7.jpg','/goods-img/6113a562-f3f1-408c-9b0d-78a84407caf7.jpg','

商品介绍加载中...

',69,49,1000,'',0,0,'2019-09-18 13:21:35',0,'2020-10-13 10:41:59'), (10183,'诺基亚(NOKIA)BH-705 银白色 5.0真无线蓝牙耳机迷你运动跑步音乐商务入耳式安卓苹果手机蓝牙耳机','新蜂精选',0,'/goods-img/abb13d3a-3445-4b26-b8e9-44cbec227b5d.jpg','/goods-img/abb13d3a-3445-4b26-b8e9-44cbec227b5d.jpg','

商品介绍加载中...

',499,499,1000,'',0,0,'2019-09-18 13:21:35',0,'2020-10-13 10:41:59'), (10184,'华为耳机原装半入耳式有线mate9p10plus8x荣耀v20v10nova2s9iv9p9play 【标准版】华为AM115 白色-热卖款','新蜂精选',0,'/goods-img/fac9c3e9-4843-46d1-8668-7e2eac17ccf2.jpg','/goods-img/fac9c3e9-4843-46d1-8668-7e2eac17ccf2.jpg','

商品介绍加载中...

',69,39,1000,'',0,0,'2019-09-18 13:21:35',0,'2020-10-13 10:41:59'), (10185,'Beats X 蓝牙无线','入耳式耳机 带麦可通话 -桀骜黑红(十周年版) MRQA2PA/A',0,'/goods-img/25910a34-e026-4954-87b0-c379999e1dd0.jpg','/goods-img/25910a34-e026-4954-87b0-c379999e1dd0.jpg','

商品介绍加载中...

',1168,799,1000,'',0,0,'2019-09-18 13:21:35',0,'2020-10-13 10:41:59'), (10186,'华为( HUAWEI) 华为无线耳机','真无线蓝牙耳机 双耳蓝牙音乐耳机 Freebuds 2 无线耳机 陶瓷白',0,'/goods-img/adf8cbc2-ccb9-408a-96d0-553848e111e9.jpg','/goods-img/adf8cbc2-ccb9-408a-96d0-553848e111e9.jpg','

商品介绍加载中...

',899,799,1000,'',0,0,'2019-09-18 13:21:35',0,'2020-10-13 10:41:59'), (10187,'【自营仓次日达】moloke真无线蓝牙耳机双耳适用于苹果华为小米 运动跑步入耳式oppo迷你商务耳机 【1:1尊享版】自动弹窗+无线充电+可触控(热卖)','新蜂精选',51,'/goods-img/1e5645d1-24cb-48eb-9aaa-f729fa0db195.jpg','/goods-img/1e5645d1-24cb-48eb-9aaa-f729fa0db195.jpg','

商品介绍加载中...

',359,199,1000,'',0,0,'2019-09-18 13:21:35',0,'2020-10-13 10:41:59'), (10188,'Beats Powerbeats Pro','完全无线高性能耳机 真无线蓝牙运动耳机 象牙白',0,'/goods-img/e028c016-6793-49a3-8b0f-d0102a415d21.jpg','/goods-img/e028c016-6793-49a3-8b0f-d0102a415d21.jpg','

商品介绍加载中...

',1888,1888,1000,'',0,0,'2019-09-18 13:21:35',0,'2020-10-13 10:41:59'), (10189,'纽曼(Newmine)NM-LK06 全兼容线控音乐手机耳机 白色','新蜂精选',0,'/goods-img/0b02244f-6908-4ccb-a9d2-ccb5a462e30e.jpg','/goods-img/0b02244f-6908-4ccb-a9d2-ccb5a462e30e.jpg','

商品介绍加载中...

',9,9,1000,'',0,0,'2019-09-18 13:21:35',0,'2020-10-13 10:41:59'), (10190,'索尼(SONY)重低音立体声耳机MDR-XB55AP 黑色','新蜂精选',0,'/goods-img/eec7b009-a9ff-45cd-a7be-4051eb7b3c22.jpg','/goods-img/eec7b009-a9ff-45cd-a7be-4051eb7b3c22.jpg','*黑色实物偏灰,请以实物为准 Bass Booster低音增强器技术可呈现紧实深邃低频。 12 毫米驱动单元和110dB/mW 的高灵敏度,呈现高质感音效。 人体工学设计的倾斜入耳方式,让耳塞能够深入耳朵内部,呈现出色的隔音效果,同时带来舒适的佩戴感和高音质的享受。 耳塞能够深入耳朵内部,呈现出色的隔音效果,同时为您带来舒适的佩戴感和高音质的享受。 采用混合两种硬度硅胶的耳塞套: 核心部分使用硬质材料保持音质,减少因耳塞变形导致的声音失真; 外围部分柔软材料提高了耳塞密闭性,让您能长时间舒适佩戴。 *线控的可用性及操作因智能手机而异 耳机线表面细小沟壑,减少容易引起缠绕的摩擦,使导线不容易纠结在一起,方便欣赏音乐和携带。 防缠绕耳机线 盲点设计 便携袋 防尘滤网 导线滑块 4种尺寸耳塞套 摘下耳机的耳塞套,可见保护单元的网罩,用来防止异物和灰尘堵塞单元,使耳机经久耐听。 在左耳外壳和耳机线的连接处设有浮点,凭手指触摸就能判别左右耳,方便操作。 随机附赠收纳袋一只,保护你心爱的耳机。 利用导线滑块来调整左右耳机线的长度,也能够减少收纳耳机时容易出现的缠线现象 提供4对不同尺寸(SS、S、M、L)的耳塞套(M号出厂时已安装至耳机上),根据你的耳洞大小自由更换,获得良好的隔音效果,佩戴舒适。 ● 立体声耳机 ● 混合硅胶耳塞(SS/S/M/L 每种尺寸2个) *M号出厂时安装至本耳机。 ● 便携袋(×1) *EXTRA BASS 和 EXTRABASS 是索尼公司的商标',229,185,1000,'',0,0,'2019-09-18 13:21:35',0,'2020-10-13 10:41:59'), (10191,'索尼(SONY)WI-1000X Hi-Res颈挂式 入耳式','无线蓝牙耳机 高音质降噪耳机 手机通话 黑色',0,'/goods-img/1c4adfba-f2f4-4ab3-8520-c28b0a437b7b.jpg','/goods-img/1c4adfba-f2f4-4ab3-8520-c28b0a437b7b.jpg','

商品介绍加载中...

',2399,1499,1000,'',0,0,'2019-09-18 13:21:35',0,'2020-10-13 10:41:59'), (10192,'小米耳机 圈铁Pro 入耳式有线运动音乐耳机耳麦','新蜂精选',51,'/goods-img/b1530f7f-d286-4eb1-8d2b-3c2a74fa9f06.jpg','/goods-img/b1530f7f-d286-4eb1-8d2b-3c2a74fa9f06.jpg','使用双动圈 + 动铁 三单元发声 / 均衡自然声音 高保真石墨烯振膜 / 25 道工序打磨 / 弹力磨砂线材 Pro 小米圈铁耳机 孕育万物的天空和大地,时刻传达着声音的释放与组合,更是寻找灵感的源头,鸟鸣、流水、雷响、风啸不同的声音互相交融,共同演奏出自然的本真。 小米圈铁耳机 Pro 使用双“动圈”单元+“动铁”单元,将三个单元共同融入到同一个耳机中,双“动圈”的醇厚低音,让声音更加扎实稳重,石墨烯材料的加入,则让声音的细节更为丰富。“动铁”的高音透亮,稳定自然,感受三频均衡的本色声音。随着声音的流淌,仿佛置身自然,听见这些细节,让声音一开始就感动内心。 双动圈+动铁,三单元发声,听见更多细节 为了可以真正实现高、中、低三频均衡,小米圈铁耳机 Pro  加入了双“动圈”单元,大动圈负责中低频,小动圈负责高频。在“动铁”单元的配合下,耳机的低频下潜深,中频声音扎实,而高频的细节展现更为丰富。那些刚刚好的声音,听在耳里,都在心里。 三频更均衡,声音更自然 我们听到的绝大多数乐器、人声,都在中低频段。为了让这部分声音更均衡、有感染力,我们都交由采用了石墨烯振膜的双动圈单元来负责,中低频更扎实,兼具丰富细节表现力。 石墨烯是目前自然界已知材料中轻薄、强度更高的材料,对声音的传导速度快,将它用作振膜材质,高频延展性能更好,细节丰富,声音清澈自然,更富穿透力。同时强度又是钢铁的100倍, 可以尽可能还原出电流信号, 真正发出高保真的好声音。 石墨烯振膜,让双动圈更有实力 小米圈铁耳机 Pro 的“动铁”单元依然采用自主研发的 \"衔铁+驱动杆\" 结构,让声音细腻真实,更为稳定,在电容分频器的作用下,让高中低音衔接更好,失真更少。不论当你听何种音乐,细腻的感情都会被准确还原,听每首歌就像读每个故事,时刻感动自己。 动铁单元设计,高频解析好,细节不失真 好的音乐人将情感与生活用真实的方式,转化为音乐传递给每个人,每首歌都是一个故事,铭刻在各自的记忆中,为了让故事更好的表达,小米圈铁耳机 Pro 在科学客观调音的基础上,再次邀请到荣获 4 次格莱美大奖的 Luca Bignardi,为小米圈铁耳机 Pro 进行主观调音,为的就是让每个喜爱音乐的人能够真切的感受到每一个故事,跟随内心,娓娓道来... 多种科学调音,让声音更鲜活,更温暖 当耳机真正为声音服务时,设计将不再只是修饰耳机外观的道具,它将会成为辅助声音的一部分,小米圈铁耳机 Pro 采用圆润的设计风格,45° 斜角入耳设计,在满足舒适的同时更保证了声音的完整呈现。精密金属音腔设计,让音乐沉于耳畔,更有声音质感,弹力 TPE 磨砂线材的选用,让耳机线更为坚固耐用,确保耳机长久使用。一副好耳机,让声音和外表一起美好。 全新的外观设计,和声音一起美好 好的设计需要灵感,而灵感源于生活,为了锁住声音的灵感,小米圈铁耳机 Pro 将耳塞设计成45°斜角式入耳,贴合耳道,满足佩戴舒适感的同时尽可能减少外界声音干扰,毫无保留地听自己爱的音乐。 45°斜角入耳,舒适佩戴 小米圈铁耳机 Pro 的线控麦克风从耳机整体设计风格出发,金属磨砂弹头造型,精致小巧,指压按键圆润舒适,听歌的同时,更能感知指尖上的金属质感。 小米圈铁耳机 Pro 的耳机线材选取 TPE 材质,作为一种具有橡胶的高弹性材质, 触感柔软、耐温等特性,用它做成耳机线,将更为抗拉、耐用并且不易缠绕。让好音乐的陪伴更长久。 小米圈铁耳机 Pro 的耳塞选取奶嘴级硅胶材质,触感柔软顺滑,减少了耳塞对皮肤的刺激,让肌肤倍感亲密,同时提供四对不同尺寸的耳塞套,让佩戴者根据不同需求选择,带上它,向自己喜爱的音乐问好! 用匠心打磨每一件产品,即使过程艰难复杂,也依然充满斗志,小米圈铁耳机 Pro 的诞生过程就是这样。25 道工序打造的金属音腔,每一处细节都精心打磨,一体成型钻石切割、细密 CD 纹雕刻、锆石喷砂、阳极氧化,千锤百炼,不放过每个细节,将金属打磨成入耳的艺术品,这就是小米圈铁耳机 Pro 对音乐执着,对好产品更要执着。 小米圈铁耳机 Pro 是铝合金音腔,采用了 CNC 钻石切割一刀成型工艺,加工精度高达0.01mm,这种工艺在对铝合金加工前都要进行工艺分析,选择合适的刀具及切削用量,将打磨成型,让耳机具有更细腻润泽的手感。 小米圈铁耳机 Pro 运用精密的 CD 纹处理,纹理细至 0.14mm,散发金属光泽,就像耳机的指纹一样。如此的精密打磨,只为让小米圈铁耳机 Pro 更具质感,让金属更光辉熠熠。 选用精细锆石喷砂,赋予小米圈铁耳机 Pro 细致均匀的外观,有效保证了耳机表面硬度,不易刮伤。出厂时,会在小米圈铁耳机 Pro 表层增加阳极处理,保证了美观程度和耐磨性,6μ的阳极厚度,坚固、耐磨,做传达好声音的艺术品。 拥有超过 700 项高于行业标准的苛刻测试,每一种测试都见证了小米圈铁耳机 Pro 的高品质, 从音乐品质到设计创新,再到匠心工艺,集合好耳机的所有亮点,都只为带给用户更好的音乐体验和使用感受,好的声音,一定需要千锤百炼 。',149,149,1000,'',0,0,'2019-09-18 13:21:35',0,'2020-10-13 10:41:59'), (10193,'Bose QuietControl 30','无线耳机 QC30耳塞式蓝牙降噪耳麦',0,'/goods-img/02cf272e-9062-4d4b-8b7f-7058f0472efa.jpg','/goods-img/02cf272e-9062-4d4b-8b7f-7058f0472efa.jpg','

商品介绍加载中...

',2498,2498,1000,'',0,0,'2019-09-18 13:21:35',0,'2020-10-13 10:41:59'), (10194,'Beats Solo3 Wireless','头戴式 蓝牙无线耳机 手机耳机 游戏耳机 - 桀骜黑红(十周年版) MRQC2PA/A',0,'/goods-img/af77eaba-fd00-4ec8-b0e6-928372a0741d.jpg','/goods-img/af77eaba-fd00-4ec8-b0e6-928372a0741d.jpg','

商品介绍加载中...

',2268,1698,1000,'',0,0,'2019-09-18 13:21:35',0,'2020-10-13 10:41:59'), (10195,'索尼 WH-1000XM3 头戴式耳机','高解析度无线蓝牙降噪(触控面板 智能降噪 长久续航)黑色',20,'/goods-img/0dc503b2-90a2-4971-9723-c085a1844b76.jpg','http://localhost:28089/goods-img/0dc503b2-90a2-4971-9723-c085a1844b76.jpg','

商品介绍加载中...

',2899,2599,987,'智能降噪 长久续航',0,0,'2019-09-18 13:21:35',0,'2020-10-13 10:41:59'), (10196,'雷蛇 Razer 北海巨妖标准版X','北海巨妖标准版升级款 头戴式游戏耳机 电竞耳麦 7.1 电脑手机耳机 黑色',0,'/goods-img/0cc81546-1408-4140-af95-0341a7778b6c.jpg','/goods-img/0cc81546-1408-4140-af95-0341a7778b6c.jpg','产品信息Product Information 产品规格Product Specifications 品牌介绍Brand Introduction 注意事项Warning & Caution 雷蛇产品在出厂时会进行检测,脚贴及USB接口处如有轻微划痕属于正常测试痕迹。 RAZER关于划痕的注意事项: 以上数据图片均为官方测试环境下结果,因使用环境/设备不同会存在一定的差异,仅供参考,数据请以实际为准!  1. 产品实物与外包装上的SN(序列号)必须一致; 2. 产品外包装不能严重破损,盒内的相关配件要齐全,不能有缺失; 3. 不能有明显的人为破损(表面有明显的人为划痕,使用及存在拆卸的痕迹); 4. 防伪标签不得撕开或损毁。 RAZER关于7天无理由退换货的注意事项: ',349,299,1000,'',0,0,'2019-09-18 13:21:35',0,'2020-10-13 10:41:59'), (10197,'森海塞尔(Sennheiser)MomentumTrueWireless 真无线蓝牙hifi发烧入耳式耳机 蓝牙5.0','黑色',0,'/goods-img/768e79e1-e875-4691-855d-262346451d22.jpg','/goods-img/768e79e1-e875-4691-855d-262346451d22.jpg','聆听带来改变 真     无     线     蓝     牙     HiFi     耳     机 MOMENTUM 真无线 懂你所需 全新的 MOMENTUM 真无线耳机,高品质的声音质量传承 MOMENTUM 品质,成为一款具有重要技术成就的新产品。 这款性能优异的蓝牙耳机融合音频质量、佩戴舒适性和精致设计及工艺。 全新的 MOMENTUM 透明聆听功能 防水防泼溅 电池使用时长 (4+8小时) 精雕细琢 经典优雅 高品质声音质量 智能降噪 智能触控操作 支持蓝牙5.0技术 智能触控操作 支持蓝牙 5.0技术 MOMENTUM真无线耳机采用Sennheiser发烧级别7毫米动圈驱动单元,可确保饱满的立体声效果,带来高保真音质,为苛刻的听者带来出色的高保真度。 高品质声音质量 两侧触摸区域都有单独的控制功能,您可以轻松使用右耳耳机语音访问智能助手(如苹果 Siri或Google智能助手)。 轻轻点击或滑动触摸界面,使用自然语音命令即可播放音乐、接听电话。 支持蓝牙5.0技术及编解码技术(包括AAC、Qualcomm apt-XTM和apt-X低延迟),这款耳机带来出众的连接稳定性和音频流畅性。 真正的无线体验 轻松适配周围环境 MOMENTUM 真无线耳机让你更好地感知外部环境,透明聆听让你能听到周围的环境声,从而更好地感知周围的环境,不需要摘掉耳机就能融入到自然的交谈之中。甚至在嘈杂的环境中,电话呼叫和语音交互也能够通过双话筒波束成形技术得以实现。 智能交互 通过自动开启/关闭和智能暂停功能,可以检测到耳机何时被收起来或者不使用,从而节约能源。 你的世界由你把控 通过双击右耳耳机开/关透明聆听功能 打开透明聆听=接收周围环境音 关闭透明聆听=物理降噪模式,不接收周围音 不需要摘掉耳机就可轻松地与周围人进行交谈。 4种尺寸的耳垫可选,均符合人体工程学设计,防水防泼溅,能够满足用户舒适佩戴的需求。 个性定制舒适体验 MOMENTUM真无线拥有4小时电池续航时间,可通过其带有集成电源的小巧耳机盒进行充电,从而享受长达12(4+8)小时的全天聆听乐趣,并满足未来所需。 镀金充电接触点 可磁性吸附到充电盒上 高保真7毫米动圈驱动单元 带来出色的声音重放 金属镭射表面 具有触控功能 多色 LED指示灯 用于语音信号拾取和透明聆听功能的话筒 舒适的入耳式 硅胶耳垫 便捷充电盒持久续航 注重细节、富于美感,这款小巧、 靓丽而轻盈的耳机是技术与艺术的 结合。它既是声音重放技术的成就,更是你耳畔精美的配饰。 质感黑色外壳,闪烁的金属镭射表面,镀金的充电接触点——时尚与功能融合于标志性的设计之中,带来优雅和实用感。 Sennheiser智能控制 MOMENTUM 真无线耳机提供了更为智能和个性化的体验,可以通过新款Sennheiser智能控制应用进行优化,根据个人喜好,利用内置音频EQ对声音进行微调。免费下载,兼容iOS 版本 11.0 及以上版本和Android 版本 7.0 及以上版本 ,简便直观的控制界面,为您的耳机提供个性化的配置和升级等功能。 APP 下载方法 Android 版本 7.0 及以上版本 打开链接下载APP https://share.weiyun.com/54byqjn iOS 版本 11.0 及以上版本 打开APP Store搜索 Sennheiser smart control 下载APP',2399,2399,1000,'',0,0,'2019-09-18 13:21:35',0,'2020-10-13 10:41:59'), (10198,'Bose SoundSport Free','真无线蓝牙耳机--黑色 运动耳机 防掉落耳塞',0,'/goods-img/d3370c50-e853-4546-a032-35073eb192ff.jpg','/goods-img/d3370c50-e853-4546-a032-35073eb192ff.jpg','

商品介绍加载中...

',1699,1699,1000,'',0,0,'2019-09-18 13:21:35',0,'2020-10-13 10:41:59'), (10199,'华为原装降噪有线手机耳机Mate9 10P9P10Plus荣耀V9V10PlayNova2s9i8x 【送耳机收纳包】AM115半入耳式耳机-经典热卖款','新蜂精选',0,'/goods-img/0cff5ace-7ab9-43a7-91fe-fb3550829577.jpg','/goods-img/0cff5ace-7ab9-43a7-91fe-fb3550829577.jpg','

商品介绍加载中...

',99,39,1000,'',0,0,'2019-09-18 13:21:35',0,'2020-10-13 10:41:59'), (10200,'Apple 采用Lightning/闪电接头的 EarPods','耳机',0,'/goods-img/7b8bcf01-0abe-4155-b1f4-e57a6b8fc36a.jpg','/goods-img/7b8bcf01-0abe-4155-b1f4-e57a6b8fc36a.jpg','

商品介绍加载中...

',223,223,1000,'',0,0,'2019-09-18 13:21:35',0,'2020-10-13 10:41:59'), (10201,'迪奥(Dior)烈艳蓝金唇膏滋润999# 3.5g 经典正红色','(口红 保湿滋润 气质显白 不挑皮) (新老包装随机)',0,'/goods-img/6b0bd268-40b1-4abf-a19b-95df7cb4d722.jpg','/goods-img/6b0bd268-40b1-4abf-a19b-95df7cb4d722.jpg','

商品介绍加载中...

',500,315,1000,'',0,0,'2019-09-18 13:24:47',0,'2020-10-13 10:41:59'), (10202,'迪奥(Dior)烈艳蓝金唇膏-哑光999# 3.5g 传奇红(口红','雾面质地 显色持久 显白 正红色 李佳琦推荐)',86,'/goods-img/d8d4ac7e-7189-459a-aef2-7116f723cb0b.jpg','/goods-img/d8d4ac7e-7189-459a-aef2-7116f723cb0b.jpg','

商品介绍加载中...

',400,315,1000,'',0,0,'2019-09-18 13:24:47',0,'2020-10-13 10:41:59'), (10203,'海囤全球 魅可(MAC)经典唇膏 子弹头口红3g','Chili 秀智色/小辣椒色',86,'/goods-img/18aca3b8-d024-47d3-a971-fb51d374b1ae.jpg','/goods-img/18aca3b8-d024-47d3-a971-fb51d374b1ae.jpg','

商品介绍加载中...

',170,155,1000,'',0,0,'2019-09-18 13:24:47',0,'2020-10-13 10:41:59'), (10204,'卡姿兰(Carslan)轻甜唇爱随心盒1.4g*4(13#暧昧 16#炽烈 18#嫉妒','19#欲望 唇盒 口红 七夕礼物 情人节礼物)',0,'/goods-img/44c8198e-f63a-45e0-8eff-789338de65f8.jpg','/goods-img/44c8198e-f63a-45e0-8eff-789338de65f8.jpg','关联销售入口 1 (1) 商品介绍加载中...',99,89,1000,'',0,0,'2019-09-18 13:24:47',0,'2020-10-13 10:41:59'), (10205,'【联名限量版】MANSLY口红套装中国风口红情人节女朋友生日礼物唇釉彩妆女磁扣锦绣红妆口红礼盒彩妆 锦绣红妆口红礼盒(6支)','新蜂精选',86,'/goods-img/c081314e-8f67-44f9-a27e-aad6c3f29343.jpg','/goods-img/c081314e-8f67-44f9-a27e-aad6c3f29343.jpg','

商品介绍加载中...

',295,295,995,'',0,0,'2019-09-18 13:24:47',0,'2020-10-13 10:41:59'), (10206,'迪奥(Dior)滋润999礼盒套装(烈艳蓝金999#3.5g 经典正红色+香氛小样1ml*3+礼盒)(小样和礼盒款式随机)','新蜂精选',0,'/goods-img/39c69481-6d13-4d84-bc1e-7dca612667f0.jpg','/goods-img/39c69481-6d13-4d84-bc1e-7dca612667f0.jpg','

商品介绍加载中...

',379,379,1000,'',0,0,'2019-09-18 13:24:47',0,'2020-10-13 10:41:59'), (10207,'圣罗兰(YSL)莹亮纯魅唇膏12#(圆管口红)4.5g 斩男色','新蜂精选',86,'/goods-img/b4335e82-c9e1-4264-92e4-e324a601fedb.jpg','/goods-img/b4335e82-c9e1-4264-92e4-e324a601fedb.jpg','

商品介绍加载中...

',320,320,1000,'',0,0,'2019-09-18 13:24:47',0,'2020-10-13 10:41:59'), (10208,'圣罗兰(YSL)纯口红1#(正红色)3.8g','新蜂精选',86,'/goods-img/57d0bf26-0a0c-4027-8a2b-deeaa29905ee.jpg','/goods-img/57d0bf26-0a0c-4027-8a2b-deeaa29905ee.jpg','

商品介绍加载中...

',350,320,1000,'',0,0,'2019-09-18 13:24:47',0,'2020-10-13 10:41:59'), (10209,'纪梵希高定香榭天鹅绒唇膏306#(小羊皮口红 法式红 雾面哑光','持久锁色)新老包装随机发货',86,'/goods-img/f30bd8cb-aadd-43aa-8615-2c4795ee7f5f.jpg','/goods-img/f30bd8cb-aadd-43aa-8615-2c4795ee7f5f.jpg','

商品介绍加载中...

',355,355,1000,'',0,0,'2019-09-18 13:24:47',0,'2020-10-13 10:41:59'), (10210,'【联名款】MANSLY口红套装红鸾心动口红礼盒中国风开运红情人节女朋友生日礼物唇釉颐和园同款彩妆口红 红鸾心动口红礼盒(6支)','新蜂精选',86,'/goods-img/f128ad98-fe4d-4264-96e3-6393b6cc98f1.jpg','/goods-img/f128ad98-fe4d-4264-96e3-6393b6cc98f1.jpg','

商品介绍加载中...

',195,195,1000,'',0,0,'2019-09-18 13:24:47',0,'2020-10-13 10:41:59'), (10211,'海囤全球 迪奥(Dior)烈艳蓝金唇膏 口红','3.5g 999号 正红色',86,'/goods-img/8fcdb86b-e826-4c1b-af3c-33a9d590c4b0.jpg','/goods-img/8fcdb86b-e826-4c1b-af3c-33a9d590c4b0.jpg','

商品介绍加载中...

',410,258,1000,'',0,0,'2019-09-18 13:24:47',0,'2020-10-13 10:41:59'), (10212,'圣罗兰(YSL)纯口红13#(正橘色)3.8g','新蜂精选',86,'/goods-img/53a4a428-8ca2-4d19-937d-15d18f324237.jpg','/goods-img/53a4a428-8ca2-4d19-937d-15d18f324237.jpg','

商品介绍加载中...

',320,320,1000,'',0,0,'2019-09-18 13:24:47',0,'2020-10-13 10:41:59'), (10213,'海囤全球 魅可(MAC)磨砂系列 雾面丝绒哑光子弹头口红','3g 316 devoted to chili 泫雅色',86,'/goods-img/2da55bd1-046f-4ac2-b1b9-56ab00bb9db1.jpg','/goods-img/2da55bd1-046f-4ac2-b1b9-56ab00bb9db1.jpg','

商品介绍加载中...

',249,165,1000,'',0,0,'2019-09-18 13:24:47',0,'2020-10-13 10:41:59'), (10214,'【情人礼物】香奈儿Chanel 口红/唇膏可可小姐水亮/丝绒系列润唇保湿口红配玫瑰花礼盒 丝绒系列','43#斩男色',86,'/goods-img/247722ea-c87a-4283-806c-bc9fe57f2253.jpg','/goods-img/247722ea-c87a-4283-806c-bc9fe57f2253.jpg','

商品介绍加载中...

',299,298,1000,'',0,0,'2019-09-18 13:24:47',0,'2020-10-13 10:41:59'), (10215,'迪奥(Dior)口红礼盒套装(烈艳蓝金唇膏哑光#999 3.5g正红色+香氛小样1ml*3随机+随机礼盒样式)','新蜂精选',86,'/goods-img/ab1a0ced-954c-4857-92f4-f7c833d9d54a.jpg','/goods-img/ab1a0ced-954c-4857-92f4-f7c833d9d54a.jpg','

商品介绍加载中...

',379,379,1000,'',0,0,'2019-09-18 13:24:47',0,'2020-10-13 10:41:59'), (10216,'圣罗兰(YSL)纯口红52# 3.8g','新蜂精选',86,'/goods-img/1eefadae-5f62-4abd-b283-077e7b6d9193.jpg','/goods-img/1eefadae-5f62-4abd-b283-077e7b6d9193.jpg','

商品介绍加载中...

',340,320,1000,'',0,0,'2019-09-18 13:24:47',0,'2020-10-13 10:41:59'), (10217,'海囤全球 汤姆福特 TOM','FORD TF口红 经典黑金唇膏 3g 16 SCARLET ROUGE 复古番茄红',0,'/goods-img/da12f5cf-2728-446a-a3bd-b78baf7056ff.jpg','/goods-img/da12f5cf-2728-446a-a3bd-b78baf7056ff.jpg','

商品介绍加载中...

',429,375,1000,'',0,0,'2019-09-18 13:24:47',0,'2020-10-13 10:41:59'), (10218,'迪奥(Dior)烈艳蓝金口红唇膏 028# 3.5g','珊瑚红 (滋润保湿 持久显色 粉嫩少女 摩洛哥王妃 幸运色)',86,'/goods-img/7030b9b6-b650-4d9d-9446-e27dab8afa1f.jpg','/goods-img/7030b9b6-b650-4d9d-9446-e27dab8afa1f.jpg','

商品介绍加载中...

',400,315,1000,'',0,0,'2019-09-18 13:24:47',0,'2020-10-13 10:41:59'), (10219,'迪奥(Dior)烈艳蓝金唇膏520# 3.5g 玫瑰红(口红','缎光 滋润保湿 长效持妆 玫红色 斩男色 告白色 粉红色)',86,'/goods-img/96a91f11-e634-4e28-be13-db8b4732463e.jpg','/goods-img/96a91f11-e634-4e28-be13-db8b4732463e.jpg','

商品介绍加载中...

',360,315,1000,'',0,0,'2019-09-18 13:24:47',0,'2020-10-13 10:41:59'), (10220,'海囤全球 迪奥(Dior)烈艳蓝金唇膏 口红','3.5g 999号 哑光-经典正红',86,'/goods-img/fe048831-384d-46b2-beec-5549f7902c11.jpg','/goods-img/fe048831-384d-46b2-beec-5549f7902c11.jpg','

商品介绍加载中...

',410,255,1000,'',0,0,'2019-09-18 13:24:47',0,'2020-10-13 10:41:59'), (10221,'欧莱雅(LOREAL)纷泽滋润唇膏RC301复古魅红3.7g(金管 口红女 滋润显色)','新蜂精选',86,'/goods-img/b7495e02-fc4c-417a-8101-ccfc75a5a475.jpg','/goods-img/b7495e02-fc4c-417a-8101-ccfc75a5a475.jpg','品牌介绍Brand Description         巴黎欧莱雅通过将科技和美丽的结合,不断谋求创新、研发新的产品配方,以合理的价格,为消费者提供品质的产品和服务。自1907年安全合成染发剂的诞生,如今巴黎欧莱雅的产品已从染发剂扩展到了护肤、彩妆等诸多领域,在中国,巴黎欧莱雅的五大产品线为护肤系列、彩妆系列、家用染发系列、洗护发系列及男士护肤系列。为了将美的产品融于美的文化、艺术、理念,将“从指尖到发梢”的美丽带给全世界的人们,巴黎欧莱雅在全世界范围精心选择各行业明星,组成“梦之队”来见证巴黎欧莱雅的实力,从各个不同的角度来讲述巴黎欧莱雅美丽无疆界的气势,并使“巴黎欧莱雅,你值得拥有!”“Because you are worth it!”的美丽概念成为一种文化!',135,99,1000,'',0,0,'2019-09-18 13:24:47',0,'2020-10-13 10:41:59'), (10222,'阿玛尼(Armani) 口红女士唇釉 生日礼物/表白礼物','红管#405番茄红 【李佳琪推荐omg】',86,'/goods-img/75fdac25-1cfa-4a9b-957d-805ac706f32c.jpg','/goods-img/75fdac25-1cfa-4a9b-957d-805ac706f32c.jpg','

商品介绍加载中...

',366,285,1000,'',0,0,'2019-09-18 13:24:47',0,'2020-10-13 10:41:59'), (10223,'美宝莲(MAYBELLINE)绝色持久唇膏雾感哑光系列R09PM 3.9g(女皇色口红新老包装)','新蜂精选',86,'/goods-img/1055e30e-3d98-4dca-8b79-8d0b5a09a37b.jpg','/goods-img/1055e30e-3d98-4dca-8b79-8d0b5a09a37b.jpg','

商品介绍加载中...

',122,106,1000,'',0,0,'2019-09-18 13:24:47',0,'2020-10-13 10:41:59'), (10224,'【专柜正品】迪奥999Dior口红唇膏烈艳蓝金 哑光滋润520/888/999送礼礼品套装 烈艳蓝金','844#橘红色赠礼盒礼袋',86,'/goods-img/7b52a7bc-0ecf-41c4-b079-d162511c9530.jpg','/goods-img/7b52a7bc-0ecf-41c4-b079-d162511c9530.jpg','

商品介绍加载中...

',339,260,1000,'',0,0,'2019-09-18 13:24:47',0,'2020-10-13 10:41:59'), (10225,'迪奥(Dior)烈艳蓝金唇膏滋润999# 3.5g 经典正红色','(口红 保湿滋润 气质显白 不挑皮) (新老包装随机)',0,'/goods-img/bb05b83f-bb91-4300-b78f-23986ba8c0dd.jpg','/goods-img/bb05b83f-bb91-4300-b78f-23986ba8c0dd.jpg','

商品介绍加载中...

',500,315,1000,'',0,0,'2019-09-18 13:25:08',0,'2020-10-13 10:41:59'), (10226,'迪奥(Dior)烈艳蓝金唇膏-哑光999# 3.5g 传奇红(口红','雾面质地 显色持久 显白 正红色 李佳琦推荐)',86,'/goods-img/67280dcf-bf32-49c1-b99b-9d86bb2ffaac.jpg','/goods-img/67280dcf-bf32-49c1-b99b-9d86bb2ffaac.jpg','

商品介绍加载中...

',400,315,1000,'',0,0,'2019-09-18 13:25:08',0,'2020-10-13 10:41:59'), (10227,'海囤全球 魅可(MAC)经典唇膏 子弹头口红3g','Chili 秀智色/小辣椒色',86,'/goods-img/2b678c5d-820c-4174-bc0c-5a65ff9501b6.jpg','/goods-img/2b678c5d-820c-4174-bc0c-5a65ff9501b6.jpg','

商品介绍加载中...

',170,155,1000,'',0,0,'2019-09-18 13:25:08',0,'2020-10-13 10:41:59'), (10228,'卡姿兰(Carslan)轻甜唇爱随心盒1.4g*4(13#暧昧 16#炽烈 18#嫉妒','19#欲望 唇盒 口红 七夕礼物 情人节礼物)',0,'/goods-img/3f513cd6-bb5f-407d-8550-24550873d83b.jpg','/goods-img/3f513cd6-bb5f-407d-8550-24550873d83b.jpg','关联销售入口 1 (1) 商品介绍加载中...',99,89,1000,'',0,0,'2019-09-18 13:25:08',0,'2020-10-13 10:41:59'), (10229,'【联名限量版】MANSLY口红套装中国风口红情人节女朋友生日礼物唇釉彩妆女磁扣锦绣红妆口红礼盒彩妆 锦绣红妆口红礼盒(6支)','新蜂精选',86,'/goods-img/d82ba7f0-6c92-4254-bfb2-71b3f8b1dfda.jpg','/goods-img/d82ba7f0-6c92-4254-bfb2-71b3f8b1dfda.jpg','

商品介绍加载中...

',295,295,1000,'',0,0,'2019-09-18 13:25:08',0,'2020-10-13 10:41:59'), (10230,'迪奥(Dior)滋润999礼盒套装(烈艳蓝金999#3.5g 经典正红色+香氛小样1ml*3+礼盒)(小样和礼盒款式随机)','新蜂精选',0,'/goods-img/f6b1195a-3231-4e81-a676-866ee838748f.jpg','/goods-img/f6b1195a-3231-4e81-a676-866ee838748f.jpg','

商品介绍加载中...

',379,379,1000,'',0,0,'2019-09-18 13:25:08',0,'2020-10-13 10:41:59'), (10231,'圣罗兰(YSL)莹亮纯魅唇膏12#(圆管口红)4.5g 斩男色','新蜂精选',86,'/goods-img/359bb052-5fea-4390-bbe6-4cb9e1c19273.jpg','/goods-img/359bb052-5fea-4390-bbe6-4cb9e1c19273.jpg','

商品介绍加载中...

',320,320,1000,'',0,0,'2019-09-18 13:25:08',0,'2020-10-13 10:41:59'), (10232,'圣罗兰(YSL)纯口红1#(正红色)3.8g','新蜂精选',86,'/goods-img/a42498e5-d912-447b-9360-0659d2d55c42.jpg','/goods-img/a42498e5-d912-447b-9360-0659d2d55c42.jpg','

商品介绍加载中...

',350,320,1000,'',0,0,'2019-09-18 13:25:08',0,'2020-10-13 10:41:59'), (10233,'纪梵希高定香榭天鹅绒唇膏306#','(小羊皮口红 法式红 雾面哑光 持久锁色)新老包装随机发货',86,'/goods-img/04949c0e-87df-445b-96dd-29e7fc69f734.jpg','http://localhost:28089/goods-img/04949c0e-87df-445b-96dd-29e7fc69f734.jpg','

商品介绍加载中...

',355,355,998,'雾面哑光 持久锁色',0,0,'2019-09-18 13:25:08',0,'2020-10-13 10:41:59'), (10234,'【联名款】MANSLY口红套装红鸾心动口红礼盒中国风开运红情人节女朋友生日礼物唇釉颐和园同款彩妆口红 红鸾心动口红礼盒(6支)','新蜂精选',86,'/goods-img/a9cd71ad-2db0-4876-9ead-c51233040220.jpg','/goods-img/a9cd71ad-2db0-4876-9ead-c51233040220.jpg','

商品介绍加载中...

',195,195,1000,'',0,0,'2019-09-18 13:25:08',0,'2020-10-13 10:41:59'), (10235,'海囤全球 迪奥(Dior)烈艳蓝金唇膏 口红','3.5g 999号 正红色',86,'/goods-img/49d2acf7-55e5-4293-a7da-5929740e1168.jpg','/goods-img/49d2acf7-55e5-4293-a7da-5929740e1168.jpg','

商品介绍加载中...

',410,258,1000,'',0,0,'2019-09-18 13:25:08',0,'2020-10-13 10:41:59'), (10236,'圣罗兰(YSL)纯口红13#(正橘色)3.8g','新蜂精选',86,'/goods-img/b0142d40-6adb-4d64-b5b2-6e4a34656990.jpg','/goods-img/b0142d40-6adb-4d64-b5b2-6e4a34656990.jpg','

商品介绍加载中...

',320,320,1000,'',0,0,'2019-09-18 13:25:08',0,'2020-10-13 10:41:59'), (10237,'MAC 雾面丝绒哑光子弹头口红','磨砂系列 3g 316 devoted to chili 泫雅色',86,'/goods-img/1930d79b-88bd-4c5c-8510-0697c9ad2578.jpg','http://localhost:28089/goods-img/1930d79b-88bd-4c5c-8510-0697c9ad2578.jpg','

商品介绍加载中...

',249,165,993,'雾面丝绒哑光',0,0,'2019-09-18 13:25:08',0,'2020-10-13 10:41:59'), (10238,'【情人礼物】香奈儿Chanel 口红/唇膏可可小姐水亮/丝绒系列润唇保湿口红配玫瑰花礼盒 丝绒系列','43#斩男色',86,'/goods-img/70219912-838c-487b-8c3c-761b00de80e9.jpg','/goods-img/70219912-838c-487b-8c3c-761b00de80e9.jpg','

商品介绍加载中...

',299,298,1000,'',0,0,'2019-09-18 13:25:08',0,'2020-10-13 10:41:59'), (10239,'迪奥(Dior)口红礼盒套装(烈艳蓝金唇膏哑光#999 3.5g正红色+香氛小样1ml*3随机+随机礼盒样式)','新蜂精选',86,'/goods-img/cbce65ee-28b3-4822-895a-38243ee506e7.jpg','/goods-img/cbce65ee-28b3-4822-895a-38243ee506e7.jpg','

商品介绍加载中...

',379,379,1000,'',0,0,'2019-09-18 13:25:08',0,'2020-10-13 10:41:59'), (10240,'圣罗兰(YSL)纯口红52# 3.8g','新蜂精选',86,'/goods-img/abff57bf-247b-4881-9589-e1336049c3ba.jpg','/goods-img/abff57bf-247b-4881-9589-e1336049c3ba.jpg','

商品介绍加载中...

',340,320,1000,'',0,0,'2019-09-18 13:25:08',0,'2020-10-13 10:41:59'), (10241,'海囤全球 汤姆福特 TOM','FORD TF口红 经典黑金唇膏 3g 16 SCARLET ROUGE 复古番茄红',0,'/goods-img/ba0cd1e9-cded-427b-8692-e8e2a0d00e9f.jpg','/goods-img/ba0cd1e9-cded-427b-8692-e8e2a0d00e9f.jpg','

商品介绍加载中...

',429,375,1000,'',0,0,'2019-09-18 13:25:08',0,'2020-10-13 10:41:59'), (10242,'迪奥(Dior)烈艳蓝金口红唇膏 028# 3.5g','珊瑚红 (滋润保湿 持久显色 粉嫩少女 摩洛哥王妃 幸运色)',86,'/goods-img/ea87e780-ed4c-447d-bd22-e88e4742721e.jpg','/goods-img/ea87e780-ed4c-447d-bd22-e88e4742721e.jpg','

商品介绍加载中...

',400,315,1000,'',0,0,'2019-09-18 13:25:08',0,'2020-10-13 10:41:59'), (10243,'迪奥(Dior)烈艳蓝金唇膏520# 3.5g 玫瑰红(口红','缎光 滋润保湿 长效持妆 玫红色 斩男色 告白色 粉红色)',86,'/goods-img/dde0b711-58b0-49fb-972c-7a71d6ec30f1.jpg','/goods-img/dde0b711-58b0-49fb-972c-7a71d6ec30f1.jpg','

商品介绍加载中...

',360,315,1000,'',0,0,'2019-09-18 13:25:08',0,'2020-10-13 10:41:59'), (10244,'海囤全球 迪奥(Dior)烈艳蓝金唇膏 口红','3.5g 999号 哑光-经典正红',86,'/goods-img/79247aeb-2903-47b0-a711-ac94e22ddd54.jpg','/goods-img/79247aeb-2903-47b0-a711-ac94e22ddd54.jpg','

商品介绍加载中...

',410,255,997,'',0,0,'2019-09-18 13:25:08',0,'2020-10-13 10:41:59'), (10245,'欧莱雅(LOREAL)纷泽滋润唇膏RC301复古魅红3.7g(金管 口红女 滋润显色)','新蜂精选',86,'/goods-img/3b420562-b449-448d-ae50-e20aab136e1b.jpg','/goods-img/3b420562-b449-448d-ae50-e20aab136e1b.jpg','品牌介绍Brand Description         巴黎欧莱雅通过将科技和美丽的结合,不断谋求创新、研发新的产品配方,以合理的价格,为消费者提供品质的产品和服务。自1907年安全合成染发剂的诞生,如今巴黎欧莱雅的产品已从染发剂扩展到了护肤、彩妆等诸多领域,在中国,巴黎欧莱雅的五大产品线为护肤系列、彩妆系列、家用染发系列、洗护发系列及男士护肤系列。为了将美的产品融于美的文化、艺术、理念,将“从指尖到发梢”的美丽带给全世界的人们,巴黎欧莱雅在全世界范围精心选择各行业明星,组成“梦之队”来见证巴黎欧莱雅的实力,从各个不同的角度来讲述巴黎欧莱雅美丽无疆界的气势,并使“巴黎欧莱雅,你值得拥有!”“Because you are worth it!”的美丽概念成为一种文化!',135,99,1000,'',0,0,'2019-09-18 13:25:08',0,'2020-10-13 10:41:59'), (10246,'阿玛尼(Armani) 口红女士唇釉 生日礼物/表白礼物','红管#405番茄红 【李佳琪推荐omg】',86,'/goods-img/db866c68-e526-42cf-a0b5-520254f30b76.jpg','/goods-img/db866c68-e526-42cf-a0b5-520254f30b76.jpg','

商品介绍加载中...

',366,285,1000,'',0,0,'2019-09-18 13:25:08',0,'2020-10-13 10:41:59'), (10247,'美宝莲(MAYBELLINE)绝色持久唇膏雾感哑光系列R09PM 3.9g(女皇色口红新老包装)','新蜂精选',86,'/goods-img/63d0a187-627d-4edb-870e-717969ad2bd0.jpg','/goods-img/63d0a187-627d-4edb-870e-717969ad2bd0.jpg','

商品介绍加载中...

',122,106,1000,'',0,0,'2019-09-18 13:25:08',0,'2020-10-13 10:41:59'), (10248,'【专柜正品】迪奥999Dior口红唇膏烈艳蓝金 哑光滋润520/888/999送礼礼品套装 烈艳蓝金','844#橘红色赠礼盒礼袋',86,'/goods-img/9822b4a5-9fd2-435b-bdd1-5bbcdc6fdfdf.jpg','/goods-img/9822b4a5-9fd2-435b-bdd1-5bbcdc6fdfdf.jpg','

商品介绍加载中...

',339,260,1000,'',0,0,'2019-09-18 13:25:08',0,'2020-10-13 10:41:59'), (10249,'Apple Macbook Air 13.3 ','Core i5 8G 128G SSD 笔记本电脑 轻薄本 银色 MQD32CH/A',0,'/goods-img/2d827a7e-fb30-493d-840a-cb21766814fd.jpg','/goods-img/2d827a7e-fb30-493d-840a-cb21766814fd.jpg','

商品介紹頁面素材由Apple提供...

',6928,5999,1000,'',0,0,'2019-09-18 13:25:52',0,'2020-10-13 10:41:59'), (10250,'Apple 2019款 Macbook Pro 13.3','【带触控栏】八代i5 8G 256G RP645显卡 银色 苹果笔记本电脑 MUHR2CH/A',0,'/goods-img/465936e0-40ad-4968-b868-4bea20c7beec.jpg','/goods-img/465936e0-40ad-4968-b868-4bea20c7beec.jpg','

商品介紹頁面素材由Apple提供...

',11499,10699,1000,'',0,0,'2019-09-18 13:25:52',0,'2020-10-13 10:41:59'), (10251,'Apple MacBook Air 13.3 ','Core i5 8G 256G SSD 银色 笔记本电脑 轻薄本 Z0UU00056原MQD42CH/A',0,'/goods-img/a4132109-8f18-4399-affd-a81fad6902c8.jpg','/goods-img/a4132109-8f18-4399-affd-a81fad6902c8.jpg','

商品介紹頁面素材由Apple提供...

',7999,7168,1000,'',0,0,'2019-09-18 13:25:52',0,'2020-10-13 10:41:59'), (10252,'Apple 2019款 MacBook Air 13.3 ','Retina屏 八代i5 8G 256G SSD 银色 笔记本电脑 轻薄本 MVFL2CH/A',0,'/goods-img/65b62668-3be5-48b0-a40c-bd05826a38c2.jpg','/goods-img/65b62668-3be5-48b0-a40c-bd05826a38c2.jpg','

商品介紹頁面素材由Apple提供...

',10399,9799,1000,'',0,0,'2019-09-18 13:25:52',0,'2020-10-13 10:41:59'), (10253,'Apple 2019款 MacBook Air 13.3 ','Retina屏 八代i5 8G 128G SSD 深空灰 笔记本电脑 轻薄本 MVFH2CH/A',0,'/goods-img/cb899039-a705-473d-9785-f245a6ed4d89.jpg','/goods-img/cb899039-a705-473d-9785-f245a6ed4d89.jpg','

商品介紹頁面素材由Apple提供...

',8899,8499,1000,'',0,0,'2019-09-18 13:25:52',0,'2020-10-13 10:41:59'), (10254,'Apple 2019款 MacBook Air 13.3 ','Retina屏 八代i5 8G 128G SSD 银色 笔记本电脑 轻薄本 MVFK2CH/A',0,'/goods-img/7810bc9d-236f-4386-a0ef-45a831b49bf2.jpg','/goods-img/7810bc9d-236f-4386-a0ef-45a831b49bf2.jpg','

商品介紹頁面素材由Apple提供...

',8899,8499,992,'再次倾心',0,0,'2019-09-18 13:25:52',0,'2020-10-13 10:41:59'), (10255,'Apple MacBook Air 13.3 ','| 定制升级 Core i7 8G 128G SSD硬盘 银色 笔记本电脑 轻薄本 Z0UU00022',0,'/goods-img/53019ece-5e61-4de9-8eac-e1f00a4ef7e3.jpg','/goods-img/53019ece-5e61-4de9-8eac-e1f00a4ef7e3.jpg','

商品介绍加载中...

',8056,6968,1000,'',0,0,'2019-09-18 13:25:52',0,'2020-10-13 10:41:59'), (10256,'Apple 2019款 Macbook Pro 13.3','【带触控栏】八代i5 8G 256G RP645显卡 深空灰 苹果笔记本电脑 MUHP2CH/A',0,'/goods-img/f08404a7-0459-4289-aa60-dd1735c95bbe.jpg','/goods-img/f08404a7-0459-4289-aa60-dd1735c95bbe.jpg','

商品介紹頁面素材由Apple提供...

',11499,10699,1000,'',0,0,'2019-09-18 13:25:52',0,'2020-10-13 10:41:59'), (10257,'苹果(Apple) MacBook Air','苹果笔记本电脑 13.3英寸轻薄本 购买套餐更实惠 2017款/i5/8GB/128GB/D32',0,'/goods-img/83740c28-473c-4954-b0dc-3cadab5a87d1.jpg','/goods-img/83740c28-473c-4954-b0dc-3cadab5a87d1.jpg','

商品介绍加载中...

',6200,5488,1000,'',0,0,'2019-09-18 13:25:52',0,'2020-10-13 10:41:59'), (10258,'Apple 2019款 MacBook Air 13.3 ','Retina屏 八代i5 8G 256G SSD 深空灰 笔记本电脑 轻薄本 MVFJ2CH/A',0,'/goods-img/78957148-4c0c-4194-bc46-7360d7b1aa65.jpg','/goods-img/78957148-4c0c-4194-bc46-7360d7b1aa65.jpg','

商品介紹頁面素材由Apple提供...

',10399,9799,983,'',0,0,'2019-09-18 13:25:52',0,'2020-10-13 10:41:59'), (10259,'Apple 2019新品 Macbook Pro 13.3','【带触控栏】八代i5 8G 256G 深空灰 笔记本电脑 轻薄本 MV962CH/A',0,'/goods-img/85787c16-8443-4db0-9cae-a811a20a0832.jpg','/goods-img/85787c16-8443-4db0-9cae-a811a20a0832.jpg','

商品介紹頁面素材由Apple提供...

',13899,12999,1000,'',0,0,'2019-09-18 13:25:52',0,'2020-10-13 10:41:59'), (10260,'Apple 2019款 MacBook Air 13.3 ','Retina屏 八代i5 8G 256G SSD 金色 苹果笔记本电脑 轻薄本 MVFN2CH/A',0,'/goods-img/82bdafc6-5828-495e-b77c-21598938b896.jpg','/goods-img/82bdafc6-5828-495e-b77c-21598938b896.jpg','

商品介紹頁面素材由Apple提供...

',10399,9799,1000,'',0,0,'2019-09-18 13:25:52',0,'2020-10-13 10:41:59'), (10261,'APPLE 苹果2018年19新款MacBook air笔记本电脑13.3英寸超薄笔记本','金色 i5/8GB内存/128GB闪存【19新款】',0,'/goods-img/270cdf75-8a7f-410e-8f2f-8eeba24f0503.jpg','/goods-img/270cdf75-8a7f-410e-8f2f-8eeba24f0503.jpg','

商品介绍加载中...

',8899,7888,1000,'',0,0,'2019-09-18 13:25:52',0,'2020-10-13 10:41:59'), (10262,'Apple 2019新品 Macbook Pro 15.4','【带触控栏】全新九代六核i7 16G 256G 深空灰 笔记本电脑轻薄本MV902CH/A',0,'/goods-img/7928eb46-9e1c-420e-a8ab-6c358d01891b.jpg','/goods-img/7928eb46-9e1c-420e-a8ab-6c358d01891b.jpg','

商品介紹頁面素材由Apple提供...

',18199,17099,1000,'',0,0,'2019-09-18 13:25:52',0,'2020-10-13 10:41:59'), (10263,'APPLE 苹果MacBook air苹果笔记本电脑13.3英寸超薄笔记本','标配+防水手提包+苹果原装鼠标版(下单送大礼包) i5+8GB内存+128GB闪存【D32】',0,'/goods-img/11968b35-9431-4b1c-a648-6ff46945ebf4.jpg','/goods-img/11968b35-9431-4b1c-a648-6ff46945ebf4.jpg','

商品介绍加载中...

',6988,5988,1000,'',0,0,'2019-09-18 13:25:52',0,'2020-10-13 10:41:59'), (10264,'APPLE苹果 MacBook Air13.3英寸轻薄笔记本电脑2017款','官方标配【购套餐版送大礼包】 i5+8GB内存+128GB闪存【D32】',0,'/goods-img/fb08ec83-2960-47f7-8679-8b78896c30d5.jpg','/goods-img/fb08ec83-2960-47f7-8679-8b78896c30d5.jpg','

商品介绍加载中...

',6188,5488,1000,'',0,0,'2019-09-18 13:25:52',0,'2020-10-13 10:41:59'), (10265,'Apple 2019款 MacBook Air 13.3 ','Retina屏 八代i5 8G 128G SSD 金色 笔记本电脑 轻薄本 MVFM2CH/A',0,'/goods-img/50748763-c0d6-4e73-80e5-864818fa3246.jpg','/goods-img/50748763-c0d6-4e73-80e5-864818fa3246.jpg','

商品介紹頁面素材由Apple提供...

',8899,8499,1000,'',0,0,'2019-09-18 13:25:52',0,'2020-10-13 10:41:59'), (10266,'Apple 2019款 Macbook Pro 13.3','【带触控栏】八代i5 8G 128G RP645显卡 深空灰 苹果笔记本电脑 MUHN2CH/A',0,'/goods-img/fe9e33a1-fbd0-4278-931f-825fef4ffb62.jpg','/goods-img/fe9e33a1-fbd0-4278-931f-825fef4ffb62.jpg','

商品介紹頁面素材由Apple提供...

',9999,9499,1000,'',0,0,'2019-09-18 13:25:52',0,'2020-10-13 10:41:59'), (10267,'Apple MacBook Air 13.3 ','定制升级 Core i7 8G 256G SSD硬盘 银色 笔记本电脑 轻薄本 Z0UU0004J',0,'/goods-img/0340d6b2-54bf-42a2-96f4-f35c5f47bb2d.jpg','/goods-img/0340d6b2-54bf-42a2-96f4-f35c5f47bb2d.jpg','

商品介紹頁面素材由Apple提供...

',9656,8499,1000,'',0,0,'2019-09-18 13:25:52',0,'2020-10-13 10:41:59'), (10268,'Apple 2019新品 Macbook Pro 15.4','【带触控栏】九代八核i9 16G 512G 深空灰 笔记本电脑 轻薄本 MV912CH/A',0,'/goods-img/33a29216-08d6-445b-b979-12d5de81d634.jpg','/goods-img/33a29216-08d6-445b-b979-12d5de81d634.jpg','

商品介紹頁面素材由Apple提供...

',21399,20399,1000,'',0,0,'2019-09-18 13:25:52',0,'2020-10-13 10:41:59'), (10269,'Apple 2019新品 Macbook Pro 13.3','【带触控栏】八代i5 8G 256G 银色 笔记本电脑 轻薄本 MV992CH/A',0,'/goods-img/a2afdb6c-69a7-4081-bd09-62174f9f5624.jpg','/goods-img/a2afdb6c-69a7-4081-bd09-62174f9f5624.jpg','商品介紹頁面素材由Apple提供  ',13899,12999,982,'',0,0,'2019-09-18 13:25:52',0,'2020-10-13 10:41:59'), (10270,'Apple Macbook Pro 13.3','【带触控栏】Core i5 8G 512G SSD 银色 笔记本电脑 轻薄本 MR9V2CH/A',0,'/goods-img/4da4fa5d-ee2d-4496-9950-e53b102f0e8e.jpg','/goods-img/4da4fa5d-ee2d-4496-9950-e53b102f0e8e.jpg','

商品介绍加载中...

',14999,13068,1000,'',0,0,'2019-09-18 13:25:52',0,'2020-10-13 10:41:59'), (10271,'Apple 2019新品 Macbook Pro 15.4','【带触控栏】全新九代六核i7 16G 256G 银色 笔记本电脑 轻薄本 MV922CH/A',0,'/goods-img/49c9f6f8-11c2-4f57-98b9-daf12715b938.jpg','/goods-img/49c9f6f8-11c2-4f57-98b9-daf12715b938.jpg','

商品介紹頁面素材由Apple提供...

',18199,17099,1000,'',0,0,'2019-09-18 13:25:52',0,'2020-10-13 10:41:59'), (10272,'Apple 2019新品 Macbook Pro 13.3','【带触控栏】八代i5 8G 512G 银色 笔记本电脑 轻薄本 MV9A2CH/A',0,'/goods-img/9dd28614-7a17-4876-8cdd-232caf4154bc.jpg','/goods-img/9dd28614-7a17-4876-8cdd-232caf4154bc.jpg','

商品介紹頁面素材由Apple提供...

',15499,14499,1000,'',0,0,'2019-09-18 13:25:52',0,'2020-10-13 10:41:59'), (10273,'Apple 2019新品 Macbook Pro 15.4','【带触控栏】九代八核i9 16G 512G 银色 笔记本电脑 轻薄本 MV932CH/A',0,'/goods-img/2dcd61b8-f434-40ee-928f-c6e4ae934db8.jpg','/goods-img/2dcd61b8-f434-40ee-928f-c6e4ae934db8.jpg','

商品介紹頁面素材由Apple提供...

',21399,20399,1000,'',0,0,'2019-09-18 13:25:52',0,'2020-10-13 10:41:59'), (10274,'【新品首发】苹果Apple MacBook Pro13.3英寸2019新款18/17苹果笔记本电脑','19款灰色/256G/带bar/MUHP2CH/A',0,'/goods-img/4dbbfbf1-80c0-4389-a02e-ca19fbeb5340.jpg','/goods-img/4dbbfbf1-80c0-4389-a02e-ca19fbeb5340.jpg','

商品介绍加载中...

',12580,10488,1000,'',0,0,'2019-09-18 13:25:52',0,'2020-10-13 10:41:59'), (10275,'【新品首发】苹果Apple MacBook Pro13.3英寸2019新款18/17苹果笔记本电脑','19款灰色/128G/带bar/MUHN2CH/A',0,'/goods-img/3b095a66-4001-4c69-9026-2e09139b5f11.jpg','/goods-img/3b095a66-4001-4c69-9026-2e09139b5f11.jpg','

商品介绍加载中...

',10100,9088,1000,'',0,0,'2019-09-18 13:25:52',0,'2020-10-13 10:41:59'), (10276,'Apple 2019新品 Macbook Pro 13.3','【带触控栏】八代i5 8G 512G 深空灰 苹果笔记本电脑 轻薄本 MV972CH/A',0,'/goods-img/82fb6b31-1afe-4bcb-a243-5205ed32d3ee.jpg','/goods-img/82fb6b31-1afe-4bcb-a243-5205ed32d3ee.jpg','

商品介紹頁面素材由Apple提供...

',15499,14499,1000,'',0,0,'2019-09-18 13:25:52',0,'2020-10-13 10:41:59'), (10277,'Apple Macbook Pro 13.3','【无触控栏】Core i5 8G 256G SSD 银色 笔记本电脑 轻薄本 MPXU2CH/A',0,'/goods-img/73a8c7e9-40af-4e0a-9826-5f6374361e61.jpg','/goods-img/73a8c7e9-40af-4e0a-9826-5f6374361e61.jpg','

商品介绍加载中...

',11299,10199,1000,'',0,0,'2019-09-18 13:25:52',0,'2020-10-13 10:41:59'), (10278,'Apple iPhone 11 (A2223)','64GB 黑色 移动联通电信4G手机 双卡双待',47,'/goods-img/4755f3e5-257c-424c-a5f4-63908061d6d9.jpg','http://localhost:28089/goods-img/4755f3e5-257c-424c-a5f4-63908061d6d9.jpg','
\n
\n
\n
\n
\n
\n
\n \n
\n
',5499,5499,998,'2019 新品',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10279,'Apple iPhone 11 (A2223)','128GB 白色 移动联通电信4G手机 双卡双待',47,'/goods-img/a0d09f94-9c46-4ee1-aaef-dfd132e7543e.jpg','/goods-img/a0d09f94-9c46-4ee1-aaef-dfd132e7543e.jpg','
\n
\n
\n
\n
\n
\n
\n \n
\n
',5999,5999,997,'2019 新品',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10280,'Apple iPhone 11 (A2223)','128GB 紫色 移动联通电信4G手机 双卡双待',47,'/goods-img/8dfe8ea9-2279-4132-a72b-4f8a52d002a4.jpg','/goods-img/8dfe8ea9-2279-4132-a72b-4f8a52d002a4.jpg','
\n
\n
\n
\n
\n
\n
\n \n
\n
',5999,5999,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10281,'Apple iPhone 11 (A2223)','64GB 红色 移动联通电信4G手机 双卡双待',47,'/goods-img/7368f461-fd0a-4f37-bc8b-31d8ad3d6e95.jpg','/goods-img/7368f461-fd0a-4f37-bc8b-31d8ad3d6e95.jpg','
\n
\n
\n
\n
\n
\n
\n \n
\n
',5499,5499,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10282,'Apple iPhone 11 (A2223)','64GB 黄色 移动联通电信4G手机 双卡双待',47,'/goods-img/cea55d85-b11e-4639-88ab-9403b05ce1e8.jpg','/goods-img/cea55d85-b11e-4639-88ab-9403b05ce1e8.jpg','
\n
\n
\n
\n
\n
\n
\n \n
\n
',5499,5499,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10283,'Apple iPhone 11 (A2223)','256GB 绿色 移动联通电信4G手机 双卡双待',47,'/goods-img/075a188a-9045-45f0-9c67-1e42e0552aa2.jpg','/goods-img/075a188a-9045-45f0-9c67-1e42e0552aa2.jpg','
\n
\n
\n
\n
\n
\n
\n \n
\n
',6799,6799,992,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10284,'Apple iPhone XR (A2108)','128GB 黑色 移动联通电信4G手机 双卡双待',47,'/goods-img/23ac3107-6309-40c8-bd28-164eb1186b62.jpg','/goods-img/23ac3107-6309-40c8-bd28-164eb1186b62.jpg','

商品介绍加载中...

',5599,5099,991,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10285,'Apple iPhone XR (A2108)','128GB 白色 移动联通电信4G手机 双卡双待',47,'/goods-img/3f47c376-c603-43fc-bfe5-2daa985ff423.jpg','/goods-img/3f47c376-c603-43fc-bfe5-2daa985ff423.jpg','

商品介绍加载中...

',5599,5099,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10286,'Apple iPhone XR (A2108)','128GB 红色 移动联通电信4G手机 双卡双待',47,'/goods-img/56cef3d7-41e6-4aad-825d-a3d423e74dfd.jpg','/goods-img/56cef3d7-41e6-4aad-825d-a3d423e74dfd.jpg','

商品介绍加载中...

',5599,5099,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10287,'Apple iPhone XR (A2108)','128GB 珊瑚色 移动联通电信4G手机 双卡双待',47,'/goods-img/c2e3b2e4-1fc8-43f3-b133-6f4eae7faa5d.jpg','/goods-img/c2e3b2e4-1fc8-43f3-b133-6f4eae7faa5d.jpg','

商品介绍加载中...

',5599,5199,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10288,'Apple iPhone XR (A2108)','128GB 蓝色 移动联通电信4G手机 双卡双待',47,'/goods-img/2f5079e9-57f3-490a-8d3d-5fd64207939d.jpg','/goods-img/2f5079e9-57f3-490a-8d3d-5fd64207939d.jpg','

商品介绍加载中...

',5599,5199,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10289,'Apple iPhone XR (A2108)','128GB 黄色 移动联通电信4G手机 双卡双待',47,'/goods-img/b1259d73-7c5a-4eca-81eb-53a4e9bcc77e.jpg','/goods-img/b1259d73-7c5a-4eca-81eb-53a4e9bcc77e.jpg','

商品介绍加载中...

',5599,5199,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10290,'Apple iPhone 11 Pro','Max (A2220) 64GB 暗夜绿色 移动联通电信4G手机 双卡双待',47,'/goods-img/0656b280-66d9-430b-9d0d-e48bf379d89a.jpg','/goods-img/0656b280-66d9-430b-9d0d-e48bf379d89a.jpg','
\n
\n
\n
\n
\n
\n
\n \n
\n
',9599,9599,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10291,'Apple iPhone 11 Pro','Max (A2220) 256GB 深空灰色 移动联通电信4G手机 双卡双待',47,'/goods-img/77ce1f09-3900-4eff-8d97-e67fa8193a84.jpg','/goods-img/77ce1f09-3900-4eff-8d97-e67fa8193a84.jpg','
\n
\n
\n
\n
\n
\n
\n \n
\n
',10899,10899,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10292,'Apple iPhone 11 Pro','Max (A2220) 64GB 金色 移动联通电信4G手机 双卡双待',47,'/goods-img/e45be404-d582-4c1e-80e8-48073327551e.jpg','/goods-img/e45be404-d582-4c1e-80e8-48073327551e.jpg','
\n
\n
\n
\n
\n
\n
\n \n
\n
',9599,9599,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10293,'Apple iPhone 11 Pro','Max (A2220) 512GB 银色 移动联通电信4G手机 双卡双待',47,'/goods-img/76670f49-4556-40ae-b485-3b25dcdcb636.jpg','/goods-img/76670f49-4556-40ae-b485-3b25dcdcb636.jpg','
\n
\n
\n
\n
\n
\n
\n \n
\n
',12699,12699,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10294,'Apple iPhone 7 (A1660)','128G 黑色 移动联通电信4G手机',47,'/goods-img/101abd40-e9a2-4ab0-9f4e-16569c9dbf82.jpg','/goods-img/101abd40-e9a2-4ab0-9f4e-16569c9dbf82.jpg','

商品介绍加载中...

',3199,2949,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10295,'Apple iPhone 7 (A1660)','128G 玫瑰金色 移动联通电信4G手机',47,'/goods-img/6229468b-bcb7-4415-880a-aea3eef4eea2.jpg','/goods-img/6229468b-bcb7-4415-880a-aea3eef4eea2.jpg','

商品介绍加载中...

',3199,2929,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10296,'Apple iPhone 7 (A1660)','128G 金色 移动联通电信4G手机',47,'/goods-img/1f5bb955-fbe7-451a-b12c-3e2115c53020.jpg','/goods-img/1f5bb955-fbe7-451a-b12c-3e2115c53020.jpg','

商品介绍加载中...

',3199,2929,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10297,'Apple iPhone 7 (A1660)','128G 银色 移动联通电信4G手机',47,'/goods-img/9fc3c48f-c8e2-426b-915a-c32b0e72998d.jpg','/goods-img/9fc3c48f-c8e2-426b-915a-c32b0e72998d.jpg','

商品介绍加载中...

',3199,2929,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10298,'Apple iPhone XS Max','(A2104) 256GB 深空灰色 移动联通电信4G手机 双卡双待',47,'/goods-img/ec4af4a5-0a53-4246-bd88-919b0541a55c.jpg','/goods-img/ec4af4a5-0a53-4246-bd88-919b0541a55c.jpg','

商品介绍加载中...

',9599,8999,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10299,'Apple iPhone XS Max','(A2104) 256GB 金色 移动联通电信4G手机 双卡双待',47,'/goods-img/b7d2373a-5a8c-4be5-a4ce-57b408c6d9f2.jpg','/goods-img/b7d2373a-5a8c-4be5-a4ce-57b408c6d9f2.jpg','

商品介绍加载中...

',9599,8999,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10300,'Apple iPhone XS Max','(A2104) 256GB 银色 移动联通电信4G手机 双卡双待',47,'/goods-img/837aaf40-5797-4929-b162-a248bfe73b36.jpg','/goods-img/837aaf40-5797-4929-b162-a248bfe73b36.jpg','

商品介绍加载中...

',9599,8999,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10301,'Apple iPhone 8 (A1863)','64GB 深空灰色 移动联通电信4G手机',47,'/goods-img/8ab049d8-5b2e-4b69-bef0-013bec414598.jpg','/goods-img/8ab049d8-5b2e-4b69-bef0-013bec414598.jpg','

商品介绍加载中...

',3699,3499,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10302,'Apple iPhone 8 (A1863)','64GB 银色 移动联通电信4G手机',47,'/goods-img/eaeb6faf-2ead-4f5d-84d2-1629686a492c.jpg','/goods-img/eaeb6faf-2ead-4f5d-84d2-1629686a492c.jpg','

商品介绍加载中...

',3699,3499,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10303,'Apple iPhone 8 (A1863)','64GB 金色 移动联通电信4G手机',47,'/goods-img/0611528c-73c8-4114-a1d8-d9387e771284.jpg','/goods-img/0611528c-73c8-4114-a1d8-d9387e771284.jpg','

商品介绍加载中...

',3699,3499,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10304,'Apple iPhone 7 Plus','(A1661) 128G 黑色 移动联通电信4G手机',47,'/goods-img/dbafc182-23b7-442c-b9cb-0ea825a659a9.jpg','/goods-img/dbafc182-23b7-442c-b9cb-0ea825a659a9.jpg','

商品介绍加载中...

',4199,3699,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10305,'Apple iPhone 7 Plus','(A1661) 128G 玫瑰金色 移动联通电信4G手机',47,'/goods-img/c227df08-9a26-430a-88a5-72c1e4da5b6e.jpg','/goods-img/c227df08-9a26-430a-88a5-72c1e4da5b6e.jpg','

商品介绍加载中...

',4199,3699,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10306,'Apple iPhone 7 Plus','(A1661) 128G 金色 移动联通电信4G手机',47,'/goods-img/bf58f29f-75ed-411e-8255-3b9f802634f2.jpg','/goods-img/bf58f29f-75ed-411e-8255-3b9f802634f2.jpg','

商品介绍加载中...

',4199,3699,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10307,'Apple iPhone 7 Plus','(A1661) 128G 银色 移动联通电信4G手机',47,'/goods-img/dfab7fee-e787-423d-9771-67e05b03b358.jpg','/goods-img/dfab7fee-e787-423d-9771-67e05b03b358.jpg','

商品介绍加载中...

',4199,3699,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10308,'Apple iPhone XS (A2099)','64GB 金色 移动联通4G手机',47,'/goods-img/b3ff5475-9519-4d94-8f07-5840bb796e60.jpg','/goods-img/b3ff5475-9519-4d94-8f07-5840bb796e60.jpg','

商品介绍加载中...

',7299,6299,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10309,'Apple iPhone XS (A2099)','64GB 深空灰色 移动联通4G手机',47,'/goods-img/7cc8d012-cfaa-45c4-ba35-70ca46c8bd66.jpg','/goods-img/7cc8d012-cfaa-45c4-ba35-70ca46c8bd66.jpg','

商品介绍加载中...

',7299,6299,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10310,'Apple iPhone XS (A2099)','256GB 银色 移动联通4G手机',47,'/goods-img/776b459b-e981-434f-bbf7-635cafab7418.jpg','/goods-img/776b459b-e981-434f-bbf7-635cafab7418.jpg','

商品介绍加载中...

',10099,7699,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10311,'Apple iPhone 8 Plus','(A1899) 64GB 深空灰色 移动联通4G手机',47,'/goods-img/8eb2e38b-84e1-4f31-9dae-841800f68038.jpg','/goods-img/8eb2e38b-84e1-4f31-9dae-841800f68038.jpg','

商品介绍加载中...

',4599,3999,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10312,'Apple iPhone 8 Plus','(A1864) 64GB 金色 移动联通电信4G手机',47,'/goods-img/58c6a2c3-d3f7-4b0a-b4ae-e649b1032087.jpg','/goods-img/58c6a2c3-d3f7-4b0a-b4ae-e649b1032087.jpg','

商品介绍加载中...

',4799,4399,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10313,'Apple iPhone 8 Plus','(A1864) 64GB 银色 移动联通电信4G手机',47,'/goods-img/2839c451-3eaf-4820-8a15-1858ce339407.jpg','/goods-img/2839c451-3eaf-4820-8a15-1858ce339407.jpg','

商品介绍加载中...

',4799,4399,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10314,'Apple 苹果 iPhone xr','手机 双卡双待 黑色 全网通64G',47,'/goods-img/35bbe123-c822-457c-aaf0-fdcd861bc06d.jpg','/goods-img/35bbe123-c822-457c-aaf0-fdcd861bc06d.jpg','

商品介绍加载中...

',6199,4598,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10315,'Apple 苹果 iPhone xr','手机 双卡双待 白色 全网通64G',47,'/goods-img/0e565b23-554e-45d3-ac62-a2fb25be7f2c.jpg','/goods-img/0e565b23-554e-45d3-ac62-a2fb25be7f2c.jpg','

商品介绍加载中...

',6199,4658,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10316,'Apple 苹果 iPhone xr','手机 双卡双待 蓝色 全网通64G',47,'/goods-img/c08b6ddc-735f-4d2c-b47f-1f0e7f62a9b1.jpg','/goods-img/c08b6ddc-735f-4d2c-b47f-1f0e7f62a9b1.jpg','

商品介绍加载中...

',6199,4698,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10317,'Apple 苹果 iPhone xr','手机 双卡双待 黄色 全网通64G',47,'/goods-img/c09636de-93b1-444e-b00e-668506676443.jpg','/goods-img/c09636de-93b1-444e-b00e-668506676443.jpg','

商品介绍加载中...

',6199,4698,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10318,'Apple 苹果 iPhone xr','手机 双卡双待 红色 全网通128G',47,'/goods-img/b26d8460-7ab5-4006-ba5c-e212ee0f31bd.jpg','/goods-img/b26d8460-7ab5-4006-ba5c-e212ee0f31bd.jpg','

商品介绍加载中...

',6699,5038,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10319,'Apple 苹果 iPhone xr','手机 双卡双待 珊瑚色 全网通64G',47,'/goods-img/fab7cf40-9b7d-4141-8227-9ce7e02e8330.jpg','/goods-img/fab7cf40-9b7d-4141-8227-9ce7e02e8330.jpg','

商品介绍加载中...

',6199,4698,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10320,'Apple iPhone 11 Pro','(A2217) 256GB 暗夜绿色 移动联通电信4G手机 双卡双待',47,'/goods-img/0025ad55-e260-4a00-be79-fa5b8c5ac0de.jpg','/goods-img/0025ad55-e260-4a00-be79-fa5b8c5ac0de.jpg','
\n
\n
\n
\n
\n
\n
\n \n
\n
',9999,9999,996,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10321,'Apple iPhone 11 Pro','(A2217) 64GB 深空灰色 移动联通电信4G手机 双卡双待',47,'/goods-img/d0abbd2a-19ca-4ae7-9b3c-1eb4eb77c565.jpg','/goods-img/d0abbd2a-19ca-4ae7-9b3c-1eb4eb77c565.jpg','
\n
\n
\n
\n
\n
\n
\n \n
\n
',8699,8699,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10322,'Apple iPhone 11 Pro','(A2217) 64GB 银色 移动联通电信4G手机 双卡双待',47,'/goods-img/7d192eff-938f-4e6d-8952-9d405707033e.jpg','/goods-img/7d192eff-938f-4e6d-8952-9d405707033e.jpg','
\n
\n
\n
\n
\n
\n
\n \n
\n
',8699,8699,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10323,'【换修无忧年付版】Apple iPhone 11 Pro','(A2217) 512GB 金色 移动联通电信4G手机 双卡双待',47,'/goods-img/38b3f3a9-7056-45a3-b183-ad46dc71f493.jpg','/goods-img/38b3f3a9-7056-45a3-b183-ad46dc71f493.jpg','
\n
\n
\n
\n
\n
\n
\n \n
\n
',12598,12598,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10324,'Apple 苹果 iPhone 6s','Plus 4G手机 金色 全网通 128G',47,'/goods-img/22febff2-db52-4f7a-8d16-414e755e788b.jpg','/goods-img/22febff2-db52-4f7a-8d16-414e755e788b.jpg','

商品介绍加载中...

',3599,2918,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10325,'Apple 苹果 iPhone 6s','Plus 4G手机 玫瑰金 全网通 128G',47,'/goods-img/dfb0d434-4d59-4fda-896a-1ebd9e4d9ece.jpg','/goods-img/dfb0d434-4d59-4fda-896a-1ebd9e4d9ece.jpg','

商品介绍加载中...

',3599,2918,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10326,'Apple 苹果 iPhone 6s','Plus 4G手机 深空灰 全网通 128G',47,'/goods-img/d3a4b902-8010-4619-89e4-96cb88e6d4e4.jpg','/goods-img/d3a4b902-8010-4619-89e4-96cb88e6d4e4.jpg','

商品介绍加载中...

',3599,2888,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10327,'Apple 苹果 iPhone 6s','Plus 4G手机 银色 全网通 128G',47,'/goods-img/b4b7e7d3-b7ba-4917-a1f9-70c52f28df9d.jpg','/goods-img/b4b7e7d3-b7ba-4917-a1f9-70c52f28df9d.jpg','

商品介绍加载中...

',3599,2988,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10328,'【二手9成新】Apple iPhone XSmax 苹果XSmax','国行二手手机 XS Max 深空灰 64G',47,'/goods-img/0514e529-6b3e-40d5-9183-84088ddb55e1.jpg','/goods-img/0514e529-6b3e-40d5-9183-84088ddb55e1.jpg','

商品介绍加载中...

',7766,6088,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10329,'【二手9成新】Apple iPhone XSmax 苹果XSmax','国行二手手机 XS Max 金色 64G',47,'/goods-img/a0dfd1ad-61ed-43ee-add4-74bdfea1d6c1.jpg','/goods-img/a0dfd1ad-61ed-43ee-add4-74bdfea1d6c1.jpg','

商品介绍加载中...

',14999,6088,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10330,'【二手9成新】Apple iPhone XSmax 苹果XSmax','国行二手手机 XS Max 银色 256G',47,'/goods-img/87b66719-fc17-4c97-a954-de8a78b42a09.jpg','/goods-img/87b66719-fc17-4c97-a954-de8a78b42a09.jpg','

商品介绍加载中...

',14999,6938,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10331,'【二手9成新】Apple iPhone 6s Plus','苹果6sPlus 二手手机(送一年碎屏险) 玫瑰金色 64G 全网通',47,'/goods-img/5b132b57-24e4-4d65-9cb8-3299dc0e9ed6.png','/goods-img/5b132b57-24e4-4d65-9cb8-3299dc0e9ed6.png','

商品介绍加载中...

',1799,1468,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10332,'【二手9成新】Apple iPhone 6s Plus','苹果6sPlus 二手手机(送一年碎屏险) 金色 64G 全网通',47,'/goods-img/f289ec14-e0e2-481e-a703-39eec00a1b15.png','/goods-img/f289ec14-e0e2-481e-a703-39eec00a1b15.png','

商品介绍加载中...

',1799,1499,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10333,'【二手9成新】Apple iPhone 6s Plus','苹果6sPlus 二手手机(送一年碎屏险) 银色 64G 全网通',47,'/goods-img/084208d0-4dc2-4f1a-aff4-4114616dfae1.png','/goods-img/084208d0-4dc2-4f1a-aff4-4114616dfae1.png','

商品介绍加载中...

',1799,1599,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10334,'【二手9成新】Apple iPhone 6s Plus','苹果6sPlus 二手手机(送一年碎屏险) 深空灰色 64G 全网通',47,'/goods-img/8a598420-0052-4551-b00a-b288b6c22a48.png','/goods-img/8a598420-0052-4551-b00a-b288b6c22a48.png','

商品介绍加载中...

',1799,1638,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10335,'Apple 苹果 iPhone xr','手机 双卡双待 白色 全网通 64G',47,'/goods-img/6110a187-511f-45d0-8b59-ea2a75546a45.jpg','/goods-img/6110a187-511f-45d0-8b59-ea2a75546a45.jpg','

商品介绍加载中...

',5499,4699,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10336,'Apple 苹果 iPhone xr','手机 双卡双待 黑色 全网通 128G',47,'/goods-img/41b10e86-857c-435c-b86d-d822e35450ab.jpg','/goods-img/41b10e86-857c-435c-b86d-d822e35450ab.jpg','

商品介绍加载中...

',5699,5079,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10337,'Apple 苹果 iPhone xr','手机 双卡双待 蓝色 全网通 64G',47,'/goods-img/d38bcaab-7a0a-4f86-ad75-60ac74a308e6.jpg','/goods-img/d38bcaab-7a0a-4f86-ad75-60ac74a308e6.jpg','

商品介绍加载中...

',5499,4699,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10338,'Apple 苹果 iPhone xr','手机 双卡双待 黄色 全网通 128G',47,'/goods-img/73fc7cb9-5b43-4bce-a2b3-a82516773de0.jpg','/goods-img/73fc7cb9-5b43-4bce-a2b3-a82516773de0.jpg','

商品介绍加载中...

',5699,5079,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10339,'Apple 苹果 iPhone xr','手机 双卡双待 珊瑚色 全网通 64G',47,'/goods-img/00e53d76-db08-4ae2-864f-ca1cd7c8c32b.jpg','/goods-img/00e53d76-db08-4ae2-864f-ca1cd7c8c32b.jpg','

商品介绍加载中...

',5499,4699,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10340,'【二手95新】Apple iPhonex XSmax苹果x xsmax','国行 二手手机 XS max金色 64G 全网通',47,'/goods-img/5b9acfd4-7808-4b3b-bf5c-4b367667418c.jpg','/goods-img/5b9acfd4-7808-4b3b-bf5c-4b367667418c.jpg','

商品介绍加载中...

',12999,6088,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10341,'【二手95新】Apple iPhonex XSmax苹果x xsmax','国行 二手手机 XS 金色 64G 全网通',47,'/goods-img/cd2b481d-a4a2-4bc0-a4e1-784a28c37ef9.jpg','/goods-img/cd2b481d-a4a2-4bc0-a4e1-784a28c37ef9.jpg','

商品介绍加载中...

',12999,5299,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10342,'【二手95新】Apple iPhonex XSmax苹果x xsmax','国行 二手手机 XS max灰色 256G 全网通',47,'/goods-img/1d866674-4e57-483a-955f-5fd1a4f5d921.jpg','/goods-img/1d866674-4e57-483a-955f-5fd1a4f5d921.jpg','

商品介绍加载中...

',12999,6938,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10343,'【二手95新】Apple iPhonex XSmax苹果x xsmax','国行 二手手机 XS max银色 64G 全网通',47,'/goods-img/3f3e086e-e4be-464f-9c20-760430cab2df.jpg','/goods-img/3f3e086e-e4be-464f-9c20-760430cab2df.jpg','

商品介绍加载中...

',12999,6088,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10344,'【二手95新】Apple iPhonex XSmax苹果x xsmax','国行 二手手机 XS 灰色 64G 全网通',47,'/goods-img/4a4a0820-aad5-47d4-a926-f040fd090c96.jpg','/goods-img/4a4a0820-aad5-47d4-a926-f040fd090c96.jpg','

商品介绍加载中...

',12999,5299,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10345,'【二手95新】Apple iPhonex XSmax苹果x xsmax','国行 二手手机 XS 银色 64G 全网通',47,'/goods-img/a6b87d83-5ba7-4683-be17-43ab9aa043e3.jpg','/goods-img/a6b87d83-5ba7-4683-be17-43ab9aa043e3.jpg','

商品介绍加载中...

',12999,5299,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10346,'【二手9成新】Apple iPhone X 苹果X','二手手机 深空灰色 64G 全网通',47,'/goods-img/3cd13e20-2a00-4049-8768-0ba662df7e40.jpg','/goods-img/3cd13e20-2a00-4049-8768-0ba662df7e40.jpg','

商品介绍加载中...

',3989,3989,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10347,'【二手9成新】Apple iPhone X 苹果X','二手手机 银色 64G 全网通',47,'/goods-img/fc3db752-e0dc-4ae7-bac3-fd60ab8a1e17.jpg','/goods-img/fc3db752-e0dc-4ae7-bac3-fd60ab8a1e17.jpg','

商品介绍加载中...

',4008,4008,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10348,'【二手9成新】苹果7Plus手机 Apple iPhone7Plus 苹果7P','二手手机 磨砂黑 128G 全网通',47,'/goods-img/24b442e2-1bdd-4350-bbab-4e4d3d3445f1.jpg','/goods-img/24b442e2-1bdd-4350-bbab-4e4d3d3445f1.jpg','

商品介绍加载中...

',2899,2399,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10349,'【二手9成新】苹果7Plus手机 Apple iPhone7Plus 苹果7P','二手手机 亮黑色 128G 全网通',47,'/goods-img/7601e13f-de8e-449c-84be-65fbc7280cfc.png','/goods-img/7601e13f-de8e-449c-84be-65fbc7280cfc.png','

商品介绍加载中...

',2899,2399,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10350,'【二手9成新】苹果7Plus手机 Apple iPhone7Plus 苹果7P','二手手机 玫瑰金 128G 全网通',47,'/goods-img/771bc653-485b-4c5d-bca3-c84d3e90020d.jpg','/goods-img/771bc653-485b-4c5d-bca3-c84d3e90020d.jpg','

商品介绍加载中...

',2666,2399,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10351,'【二手9成新】苹果7Plus手机 Apple iPhone7Plus 苹果7P','二手手机 金色 128G 全网通',47,'/goods-img/5a170339-acb4-4890-bd08-bb109864e853.jpg','/goods-img/5a170339-acb4-4890-bd08-bb109864e853.jpg','

商品介绍加载中...

',2739,2399,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10352,'【二手9成新】苹果7Plus手机 Apple iPhone7Plus 苹果7P','二手手机 银色 128G 全网通',47,'/goods-img/a419ebb4-18a5-4295-9404-0593dd215ad0.jpg','/goods-img/a419ebb4-18a5-4295-9404-0593dd215ad0.jpg','

商品介绍加载中...

',2699,2466,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10353,'【二手9成新】Apple iPhone X 苹果X','二手手机 全网通 深空灰 64G',47,'/goods-img/4f666eee-c2c7-459c-934e-b32714d1e1c4.png','/goods-img/4f666eee-c2c7-459c-934e-b32714d1e1c4.png','

商品介绍加载中...

',5188,3956,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10354,'【二手9成新】苹果8Plus手机 Apple iPhone 8Plus','苹果8P 二手手机 深空灰 64G 全网通',47,'/goods-img/ada8e547-dca3-47fc-8aab-35884575090a.jpg','/goods-img/ada8e547-dca3-47fc-8aab-35884575090a.jpg','

商品介绍加载中...

',3888,3199,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10355,'【二手9成新】苹果8Plus手机 Apple iPhone 8Plus','苹果8P 二手手机 金色 64G 全网通',47,'/goods-img/76a2e417-2f15-412f-ab73-3a5eb2a7d2d1.jpg','/goods-img/76a2e417-2f15-412f-ab73-3a5eb2a7d2d1.jpg','

商品介绍加载中...

',3550,3199,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10356,'【二手9成新】苹果8Plus手机 Apple iPhone 8Plus','苹果8P 二手手机 银色 64G 全网通',47,'/goods-img/5bfb8955-0b1c-4652-b162-a9b91b71211a.jpg','/goods-img/5bfb8955-0b1c-4652-b162-a9b91b71211a.jpg','

商品介绍加载中...

',3499,3238,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10357,'【二手9成新】苹果8Plus手机 Apple iPhone 8Plus','苹果8P 二手手机 中国红 64G 全网通',47,'/goods-img/d31193ee-04c1-4bac-8a91-1a4690a396be.jpg','/goods-img/d31193ee-04c1-4bac-8a91-1a4690a396be.jpg','

商品介绍加载中...

',3438,3299,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10358,'【二手9成新】Apple iPhoneX 苹果X 二手苹果x手机','深空灰 64G全网通',47,'/goods-img/b9264842-cd50-4d6f-a4a5-e8cc9dd483a4.png','/goods-img/b9264842-cd50-4d6f-a4a5-e8cc9dd483a4.png','

商品介绍加载中...

',4799,3989,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10359,'【二手9成新】Apple iPhoneX 苹果X 二手苹果x手机','银色 64G全网通',47,'/goods-img/58e9a125-61c1-416b-b17f-99cda431a202.png','/goods-img/58e9a125-61c1-416b-b17f-99cda431a202.png','

商品介绍加载中...

',4799,4016,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10360,'【二手95新】Apple iPhone XS 苹果xs','国行全网通二手手机 银色 全网通 64G',47,'/goods-img/5a732ada-1fdb-48f1-b106-666159565a94.jpg','/goods-img/5a732ada-1fdb-48f1-b106-666159565a94.jpg','

商品介绍加载中...

',9999,5299,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10361,'【二手95新】Apple iPhone XS 苹果xs','国行全网通二手手机 金色 全网通 256G',47,'/goods-img/f9e9b321-4b25-40c5-af6d-d9f3fe74a053.jpg','/goods-img/f9e9b321-4b25-40c5-af6d-d9f3fe74a053.jpg','

商品介绍加载中...

',9999,6008,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10362,'【二手9成新】Apple iPhone X 苹果x','二手手机 X 银色 256G 全网通',47,'/goods-img/8da60128-fcc7-46ed-98b6-0066c69624c0.png','/goods-img/8da60128-fcc7-46ed-98b6-0066c69624c0.png','

商品介绍加载中...

',5058,4639,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10363,'【二手9成新】Apple iPhone X 苹果x','国行全网通二手手机 X 灰色 64G 全网通',47,'/goods-img/8aca87a3-65dd-4c42-91c7-bbbd10fcf7a6.jpg','/goods-img/8aca87a3-65dd-4c42-91c7-bbbd10fcf7a6.jpg','

商品介绍加载中...

',6999,3999,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10364,'【二手9成新】Apple iPhone X 苹果x','国行全网通二手手机 X 银色 64G 全网通',47,'/goods-img/fdec1b37-9a2f-46ea-af03-5091d83e546a.jpg','/goods-img/fdec1b37-9a2f-46ea-af03-5091d83e546a.jpg','

商品介绍加载中...

',6999,4078,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10365,'【二手9成新】Apple iPhone XR 苹果xr','二手手机双卡双待 白色 128G 全网通',47,'/goods-img/9834bb8d-fe1c-4218-a624-4a25aecb0676.jpg','/goods-img/9834bb8d-fe1c-4218-a624-4a25aecb0676.jpg','

商品介绍加载中...

',5888,4299,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10366,'【二手9成新】Apple iPhone XR 苹果xr','二手手机双卡双待 蓝色 128G 全网通',47,'/goods-img/3993feaa-0365-4d7e-9cc5-dcf583243ca3.jpg','/goods-img/3993feaa-0365-4d7e-9cc5-dcf583243ca3.jpg','

商品介绍加载中...

',5888,4399,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10367,'【二手9成新】Apple iPhone XR 苹果xr','二手手机双卡双待 黑色 64G 全网通',47,'/goods-img/ba9cf789-60a8-48db-8329-97c3fc13a061.jpg','/goods-img/ba9cf789-60a8-48db-8329-97c3fc13a061.jpg','

商品介绍加载中...

',5888,4055,1000,'',0,0,'2019-09-18 13:27:13',0,'2020-10-13 10:41:59'), (10689,'荣耀Play3 6.39英寸魅眼全视屏 4000mAh大电池 真4800万AI三摄','麒麟710F自研芯片 全网通4GB+64GB 幻夜黑',45,'/goods-img/9aa34959-cd60-418f-b42e-aa7243b6869c.jpg','/goods-img/9aa34959-cd60-418f-b42e-aa7243b6869c.jpg','

商品介绍加载中...

',999,999,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10690,'华为 HUAWEI 畅享10 Plus','超高清全视屏前置悬浮式镜头4800万超广角AI三摄 4GB+128GB幻夜黑全网通双4G手机',46,'/goods-img/2613a582-460c-4c2b-bbc0-6c7dbf501bd2.jpg','/goods-img/2613a582-460c-4c2b-bbc0-6c7dbf501bd2.jpg','

商品介绍加载中...

',1499,1499,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10691,'华为 HUAWEI 畅享10 Plus','超高清全视屏前置悬浮式镜头4800万超广角AI三摄 4GB+128GB翡冷翠全网通双4G手机',46,'/goods-img/21b0751b-f6ae-4a57-8fb8-61e007395c43.jpg','/goods-img/21b0751b-f6ae-4a57-8fb8-61e007395c43.jpg','

商品介绍加载中...

',1499,1499,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10692,'华为 HUAWEI 畅享10 Plus','超高清全视屏前置悬浮式镜头4800万超广角AI三摄 6GB+128GB天空之境全网通双4G手机',46,'/goods-img/3f68538f-3b56-4e98-9676-99139857428c.jpg','/goods-img/3f68538f-3b56-4e98-9676-99139857428c.jpg','

商品介绍加载中...

',1799,1799,999,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10693,'荣耀10青春版 幻彩渐变 2400万AI自拍 全网通版4GB+64GB','渐变蓝 移动联通电信4G全面屏手机 双卡双待',45,'/goods-img/f8ab28c3-8e04-49a0-ba05-2e6a3ae7211f.jpg','/goods-img/f8ab28c3-8e04-49a0-ba05-2e6a3ae7211f.jpg','

商品介绍加载中...

',1099,999,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10694,'荣耀10青春版 幻彩渐变 2400万AI自拍 全网通版4GB+64GB','幻夜黑 移动联通电信4G全面屏手机 双卡双待',45,'/goods-img/de654f42-d58d-4336-8edd-da01c3523449.jpg','/goods-img/de654f42-d58d-4336-8edd-da01c3523449.jpg','

商品介绍加载中...

',1099,999,999,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10695,'荣耀10青春版 幻彩渐变 2400万AI自拍 全网通版4GB+64GB','渐变红 移动联通电信4G全面屏手机 双卡双待',45,'/goods-img/87254a42-9fdf-4e68-a11e-e8e2eef28d2c.jpg','/goods-img/87254a42-9fdf-4e68-a11e-e8e2eef28d2c.jpg','

商品介绍加载中...

',1099,999,997,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10696,'荣耀10青春版 幻彩渐变 2400万AI自拍 全网通版4GB+64GB','铃兰白 移动联通电信4G全面屏手机 双卡双待',45,'/goods-img/81b7060a-7274-4bff-86c0-72d5fc7ff383.jpg','/goods-img/81b7060a-7274-4bff-86c0-72d5fc7ff383.jpg','

商品介绍加载中...

',1099,999,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10697,'荣耀8X 千元屏霸 91%屏占比 2000万AI双摄','4GB+64GB 幻夜黑 移动联通电信4G全面屏手机 双卡双待',45,'/goods-img/d7f74e8f-5c52-422b-ac99-a8d691830494.jpg','/goods-img/d7f74e8f-5c52-422b-ac99-a8d691830494.jpg','

商品介绍加载中...

',1399,999,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10698,'荣耀8X 千元屏霸 91%屏占比 2000万AI双摄','4GB+64GB 幻影蓝 移动联通电信4G全面屏手机 双卡双待',45,'/goods-img/7031c07e-a70f-4f6d-9e2d-d0af31e3393a.jpg','/goods-img/7031c07e-a70f-4f6d-9e2d-d0af31e3393a.jpg','

商品介绍加载中...

',1399,999,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10699,'荣耀8X 千元屏霸 91%屏占比 2000万AI双摄','4GB+64GB 魅海蓝 移动联通电信4G全面屏手机 双卡双待',45,'/goods-img/b7bfcc28-98c2-4cb4-8ce3-afe4c482b674.jpg','/goods-img/b7bfcc28-98c2-4cb4-8ce3-afe4c482b674.jpg','

商品介绍加载中...

',1399,999,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10700,'荣耀8X 千元屏霸 91%屏占比 2000万AI双摄','4GB+64GB 魅焰红 移动联通电信4G全面屏手机 双卡双待',45,'/goods-img/6a160b96-9b4a-4844-b335-feb31b1f5d8c.jpg','/goods-img/6a160b96-9b4a-4844-b335-feb31b1f5d8c.jpg','

商品介绍加载中...

',1399,999,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10701,'荣耀8X 千元屏霸 91%屏占比 2000万AI双摄','4GB+64GB 梦幻紫 移动联通电信4G全面屏手机 双卡双待',45,'/goods-img/8ccc13ec-96fe-4488-a604-526601548c9e.jpg','/goods-img/8ccc13ec-96fe-4488-a604-526601548c9e.jpg','

商品介绍加载中...

',1399,999,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10702,'华为 HUAWEI P30 超感光徕卡三摄麒麟980AI智能芯片全面屏屏内指纹版手机8GB+128GB天空之境全网通双4G手机','新蜂精选',46,'/goods-img/edb7e8ef-7785-418b-a75e-dfed2aa74e39.jpg','/goods-img/edb7e8ef-7785-418b-a75e-dfed2aa74e39.jpg','

商品介绍加载中...

',4288,3988,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10703,'华为 HUAWEI P30 超感光徕卡三摄麒麟980AI智能芯片全面屏屏内指纹版手机8GB+128GB亮黑色全网通双4G手机','新蜂精选',46,'/goods-img/e13294f7-9ab0-42dc-afb1-9f41c59436cf.jpg','/goods-img/e13294f7-9ab0-42dc-afb1-9f41c59436cf.jpg','

商品介绍加载中...

',4288,3988,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10704,'华为 HUAWEI P30 超感光徕卡三摄麒麟980AI智能芯片全面屏屏内指纹版手机8GB+128GB珠光贝母全网通双4G手机','新蜂精选',46,'/goods-img/b9e6d770-06dd-40f4-9ae5-31103cec6e5f.jpg','/goods-img/b9e6d770-06dd-40f4-9ae5-31103cec6e5f.jpg','

商品介绍加载中...

',4288,3988,998,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10705,'华为 HUAWEI P30 超感光徕卡三摄麒麟980AI智能芯片全面屏屏内指纹版手机8GB+128GB极光色全网通双4G手机','新蜂精选',46,'/goods-img/20312f4e-da4f-49b9-8150-ab54f0302915.jpg','/goods-img/20312f4e-da4f-49b9-8150-ab54f0302915.jpg','

商品介绍加载中...

',4288,3988,997,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10706,'华为 HUAWEI P30 超感光徕卡三摄麒麟980AI智能芯片全面屏屏内指纹版手机8GB+128GB赤茶橘全网通双4G手机','新蜂精选',46,'/goods-img/192b1727-bcab-4bdf-8494-182f8ec5b2e6.jpg','/goods-img/192b1727-bcab-4bdf-8494-182f8ec5b2e6.jpg','

商品介绍加载中...

',4288,3988,987,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10707,'荣耀20i 3200万AI自拍 超广角三摄 全网通版6GB+64GB','渐变蓝 移动联通电信4G全面屏手机 双卡双待',45,'/goods-img/74146e03-42d1-453c-843d-b02d8bcc24f4.jpg','/goods-img/74146e03-42d1-453c-843d-b02d8bcc24f4.jpg','

商品介绍加载中...

',1399,1299,996,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10708,'荣耀20i 3200万AI自拍 超广角三摄 全网通版6GB+64GB','渐变红 移动联通电信4G全面屏手机 双卡双待',45,'/goods-img/4c066fc2-3a58-44df-9dc6-8465b25f92ef.jpg','/goods-img/4c066fc2-3a58-44df-9dc6-8465b25f92ef.jpg','

商品介绍加载中...

',1399,1299,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10709,'荣耀20i 3200万AI自拍 超广角三摄 全网通版6GB+64GB','幻夜黑 移动联通电信4G全面屏手机 双卡双待',45,'/goods-img/525bdd6e-848b-4e02-b19f-1a08fdb87faa.jpg','/goods-img/525bdd6e-848b-4e02-b19f-1a08fdb87faa.jpg','

商品介绍加载中...

',1399,1299,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10710,'荣耀9X 麒麟810 4000mAh超强续航 4800万超清夜拍','6.59英寸升降全面屏 全网通6GB+64GB 魅海蓝',45,'/goods-img/7b8b7da7-f154-453e-a6a6-ea2f5e7d8b4a.jpg','/goods-img/7b8b7da7-f154-453e-a6a6-ea2f5e7d8b4a.jpg','

商品介绍加载中...

',1599,1599,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10711,'荣耀9X 麒麟810 4000mAh超强续航 4800万超清夜拍','6.59英寸升降全面屏 全网通6GB+64GB 幻夜黑',45,'/goods-img/d30f7986-bc0f-4ea8-8fbb-94c6bae248f5.jpg','/goods-img/d30f7986-bc0f-4ea8-8fbb-94c6bae248f5.jpg','

商品介绍加载中...

',1599,1599,975,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10712,'荣耀9X 麒麟810 4000mAh超强续航 4800万超清夜拍','6.59英寸升降全面屏 全网通4GB+64GB 魅焰红',45,'/goods-img/95b5df3b-cfec-40bb-8ead-35e0fe7fb7b2.jpg','/goods-img/95b5df3b-cfec-40bb-8ead-35e0fe7fb7b2.jpg','

商品介绍加载中...

',1399,1399,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10713,'荣耀20 李现同款 4800万超广角AI四摄 3200W美颜自拍','麒麟Kirin980全网通版8GB+128GB 蓝水翡翠 全面屏手机',45,'/goods-img/2469b8fa-8117-4409-a8d6-3b52a33b3e51.jpg','/goods-img/2469b8fa-8117-4409-a8d6-3b52a33b3e51.jpg','

商品介绍加载中...

',2699,2499,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10714,'荣耀20 李现同款 4800万超广角AI四摄 3200W美颜自拍','麒麟Kirin980全网通版8GB+128GB 幻夜黑 全面屏手机',45,'/goods-img/474e2ef0-2321-4363-ab31-7a838546f172.jpg','/goods-img/474e2ef0-2321-4363-ab31-7a838546f172.jpg','

商品介绍加载中...

',2699,2499,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10715,'荣耀20 李现同款 4800万超广角AI四摄 3200W美颜自拍','麒麟Kirin980全网通版8GB+128GB 冰岛白 全面屏手机',45,'/goods-img/77d87d20-4fc7-441c-82a8-baf9089fc3ad.jpg','/goods-img/77d87d20-4fc7-441c-82a8-baf9089fc3ad.jpg','

商品介绍加载中...

',2699,2499,994,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10716,'荣耀20 李现同款 4800万超广角AI四摄 3200W美颜自拍','麒麟Kirin980全网通版8GB+128GB 幻影蓝 全面屏手机',45,'/goods-img/1a200710-8c41-4411-8edf-a49575807a08.jpg','/goods-img/1a200710-8c41-4411-8edf-a49575807a08.jpg','

商品介绍加载中...

',2699,2499,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10717,'荣耀20 PRO 李现同款 4800万全焦段AI四摄','双光学防抖 麒麟980 全网通4G 8GB+128GB 冰岛幻境 拍照手机',45,'/goods-img/391cd4e6-6071-41ea-a6fc-d983b30a5470.jpg','/goods-img/391cd4e6-6071-41ea-a6fc-d983b30a5470.jpg','

商品介绍加载中...

',3199,2899,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10718,'荣耀20 PRO 李现同款 4800万全焦段AI四摄','双光学防抖 麒麟980 全网通4G 8GB+128GB 蓝水翡翠 拍照手机',45,'/goods-img/5d7ee18f-ca20-4d72-a803-dc5b03bd80e2.jpg','/goods-img/5d7ee18f-ca20-4d72-a803-dc5b03bd80e2.jpg','

商品介绍加载中...

',3199,2899,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10719,'荣耀20 PRO 李现同款 4800万全焦段AI四摄','双光学防抖 麒麟980 全网通4G 8GB+128GB 幻夜星河 拍照手机',45,'/goods-img/e1505375-d00d-4cd8-a090-a13490b430d5.jpg','/goods-img/e1505375-d00d-4cd8-a090-a13490b430d5.jpg','

商品介绍加载中...

',3199,2899,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10720,'荣耀20 PRO × MOSCHINO联名版','4800万全焦段AI四摄 双光学防抖 麒麟980 8GB+256GB 黑色',45,'/goods-img/0ae89667-8a69-4efc-b8d8-c0ebaf56753a.jpg','/goods-img/0ae89667-8a69-4efc-b8d8-c0ebaf56753a.jpg','

商品介绍加载中...

',3799,3799,997,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10721,'华为 HUAWEI 畅享 9S','6GB+64GB 幻夜黑 全网通 2400万超广角三摄珍珠屏大存储 移动联通电信4G手机 双卡双待',46,'/goods-img/1b96ae9b-8c56-465e-9e82-ff712305e2d9.jpg','/goods-img/1b96ae9b-8c56-465e-9e82-ff712305e2d9.jpg','

商品介绍加载中...

',1499,1199,989,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10722,'华为 HUAWEI 畅享 9S','6GB+64GB 极光蓝 全网通 2400万超广角三摄珍珠屏大存储 移动联通电信4G手机 双卡双待',46,'/goods-img/b49530f5-fe13-42b3-9ca9-6f1367e0f8f8.jpg','/goods-img/b49530f5-fe13-42b3-9ca9-6f1367e0f8f8.jpg','

商品介绍加载中...

',1499,1199,994,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10723,'华为 HUAWEI 畅享 9S','6GB+64GB 珊瑚红 全网通 2400万超广角三摄珍珠屏大存储 移动联通电信4G手机 双卡双待',46,'/goods-img/84397a4c-ff06-4f08-bad5-bd4d5f8e23ff.jpg','/goods-img/84397a4c-ff06-4f08-bad5-bd4d5f8e23ff.jpg','

商品介绍加载中...

',1499,1199,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10724,'荣耀V20 游戏手机 麒麟980芯片 魅眼全视屏','4800万深感相机 6GB+128GB 幻夜黑 移动联通电信4G全面屏手机',45,'/goods-img/7a58b5b2-0101-4a55-9872-d7765f08cf19.jpg','/goods-img/7a58b5b2-0101-4a55-9872-d7765f08cf19.jpg','

商品介绍加载中...

',2199,2099,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10725,'荣耀V20 游戏手机 麒麟980芯片 魅眼全视屏','4800万深感相机 6GB+128GB 魅海蓝 移动联通电信4G全面屏手机',45,'/goods-img/5dd6b4de-0b39-48fc-9285-7356c22edf7b.jpg','/goods-img/5dd6b4de-0b39-48fc-9285-7356c22edf7b.jpg','

商品介绍加载中...

',2199,2099,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10726,'荣耀V20 游戏手机 麒麟980芯片 魅眼全视屏','4800万深感相机 6GB+128GB 幻影蓝 移动联通电信4G全面屏手机',45,'/goods-img/c5a6593b-ef49-42fd-b330-0be8021362d8.jpg','/goods-img/c5a6593b-ef49-42fd-b330-0be8021362d8.jpg','

商品介绍加载中...

',2199,2099,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10727,'荣耀V20 游戏手机 麒麟980芯片 魅眼全视屏','4800万深感相机 6GB+128GB 魅丽红 移动联通电信4G全面屏手机',45,'/goods-img/b57f705a-ef7f-4a9f-a244-3fc980e17555.jpg','/goods-img/b57f705a-ef7f-4a9f-a244-3fc980e17555.jpg','

商品介绍加载中...

',2199,2099,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10728,'荣耀V20 游戏手机 麒麟980芯片 魅眼全视屏','4800万深感相机 6GB+128GB 幻影红 移动联通电信4G全面屏手机',45,'/goods-img/3dd91f7d-8f89-4e8a-a808-fa556ee1ceb3.jpg','/goods-img/3dd91f7d-8f89-4e8a-a808-fa556ee1ceb3.jpg','

商品介绍加载中...

',2199,2099,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10729,'华为 HUAWEI P20 AI智慧徕卡双摄全面屏游戏手机','6GB+128GB 亮黑色 全网通移动联通电信4G手机 双卡双待',46,'/goods-img/f8edc81a-8fbd-425b-8ed7-d6b4c14ec6a1.jpg','/goods-img/f8edc81a-8fbd-425b-8ed7-d6b4c14ec6a1.jpg','

商品介绍加载中...

',3088,2799,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10730,'华为 HUAWEI P20 AI智慧徕卡双摄全面屏游戏手机','6GB+64GB 极光色 全网通移动联通电信4G手机 双卡双待',46,'/goods-img/c17c5292-2c20-4196-88e3-7ea813530db5.jpg','/goods-img/c17c5292-2c20-4196-88e3-7ea813530db5.jpg','

商品介绍加载中...

',2788,2679,997,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10731,'华为 HUAWEI P20 AI智慧徕卡双摄全面屏游戏手机','6GB+64GB 宝石蓝 全网通移动联通电信4G手机 双卡双待',46,'/goods-img/b43bcd55-3709-4c32-b3a2-5b59c80f3610.jpg','/goods-img/b43bcd55-3709-4c32-b3a2-5b59c80f3610.jpg','

商品介绍加载中...

',2788,2699,999,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10732,'华为 HUAWEI P20 AI智慧全面屏','6GB+64GB 极光闪蝶色 全网通版 移动联通电信4G手机 双卡双待',46,'/goods-img/3b183d9a-ac01-4bed-a7bb-1ddeba6ad416.jpg','/goods-img/3b183d9a-ac01-4bed-a7bb-1ddeba6ad416.jpg','

商品介绍加载中...

',2788,2679,998,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10733,'华为 HUAWEI P20 AI智慧全面屏','6GB+64GB 珠光贝母色 全网通版 移动联通电信4G手机 双卡双待',46,'/goods-img/28e94d5d-9ccc-4843-a296-2747530037ce.jpg','/goods-img/28e94d5d-9ccc-4843-a296-2747530037ce.jpg','

商品介绍加载中...

',3388,2988,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10734,'华为 HUAWEI P20 AI智慧徕卡双摄全面屏游戏手机','6GB+128GB 香槟金 全网通移动联通电信4G手机 双卡双待',46,'/goods-img/0b11241e-4d6b-44ea-afb0-e029d1b5a54d.jpg','/goods-img/0b11241e-4d6b-44ea-afb0-e029d1b5a54d.jpg','

商品介绍加载中...

',3888,3888,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10735,'荣耀20S 李现同款 3200万人像超级夜景 4800万超广角AI三摄','麒麟810旗舰级芯片 全网通版6GB+128GB 蝶羽蓝',45,'/goods-img/8883043d-bef3-442c-9ccf-af9c03510c5d.jpg','/goods-img/8883043d-bef3-442c-9ccf-af9c03510c5d.jpg','

商品介绍加载中...

',1899,1899,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10736,'华为 HUAWEI 畅享MAX 4GB+64GB','幻夜黑 全网通版 珍珠屏杜比全景声大电池 移动联通电信4G手机 双卡双待',46,'/goods-img/522ed5b9-bcae-401f-9933-d2e957bb3384.jpg','/goods-img/522ed5b9-bcae-401f-9933-d2e957bb3384.jpg','

商品介绍加载中...

',1199,999,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10737,'华为 HUAWEI 畅享MAX 4GB+64GB','琥珀棕 全网通版 珍珠屏杜比全景声大电池 移动联通电信4G手机 双卡双待',46,'/goods-img/36bdfdb9-21b1-46d5-9534-8b3873c9b6d9.jpg','/goods-img/36bdfdb9-21b1-46d5-9534-8b3873c9b6d9.jpg','

商品介绍加载中...

',1199,999,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10738,'华为 HUAWEI 畅享MAX 4GB+128GB','天际白 全网通版 珍珠屏杜比全景声大电池 移动联通电信4G手机 双卡双待',46,'/goods-img/51fa04cf-1c05-49ee-8dea-0c1757ff32c4.jpg','/goods-img/51fa04cf-1c05-49ee-8dea-0c1757ff32c4.jpg','

商品介绍加载中...

',1899,1399,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10739,'华为 HUAWEI P30 Pro','超感光徕卡四摄10倍混合变焦麒麟980芯片屏内指纹 8GB+128GB极光色全网通版双4G手机',46,'/goods-img/65c8e729-aeca-4780-977b-4d0d39d4aa2e.jpg','/goods-img/65c8e729-aeca-4780-977b-4d0d39d4aa2e.jpg','

商品介绍加载中...

',5488,4988,998,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10740,'华为 HUAWEI P30 Pro','超感光徕卡四摄10倍混合变焦麒麟980芯片屏内指纹 8GB+128GB亮黑色全网通版双4G手机',46,'/goods-img/bc90bb1e-494a-44d4-b180-42a994ec80fc.jpg','/goods-img/bc90bb1e-494a-44d4-b180-42a994ec80fc.jpg','

商品介绍加载中...

',5488,4988,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10741,'华为 HUAWEI P30 Pro','超感光徕卡四摄10倍混合变焦麒麟980芯片屏内指纹 8GB+128GB珠光贝母全网通版双4G手机',46,'/goods-img/a6f309b7-765a-4407-be71-bbd5b764d448.jpg','/goods-img/a6f309b7-765a-4407-be71-bbd5b764d448.jpg','

商品介绍加载中...

',5488,4988,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10742,'华为 HUAWEI P30 Pro','超感光徕卡四摄10倍混合变焦麒麟980芯片屏内指纹 8GB+256GB天空之境全网通版双4G手机',46,'/goods-img/dda1d575-cdac-4eb4-a118-3834490166f7.jpg','/goods-img/dda1d575-cdac-4eb4-a118-3834490166f7.jpg','

商品介绍加载中...

',5988,5488,961,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10743,'华为 HUAWEI P30 Pro','超感光徕卡四摄10倍混合变焦麒麟980芯片屏内指纹 8GB+256GB墨玉蓝全网通版双4G手机',46,'/goods-img/8755a735-baa1-4f17-a9bd-30c4f4f1451b.jpg','/goods-img/8755a735-baa1-4f17-a9bd-30c4f4f1451b.jpg','

商品介绍加载中...

',5988,5488,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10744,'华为 HUAWEI P30 Pro','超感光徕卡四摄10倍混合变焦麒麟980芯片屏内指纹 8GB+128GB赤茶橘全网通版双4G手机',46,'/goods-img/44e78820-86f3-429d-94af-64f6af308846.jpg','/goods-img/44e78820-86f3-429d-94af-64f6af308846.jpg','

商品介绍加载中...

',5488,4988,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10745,'华为 HUAWEI P30 Pro','超感光徕卡四摄10倍混合变焦麒麟980芯片屏内指纹 8GB+128GB嫣紫色全网通版双4G手机',46,'/admin/dist/img/no-img.png','/admin/dist/img/no-img.png','

商品介绍加载中...

',5488,4988,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10746,'华为 HUAWEI nova 5','Pro 前置3200万人像超级夜景4800万AI四摄麒麟980芯片8GB+128GB绮境森林全网通双4G手机',46,'/goods-img/2948815e-043a-4f47-896f-7f6ccf916369.jpg','/goods-img/2948815e-043a-4f47-896f-7f6ccf916369.jpg','

商品介绍加载中...

',2999,2999,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10747,'华为 HUAWEI nova 5','Pro 前置3200万人像超级夜景4800万AI四摄麒麟980芯片8GB+128GB亮黑色全网通双4G手机',46,'/goods-img/df1bea42-9172-4cd5-9fc5-f35bb736108f.jpg','/goods-img/df1bea42-9172-4cd5-9fc5-f35bb736108f.jpg','

商品介绍加载中...

',2999,2999,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10748,'华为 HUAWEI nova 5','Pro 前置3200万人像超级夜景4800万AI四摄麒麟980芯片 8GB+128GB仲夏紫全网通双4G手机',46,'/goods-img/ab6f8463-794f-4f40-87b8-d01e6260ff1c.jpg','/goods-img/ab6f8463-794f-4f40-87b8-d01e6260ff1c.jpg','

商品介绍加载中...

',2999,2999,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10749,'华为 HUAWEI nova 5','Pro 前置3200万人像超级夜景4800万AI四摄麒麟980芯片8GB+128GB苏音蓝全网通双4G手机',46,'/goods-img/98e90b6e-2a5d-462d-8cd1-44699144a0b5.jpg','/goods-img/98e90b6e-2a5d-462d-8cd1-44699144a0b5.jpg','

商品介绍加载中...

',2999,2999,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10750,'华为 HUAWEI nova 5','Pro 前置3200万人像超级夜景4800万AI四摄麒麟980芯片8GB+128GB珊瑚橙全网通双4G手机',46,'/goods-img/ec0bafed-d651-4be7-b2aa-13e84248219a.jpg','/goods-img/ec0bafed-d651-4be7-b2aa-13e84248219a.jpg','

商品介绍加载中...

',2999,2999,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10751,'华为 HUAWEI nova 5','Pro 前置3200万人像超级夜景4800万AI四摄麒麟980芯片8GB+256GB仲夏紫星耀礼盒版全网通',46,'/goods-img/83f39052-5a1c-4769-a7db-cf2bd53d2a29.jpg','/goods-img/83f39052-5a1c-4769-a7db-cf2bd53d2a29.jpg','

商品介绍加载中...

',3799,3599,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10752,'华为 HUAWEI nova 5i','后置AI四摄 极点全面屏 前置2400万高清摄像头 8GB+128GB 苏音蓝 全网通双卡双待',46,'/goods-img/4b2bffff-ec0b-42e0-8152-ada9a121ad31.jpg','/goods-img/4b2bffff-ec0b-42e0-8152-ada9a121ad31.jpg','

商品介绍加载中...

',2199,2199,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10753,'华为 HUAWEI nova 5i','后置AI四摄 极点全面屏 前置2400万高清摄像头 8GB+128GB 幻夜黑 全网通双卡双待',46,'/goods-img/04dce482-ff0e-483c-b324-dfc030b6cdd1.jpg','/goods-img/04dce482-ff0e-483c-b324-dfc030b6cdd1.jpg','

商品介绍加载中...

',2199,2199,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10754,'华为 HUAWEI nova 5i','后置AI四摄 极点全面屏 前置2400万高清摄像头 8GB+128GB 蜜语红 全网通双卡双待',46,'/goods-img/b5e139d3-ea6b-4874-9ccc-c18aca44a8bc.jpg','/goods-img/b5e139d3-ea6b-4874-9ccc-c18aca44a8bc.jpg','

商品介绍加载中...

',2199,2199,996,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10755,'荣耀9X PRO 麒麟810液冷散热 4000mAh超强续航','4800万超广角夜拍三摄 6.59英寸全网通8GB+128GB 幻影紫',45,'/goods-img/86bd80cd-140b-474c-8277-3747332f61b3.jpg','/goods-img/86bd80cd-140b-474c-8277-3747332f61b3.jpg','

商品介绍加载中...

',2199,2199,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10756,'荣耀9X PRO 麒麟810液冷散热 4000mAh超强续航','4800万超广角夜拍三摄 6.59英寸全网通8GB+128GB 幻夜黑',45,'/goods-img/3b008be9-e906-4364-8aa0-0df2e670dbd2.jpg','/goods-img/3b008be9-e906-4364-8aa0-0df2e670dbd2.jpg','

商品介绍加载中...

',2199,2199,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10757,'荣耀畅玩8C两天一充 莱茵护眼 刘海屏 全网通版4GB+32GB','极光蓝 移动联通电信4G全面屏手机 双卡双待',45,'/goods-img/7f7d2343-6743-490b-baec-3e0a76d061e5.jpg','/goods-img/7f7d2343-6743-490b-baec-3e0a76d061e5.jpg','

商品介绍加载中...

',899,799,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10758,'荣耀畅玩8C两天一充 莱茵护眼 刘海屏 全网通版4GB+32GB','铂光金 移动联通电信4G全面屏手机 双卡双待',45,'/goods-img/b163ca1b-7deb-4b15-818a-dc765c852305.jpg','/goods-img/b163ca1b-7deb-4b15-818a-dc765c852305.jpg','

商品介绍加载中...

',899,799,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10759,'荣耀畅玩8C两天一充 莱茵护眼 刘海屏 全网通版4GB+32GB','星云紫 移动联通电信4G全面屏手机 双卡双待',45,'/goods-img/f949289a-4c51-4159-a754-871da347e1e5.jpg','/goods-img/f949289a-4c51-4159-a754-871da347e1e5.jpg','

商品介绍加载中...

',899,799,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10760,'荣耀畅玩8C两天一充 莱茵护眼 刘海屏 全网通版4GB+64GB','幻夜黑 移动联通电信4G全面屏手机 双卡双待',45,'/goods-img/27c3c018-95c5-429f-9ad7-be0fedd78329.jpg','/goods-img/27c3c018-95c5-429f-9ad7-be0fedd78329.jpg','

商品介绍加载中...

',1399,1099,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10761,'荣耀畅玩8C两天一充 莱茵护眼 刘海屏 全网通版4GB+64GB','幻影蓝 移动联通电信4G全面屏手机 双卡双待',45,'/goods-img/61224f59-e11a-4005-84dc-cadfdd4162f6.jpg','/goods-img/61224f59-e11a-4005-84dc-cadfdd4162f6.jpg','

商品介绍加载中...

',1399,1099,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10762,'华为 HUAWEI 畅享8e 青春版','2GB+32GB全面屏 金色 全网通版 移动联通电信4G手机 双卡双待',46,'/goods-img/af23223e-56fa-4aa7-b832-c55c713fa604.jpg','/goods-img/af23223e-56fa-4aa7-b832-c55c713fa604.jpg','

商品介绍加载中...

',699,549,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10763,'华为 HUAWEI 畅享8e青春 2GB+32GB全面屏','黑色 全网通版 移动联通电信4G手机 双卡双待',46,'/goods-img/bf64e22d-1cd3-40b0-9ce1-cc944e35d2d4.jpg','/goods-img/bf64e22d-1cd3-40b0-9ce1-cc944e35d2d4.jpg','

商品介绍加载中...

',699,549,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10764,'华为 HUAWEI 畅享8e青春 2GB+32GB全面屏','蓝色 全网通版 移动联通电信4G手机 双卡双待',46,'/goods-img/70f9ecf9-4859-45de-8f67-5afbdba6735c.jpg','/goods-img/70f9ecf9-4859-45de-8f67-5afbdba6735c.jpg','

商品介绍加载中...

',699,549,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10765,'华为 HUAWEI nova 4e','3200万立体美颜AI超广角三摄珍珠屏6GB+128GB雀翎蓝全网通版双4G手机',46,'/goods-img/55b997f9-fa22-40b0-8b33-429760c2af49.jpg','/goods-img/55b997f9-fa22-40b0-8b33-429760c2af49.jpg','

商品介绍加载中...

',1999,1799,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10766,'华为 HUAWEI nova 4e','3200万立体美颜AI超广角三摄珍珠屏6GB+128GB幻夜黑全网通版双4G手机',46,'/goods-img/8d675ec6-efe0-4ca6-8f83-193820b07256.jpg','/goods-img/8d675ec6-efe0-4ca6-8f83-193820b07256.jpg','

商品介绍加载中...

',1999,1799,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10767,'华为 HUAWEI nova 4e','3200万立体美颜AI超广角三摄珍珠屏6GB+128GB珍珠白全网通版双4G手机',46,'/goods-img/c8ce9a44-7b40-48b2-91cb-2a1607561b4a.jpg','/goods-img/c8ce9a44-7b40-48b2-91cb-2a1607561b4a.jpg','

商品介绍加载中...

',1999,1799,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10768,'华为 HUAWEI 畅享9 Plus','4GB+64GB 极光紫 全网通 四摄超清全面屏大电池 移动联通电信4G手机 双卡双待',46,'/goods-img/5ea16713-f6ae-4fa7-a53d-1700c29cb3d3.jpg','/goods-img/5ea16713-f6ae-4fa7-a53d-1700c29cb3d3.jpg','

商品介绍加载中...

',1299,1199,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10769,'华为 HUAWEI 畅享9 Plus','4GB+64GB 幻夜黑 全网通 四摄超清全面屏大电池 移动联通电信4G手机 双卡双待',46,'/goods-img/39e4b0c8-c4c5-4162-8a32-3bb9bb483503.jpg','/goods-img/39e4b0c8-c4c5-4162-8a32-3bb9bb483503.jpg','

商品介绍加载中...

',1299,1199,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10770,'华为 HUAWEI 畅享9 Plus','4GB+64GB 宝石蓝 全网通 四摄超清全面屏大电池 移动联通电信4G手机 双卡双待',46,'/goods-img/ca2bb115-c75e-475b-93ab-c2436f31aa16.jpg','/goods-img/ca2bb115-c75e-475b-93ab-c2436f31aa16.jpg','

商品介绍加载中...

',1299,1199,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10771,'华为 HUAWEI 畅享9 Plus','4GB+64GB 樱语粉 全网通 四摄超清全面屏大电池 移动联通电信4G手机 双卡双待',46,'/goods-img/65e953c4-1d29-423a-b7d7-4276c4d42aaa.jpg','/goods-img/65e953c4-1d29-423a-b7d7-4276c4d42aaa.jpg','

商品介绍加载中...

',1299,1199,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10772,'华为 HUAWEI nova 3i','全面屏高清四摄游戏手机4GB+128GB 亮黑色 移动4G+ 移动联通电信4G手机双卡双待',46,'/goods-img/2252c604-ced3-4e92-b58b-15402ae7be2c.jpg','/goods-img/2252c604-ced3-4e92-b58b-15402ae7be2c.jpg','

商品介绍加载中...

',1399,1299,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10773,'华为 HUAWEI nova 3i','全面屏高清四摄游戏手机4GB+128GB 蓝楹紫 移动4G+ 移动联通电信4G手机双卡双待',46,'/goods-img/a17dc2b3-17dc-4be7-a04d-12a3fa62de31.jpg','/goods-img/a17dc2b3-17dc-4be7-a04d-12a3fa62de31.jpg','

商品介绍加载中...

',1399,1299,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10774,'华为 HUAWEI nova 5i','Pro 前置3200万人像超级夜景4800万AI四摄极点全面屏6GB+128GB翡冷翠全网通双4G手机',46,'/goods-img/e3f32e21-1208-481d-bfcd-8447de78043b.jpg','/goods-img/e3f32e21-1208-481d-bfcd-8447de78043b.jpg','

商品介绍加载中...

',2199,2149,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10775,'华为 HUAWEI nova 5i','Pro 前置3200万人像超级夜景4800万AI四摄极点全面屏6GB+128GB幻夜黑全网通双4G手机',46,'/goods-img/1eb1e40c-7f38-47ed-a839-d43c1d0b79a8.jpg','/goods-img/1eb1e40c-7f38-47ed-a839-d43c1d0b79a8.jpg','

商品介绍加载中...

',2199,2149,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10776,'华为 HUAWEI nova 5i','Pro 前置3200万人像超级夜景4800万AI四摄极点全面屏6GB+128GB极光色全网通双4G手机',46,'/goods-img/80f05e0d-0d06-4aa8-bca5-0d39a2365b4b.jpg','/goods-img/80f05e0d-0d06-4aa8-bca5-0d39a2365b4b.jpg','

商品介绍加载中...

',2199,2149,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10777,'华为 HUAWEI Mate 20','麒麟980AI智能芯片全面屏超微距影像超大广角徕卡三摄6GB+128GB亮黑色全网通版双4G手机',46,'/goods-img/9024ab8a-be67-4459-8414-8d84225851a7.jpg','/goods-img/9024ab8a-be67-4459-8414-8d84225851a7.jpg','

商品介绍加载中...

',3799,3699,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10778,'华为 HUAWEI Mate 20','麒麟980AI智能芯片全面屏超微距影像超大广角徕卡三摄6GB+128GB极光色全网通版双4G手机',46,'/goods-img/940a6c56-9f7b-4008-8679-c7ef5a44d695.jpg','/goods-img/940a6c56-9f7b-4008-8679-c7ef5a44d695.jpg','

商品介绍加载中...

',3799,3699,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10779,'华为 HUAWEI Mate 20','麒麟980AI智能芯片全面屏超微距影像超大广角徕卡三摄6GB+64GB翡冷翠全网通版双4G手机',46,'/goods-img/08f9a912-f049-4cf8-a839-115fc6582398.jpg','/goods-img/08f9a912-f049-4cf8-a839-115fc6582398.jpg','

商品介绍加载中...

',3299,3199,994,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10780,'华为 HUAWEI Mate 20','麒麟980AI智能芯片全面屏超微距影像超大广角徕卡三摄6GB+128GB宝石蓝全网通版双4G手机',46,'/goods-img/5d57e0ba-1bc7-45a7-9677-f501e0384442.jpg','/goods-img/5d57e0ba-1bc7-45a7-9677-f501e0384442.jpg','

商品介绍加载中...

',3799,3699,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10781,'华为 HUAWEI 麦芒 8','超广角AI三摄 高清珍珠屏 大存储 6GB+128GB 极光蓝 全网通双4G手机',46,'/goods-img/bde7fc16-fb6b-42b0-8950-13ff287c3cd3.jpg','/goods-img/bde7fc16-fb6b-42b0-8950-13ff287c3cd3.jpg','

商品介绍加载中...

',1899,1699,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10782,'华为 HUAWEI 麦芒 8','超广角AI三摄 高清珍珠屏 大存储 6GB+128GB 幻夜黑 全网通双4G手机',46,'/goods-img/e299773e-14e4-4168-adab-514f6c6d35ed.jpg','/goods-img/e299773e-14e4-4168-adab-514f6c6d35ed.jpg','

商品介绍加载中...

',1899,1699,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10783,'华为 HUAWEI 麦芒 8','超广角AI三摄 高清珍珠屏 大存储 6GB+128GB 宝石蓝 全网通双4G手机',46,'/goods-img/2a3fb7d2-cb76-47b2-88c6-db0f869b5718.jpg','/goods-img/2a3fb7d2-cb76-47b2-88c6-db0f869b5718.jpg','

商品介绍加载中...

',1899,1699,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10784,'荣耀8X Max 骁龙660 7.12英寸90%屏占比珍珠屏','6GB+64GB 魅海蓝 移动联通电信4G全面屏手机 双卡双待',45,'/goods-img/c0763005-4e67-4861-98f2-e6a550ec4d87.jpg','/goods-img/c0763005-4e67-4861-98f2-e6a550ec4d87.jpg','

商品介绍加载中...

',1799,1199,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10785,'荣耀8X Max 骁龙660 7.12英寸90%屏占比珍珠屏','6GB+64GB 幻夜黑 移动联通电信4G全面屏手机 双卡双待',45,'/goods-img/aea7760b-d950-4f64-8db9-ef055f15d234.jpg','/goods-img/aea7760b-d950-4f64-8db9-ef055f15d234.jpg','

商品介绍加载中...

',1799,1199,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10786,'荣耀8X Max 骁龙660 7.12英寸90%屏占比珍珠屏','6GB+64GB 魅焰红 移动联通电信4G全面屏手机 双卡双待',45,'/goods-img/f5e2d2e7-541a-44fa-ad5c-4f15f48ebfc9.jpg','/goods-img/f5e2d2e7-541a-44fa-ad5c-4f15f48ebfc9.jpg','

商品介绍加载中...

',1799,1199,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10787,'华为 HUAWEI Mate 10','4GB+64GB 亮黑色 移动4G+手机 双卡双待',46,'/goods-img/b67a4ac6-7766-4995-8110-1bd442ec0797.jpg','/goods-img/b67a4ac6-7766-4995-8110-1bd442ec0797.jpg','

商品介绍加载中...

',1799,1799,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10788,'华为 HUAWEI 畅享9 3GB+32GB','极光蓝 高清珍珠屏 AI长续航 全网通标配版 移动联通电信4G手机',46,'/goods-img/bd8b2d93-c251-46b8-9990-77baaf3075f3.jpg','/goods-img/bd8b2d93-c251-46b8-9990-77baaf3075f3.jpg','

商品介绍加载中...

',999,799,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10789,'华为 HUAWEI 畅享9 3GB+32GB','幻夜黑 高清珍珠屏 AI长续航 全网通标配版 移动联通电信4G手机',46,'/goods-img/71ae1ce8-38e8-4da3-8fa1-5e8157a12685.jpg','/goods-img/71ae1ce8-38e8-4da3-8fa1-5e8157a12685.jpg','

商品介绍加载中...

',999,799,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10790,'华为 HUAWEI 畅享9 4GB+64GB','极光紫 高清珍珠屏 AI长续航 全网通高配版 移动联通电信4G手机',46,'/goods-img/371386b8-ddf4-4fc1-985e-ef0e1a076710.jpg','/goods-img/371386b8-ddf4-4fc1-985e-ef0e1a076710.jpg','

商品介绍加载中...

',1099,1099,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10791,'华为 HUAWEI 畅享9 4GB+64GB','珊瑚红 高清珍珠屏 AI长续航 全网通高配版 移动联通电信4G手机',46,'/goods-img/60392ae1-d076-47b5-a00d-b2278e01ccb5.jpg','/goods-img/60392ae1-d076-47b5-a00d-b2278e01ccb5.jpg','

商品介绍加载中...

',1099,1099,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10792,'荣耀畅玩8A 6.09英寸珍珠全面屏 震撼大音量 3GB+32GB','幻夜黑 移动联通电信4G全面屏手机 双卡双待',45,'/goods-img/0a592388-1535-4f9f-8201-ecb78c48bb3d.jpg','/goods-img/0a592388-1535-4f9f-8201-ecb78c48bb3d.jpg','

商品介绍加载中...

',799,649,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10793,'荣耀畅玩8A 6.09英寸珍珠全面屏 震撼大音量 3GB+32GB','极光蓝 移动联通电信4G全面屏手机 双卡双待',45,'/goods-img/fd218943-8f6f-4fb8-91a4-d6216cc5afdc.jpg','/goods-img/fd218943-8f6f-4fb8-91a4-d6216cc5afdc.jpg','

商品介绍加载中...

',799,649,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10794,'荣耀畅玩8A 6.09英寸珍珠全面屏 震撼大音量 3GB+32GB','魅焰红 移动联通电信4G全面屏手机 双卡双待',45,'/goods-img/54641753-d8e7-45da-8c6c-81192552cf15.jpg','/goods-img/54641753-d8e7-45da-8c6c-81192552cf15.jpg','

商品介绍加载中...

',799,649,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10795,'荣耀畅玩8A 6.09英寸珍珠全面屏 震撼大音量 3GB+32GB','铂光金 移动联通电信4G全面屏手机 双卡双待',45,'/goods-img/7b65ad3d-74a4-4322-8653-6bda47a8b4eb.jpg','/goods-img/7b65ad3d-74a4-4322-8653-6bda47a8b4eb.jpg','

商品介绍加载中...

',799,649,1000,'',0,0,'2019-09-18 13:37:44',0,'2020-10-13 10:41:59'), (10796,'Redmi K20Pro 骁龙855 索尼4800万超广角三摄','AMOLED弹出式全面屏 8GB+256GB 碳纤黑 游戏智能手机 小米 红米',0,'/goods-img/2a05cc6a-3eea-42f9-ab97-2e2529a72099.jpg','/goods-img/2a05cc6a-3eea-42f9-ab97-2e2529a72099.jpg','

商品介绍加载中...

',2999,2699,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10797,'小米9 Pro 5G 全面屏游戏拍照新品手机','新蜂精选',51,'/goods-img/d5fc8bec-0add-48d3-b73b-349a0375e8dc.jpg','/goods-img/d5fc8bec-0add-48d3-b73b-349a0375e8dc.jpg','

商品介绍加载中...

',9999,9999,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10798,'【新品抢购】Redmi Note8 4800万全场景四摄 4000mAh长续航','高通骁龙665 18W快充 小金刚品质保证 4GB+64GB 梦幻蓝 游戏智能手机 小米 红米',0,'/goods-img/e4e4c543-6d9a-4b19-bedf-3f40024cb710.jpg','/goods-img/e4e4c543-6d9a-4b19-bedf-3f40024cb710.jpg','

商品介绍加载中...

',999,999,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10799,'【新品抢购】Redmi Note8 4800万全场景四摄 4000mAh长续航','高通骁龙665 18W快充 小金刚品质保证 4GB+64GB 皓月白 游戏智能手机 小米 红米',0,'/goods-img/87e0f6ab-45ef-4710-a5f4-e57a470b6b26.jpg','/goods-img/87e0f6ab-45ef-4710-a5f4-e57a470b6b26.jpg','

商品介绍加载中...

',999,999,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10800,'【新品抢购】Redmi Note8 4800万全场景四摄 4000mAh长续航','高通骁龙665 18W快充 小金刚品质保证 4GB+64GB 曜石黑 游戏智能手机 小米 红米',0,'/goods-img/4a5c5b20-2dd3-4343-a6d1-31195c9edea4.jpg','/goods-img/4a5c5b20-2dd3-4343-a6d1-31195c9edea4.jpg','

商品介绍加载中...

',999,999,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10801,'Redmi Note7 4800万双摄千元机 满血骁龙660','18个月超长质保 4000mAh超长续航 6GB+64GB 镜花水月 游戏智能手机 小米 红米',0,'/goods-img/30ef1f51-f958-486f-8d79-f48f6d8293dd.jpg','/goods-img/30ef1f51-f958-486f-8d79-f48f6d8293dd.jpg','

商品介绍加载中...

',1199,1099,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10802,'Redmi Note7 4800万双摄千元机 满血骁龙660','18个月超长质保 4000mAh超长续航 6GB+64GB 亮黑色 游戏智能手机 小米 红米',20,'/goods-img/92beacb0-f692-42ff-a20f-8fecd2b0c046.jpg','/goods-img/92beacb0-f692-42ff-a20f-8fecd2b0c046.jpg','

*相机默认1200w,如何设置4800w? 打开相机 — 右滑切换到“专业”模式 — 点击屏幕左上方的“48MP”,打开4800万超清。 

*如何设置全面屏模式? 点击设置 — 点击全面屏 — 进入全面屏设置会出现两个选项,可以选择经典导航也可以选择全面屏手势。选择全面屏手势,可进行手势学习,使用全面屏模式进行操作 

*是否支持OTG功能? 支持。 

*红米Note7出厂预装版本是Andriod 9.0吗? 该商品首批出厂操作系统:MIUI 10 (Andriod 9.0)。 

*有呼吸灯吗?是否支持NFC? 是否支持收音机? 有呼吸灯,不支持NFC,支持收音机。

*4800万模式是否支持AI场景识别,能否有快速切换方式介绍? 4800万模式下不支持AI场景识别,普通相机模式下可支持AI识别。

*是否支持王者荣耀Vulkan 模式? 目前暂不支持王者荣耀Vulkan模式。      Redmi 红米Note 7 常见问题

',1199,1099,1000,'手机',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10803,'Redmi Note7 4800万双摄千元机 满血骁龙660','18个月超长质保 4000mAh超长续航 6GB+64GB 暮光金 游戏智能手机 小米 红米',0,'/goods-img/0cf95c37-2665-4894-bd42-5f8de06c6d94.jpg','/goods-img/0cf95c37-2665-4894-bd42-5f8de06c6d94.jpg','

*相机默认1200w,如何设置4800w? 打开相机 — 右滑切换到“专业”模式 — 点击屏幕左上方的“48MP”,打开4800万超清。 

*如何设置全面屏模式? 点击设置 — 点击全面屏 — 进入全面屏设置会出现两个选项,可以选择经典导航也可以选择全面屏手势。选择全面屏手势,可进行手势学习,使用全面屏模式进行操作 

*是否支持OTG功能? 支持。 

*红米Note7出厂预装版本是Andriod 9.0吗? 该商品首批出厂操作系统:MIUI 10 (Andriod 9.0)。 

*有呼吸灯吗?是否支持NFC? 是否支持收音机? 有呼吸灯,不支持NFC,支持收音机。

*4800万模式是否支持AI场景识别,能否有快速切换方式介绍? 4800万模式下不支持AI场景识别,普通相机模式下可支持AI识别。

*是否支持王者荣耀Vulkan 模式? 目前暂不支持王者荣耀Vulkan模式。      Redmi 红米Note 7 常见问题

',1199,1099,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10804,'Redmi Note7 4800万双摄千元机 满血骁龙660','4000mAh超长续航 6GB+64GB 梦幻蓝 游戏智能手机 小米 红米',0,'/goods-img/f6c46245-b957-41ed-b235-133c17cba7f9.jpg','/goods-img/f6c46245-b957-41ed-b235-133c17cba7f9.jpg','

*相机默认1200w,如何设置4800w? 打开相机 — 右滑切换到“专业”模式 — 点击屏幕左上方的“48MP”,打开4800万超清。 

*如何设置全面屏模式? 点击设置 — 点击全面屏 — 进入全面屏设置会出现两个选项,可以选择经典导航也可以选择全面屏手势。选择全面屏手势,可进行手势学习,使用全面屏模式进行操作 

*是否支持OTG功能? 支持。 

*红米Note7出厂预装版本是Andriod 9.0吗? 该商品首批出厂操作系统:MIUI 10 (Andriod 9.0)。 

*有呼吸灯吗?是否支持NFC? 是否支持收音机? 有呼吸灯,不支持NFC,支持收音机。

*4800万模式是否支持AI场景识别,能否有快速切换方式介绍? 4800万模式下不支持AI场景识别,普通相机模式下可支持AI识别。

*是否支持王者荣耀Vulkan 模式? 目前暂不支持王者荣耀Vulkan模式。      Redmi 红米Note 7 常见问题

',1199,1099,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10805,'【新品抢购】Redmi Note8Pro 6400万全场景四摄 液冷游戏芯','4500mAh长续航 NFC 18W快充 红外遥控 6GB+64GB 贝母白 游戏智能手机 小米 红米',0,'/goods-img/54985ce7-1df6-442f-9a28-0ff0bab924bd.jpg','/goods-img/54985ce7-1df6-442f-9a28-0ff0bab924bd.jpg','

商品介绍加载中...

',1399,1399,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10806,'【新品抢购】Redmi Note8Pro 6400万全场景四摄 液冷游戏芯','4500mAh长续航 NFC 18W快充 红外遥控 6GB+128GB 冰翡翠 游戏智能手机 小米 红米',0,'/goods-img/e3de1717-e373-4544-9f1e-057a91fd2595.jpg','/goods-img/e3de1717-e373-4544-9f1e-057a91fd2595.jpg','

商品介绍加载中...

',1599,1599,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10807,'【新品抢购】Redmi Note8Pro 6400万全场景四摄 液冷游戏芯','4500mAh长续航 NFC 18W快充 红外遥控 6GB+64GB 电光灰 游戏智能手机 小米 红米',0,'/goods-img/a1552f03-58ab-4b05-91ec-7df52af18a66.jpg','/goods-img/a1552f03-58ab-4b05-91ec-7df52af18a66.jpg','

商品介绍加载中...

',1399,1399,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10808,'Redmi Note7Pro 索尼4800万超清双摄 骁龙675','18个月超长质保 4000mAh超长续航 6GB+128GB 亮黑色 游戏智能手机 小米 红米',0,'/goods-img/647470fa-85b1-4626-99d0-d5b7512c8f23.jpg','/goods-img/647470fa-85b1-4626-99d0-d5b7512c8f23.jpg','

商品介绍加载中...

',1399,1399,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10809,'Redmi Note7pro 索尼4800万超清双摄 骁龙675','18个月超长质保 4000mAh超长续航 6GB+128GB 镜花水月 游戏智能手机 小米 红米',0,'/goods-img/edb8a694-84a5-47da-9bae-30f7a69d2c63.jpg','/goods-img/edb8a694-84a5-47da-9bae-30f7a69d2c63.jpg','

商品介绍加载中...

',1399,1399,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10810,'Redmi Note7Pro 索尼4800万超清双摄 骁龙675','18个月超长质保 4000mAh超长续航 6GB+128GB 梦幻蓝 游戏智能手机 小米 红米',0,'/goods-img/c76edfa6-c16e-45b9-9119-46d300739112.jpg','/goods-img/c76edfa6-c16e-45b9-9119-46d300739112.jpg','

商品介绍加载中...

',1399,1399,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10811,'Redmi Note7Pro 索尼4800万超清双摄 骁龙675','18个月超长质保 4000mAh超长续航 6GB+128GB 暮光金 游戏智能手机 小米 红米',0,'/goods-img/bf0c2d17-3630-4709-af38-d7bd14a76f22.jpg','/goods-img/bf0c2d17-3630-4709-af38-d7bd14a76f22.jpg','

商品介绍加载中...

',1399,1399,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10812,'Redmi 7A 4000mAh超长续航 AI人脸解锁','骁龙8核 标配10W充电器 整机防泼溅 3GB+32GB 磨砂黑 游戏智能手机 小米 红米',0,'/goods-img/28c56015-cb20-44cb-86fb-246ad509e828.jpg','/goods-img/28c56015-cb20-44cb-86fb-246ad509e828.jpg','

商品介绍加载中...

',699,599,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10813,'Redmi 7A 4000mAh超长续航 AI人脸解锁','骁龙8核 标配10W充电器 整机防泼溅 3GB+32GB 晨曦蓝 游戏智能手机 小米 红米',0,'/goods-img/d845c984-f749-4f22-86a5-558677b1322c.jpg','/goods-img/d845c984-f749-4f22-86a5-558677b1322c.jpg','

商品介绍加载中...

',699,599,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10814,'Redmi 7A 4000mAh超长续航 AI人脸解锁','骁龙8核 标配10W充电器 整机防泼溅 3GB+32GB 雾光金 游戏智能手机 小米 红米',0,'/goods-img/56ac4c58-8742-40c8-b130-83b4d2925a8c.jpg','/goods-img/56ac4c58-8742-40c8-b130-83b4d2925a8c.jpg','

商品介绍加载中...

',599,599,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10815,'Redmi 7 4000mAh超长续航 骁龙632','1200万AI双摄 18个月超长质保 AI人脸解锁 3GB+32GB 亮黑色 游戏智能手机 小米 红米',0,'/goods-img/0647d1b4-d19a-4424-b6ac-68344addacb4.jpg','/goods-img/0647d1b4-d19a-4424-b6ac-68344addacb4.jpg','

商品介绍加载中...

',699,699,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10816,'Redmi 7 4000mAh超长续航 骁龙632','1200万AI双摄 18个月超长质保 AI人脸解锁 3GB+32GB 魅夜红 游戏智能手机 小米 红米',0,'/goods-img/711c54f0-f9d0-472e-b61b-94e25c628599.jpg','/goods-img/711c54f0-f9d0-472e-b61b-94e25c628599.jpg','

商品介绍加载中...

',699,699,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10817,'Redmi 7 4000mAh超长续航 骁龙632','1200万AI双摄 18个月超长质保 AI人脸解锁 3GB+32GB 梦幻蓝 游戏智能手机 小米 红米',0,'/goods-img/c8c97b68-3ba6-4f97-8940-d04c9e7c7302.jpg','/goods-img/c8c97b68-3ba6-4f97-8940-d04c9e7c7302.jpg','

商品介绍加载中...

',699,699,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10818,'小米MIX2S 骁龙845 AI感光双摄 四曲面陶瓷全面屏','白色 多功能 NFC 6GB+128GB 游戏智能拍照手机',51,'/goods-img/d423bb5c-60c8-4b66-bd72-3490b5d6461b.jpg','/goods-img/d423bb5c-60c8-4b66-bd72-3490b5d6461b.jpg','

商品介绍加载中...

',2099,1799,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10819,'小米MIX2S 骁龙845 AI感光双摄 四曲面陶瓷全面屏','黑色 多功能 NFC 6GB+128GB 游戏智能拍照手机',51,'/goods-img/9a554cae-5bec-4964-992f-e2f4de192e2c.jpg','/goods-img/9a554cae-5bec-4964-992f-e2f4de192e2c.jpg','

商品介绍加载中...

',2099,1799,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10820,'小米9 4800万超广角三摄 6GB+128GB全息幻彩蓝 骁龙855','全网通4G 双卡双待 水滴全面屏拍照智能游戏手机',51,'/goods-img/55a6dc67-1ed9-421a-9782-acdfa9c123e1.jpg','/goods-img/55a6dc67-1ed9-421a-9782-acdfa9c123e1.jpg','

商品介绍加载中...

',2799,2599,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10821,'小米9 4800万超广角三摄 8GB+256GB 透明版','骁龙855 全网通4G 双卡双待 水滴全面屏拍照智能游戏手机',51,'/goods-img/54249648-d37b-4b22-80dc-243e58ed56a1.jpg','/goods-img/54249648-d37b-4b22-80dc-243e58ed56a1.jpg','

商品介绍加载中...

',3699,3699,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10822,'小米9 4800万超广角三摄 8GB+128GB 深空灰','骁龙855 全网通4G 双卡双待 水滴全面屏拍照智能游戏手机',51,'/goods-img/e8087861-89fd-43af-b64d-290864b0fe35.jpg','/goods-img/e8087861-89fd-43af-b64d-290864b0fe35.jpg','

商品介绍加载中...

',2999,2799,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10823,'小米9 4800万超广角三摄 8GB+128GB 全息幻彩紫','骁龙855 全网通4G 双卡双待 水滴全面屏拍照智能游戏手机',51,'/goods-img/7a406989-061b-4f69-baa1-6fa499aa091d.jpg','/goods-img/7a406989-061b-4f69-baa1-6fa499aa091d.jpg','

商品介绍加载中...

',2999,2799,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10824,'小米CC9e 索尼4800万旗舰相机 3200万美颜自拍 4030mAh','屏幕指纹 白色恋人 6GB+64GB 游戏智能拍照手机',51,'/goods-img/8fc9776e-9393-421d-998c-e516b3877dba.jpg','/goods-img/8fc9776e-9393-421d-998c-e516b3877dba.jpg','

商品介绍加载中...

',1399,1299,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10825,'小米CC9e 索尼4800万旗舰相机 3200万美颜自拍 4030mAh','屏幕指纹 暗夜王子 6GB+64GB 游戏智能拍照手机',51,'/goods-img/033685d7-bf11-4389-9e52-ef5a51182306.jpg','/goods-img/033685d7-bf11-4389-9e52-ef5a51182306.jpg','

商品介绍加载中...

',1399,1299,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10826,'小米CC9e 索尼4800万旗舰相机 3200万美颜自拍 4030mAh','屏幕指纹 深蓝星球 6GB+64GB 游戏智能拍照手机',51,'/goods-img/e8dba692-7fda-4f42-b0ee-6f51ca7dc77d.jpg','/goods-img/e8dba692-7fda-4f42-b0ee-6f51ca7dc77d.jpg','

商品介绍加载中...

',1399,1299,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10827,'小米CC9 3200万美颜自拍 索尼4800万超清三摄 多功能NFC','4030mAh 深蓝星球 6GB+64GB 游戏智能拍照手机',51,'/goods-img/387afca1-a14a-4ab8-9d99-120b7095029c.jpg','/goods-img/387afca1-a14a-4ab8-9d99-120b7095029c.jpg','

商品介绍加载中...

',1799,1799,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10828,'小米CC9 3200万美颜自拍 索尼4800万超清三摄 多功能NFC','4030mAh 白色恋人 6GB+64GB 游戏智能拍照手机',51,'/goods-img/f96f376e-8341-4bad-ad2a-b3f12486958a.jpg','/goods-img/f96f376e-8341-4bad-ad2a-b3f12486958a.jpg','

商品介绍加载中...

',1799,1799,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10829,'小米CC9 3200万美颜自拍 索尼4800万超清三摄 多功能NFC','4030mAh 暗夜王子 6GB+128GB 游戏智能拍照手机',51,'/goods-img/4c148e8e-7e26-4c74-a3d3-f5f37ae9248d.jpg','/goods-img/4c148e8e-7e26-4c74-a3d3-f5f37ae9248d.jpg','

商品介绍加载中...

',1999,1999,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10830,'小米CC9美图定制版 索尼4800万AI三摄 3200万美颜自拍 全身美型','多功能NFC 8GB+256GB 游戏智能拍照手机',51,'/goods-img/92482741-3637-4cd3-91ff-cc5aeb0d3316.jpg','/goods-img/92482741-3637-4cd3-91ff-cc5aeb0d3316.jpg','

商品介绍加载中...

',2599,2599,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10831,'小米Play 流光渐变AI双摄 6GB+128GB 梦幻蓝','移动4G+ 双卡双待 小水滴全面屏拍照游戏智能手机',51,'/goods-img/f0b19f6c-6a8b-4128-8e5d-2e4953331c46.jpg','/goods-img/f0b19f6c-6a8b-4128-8e5d-2e4953331c46.jpg','

商品介绍加载中...

',999,999,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10832,'小米Play 流光渐变AI双摄 6GB+128GB 黑色','移动4G+ 双卡双待 小水滴全面屏拍照游戏智能手机',51,'/goods-img/e39da33d-1b55-4e97-b8e6-824ac2cd1062.jpg','/goods-img/e39da33d-1b55-4e97-b8e6-824ac2cd1062.jpg','

商品介绍加载中...

',999,999,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10833,'小米Play 流光渐变AI双摄 6GB+64GB 暮光金','全网通4G 双卡双待 小水滴全面屏拍照游戏智能手机',51,'/goods-img/2a93185a-8d3b-4908-af8c-c17db78e2fb0.jpg','/goods-img/2a93185a-8d3b-4908-af8c-c17db78e2fb0.jpg','

商品介绍加载中...

',899,899,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10834,'小米9SE 骁龙712 索尼4800万超广角三摄 5.97英寸舒适握感','全息幻彩蓝 8GB+128GB 游戏智能拍照手机',51,'/goods-img/b28f3eac-0091-442f-90f3-68914bf947c7.jpg','/goods-img/b28f3eac-0091-442f-90f3-68914bf947c7.jpg','

商品介绍加载中...

',2099,1899,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10835,'小米9 SE 4800万超广角三摄 骁龙712','水滴全面屏 游戏智能拍照手机 6GB+64GB 深空灰 全网通4G 双卡双待',51,'/goods-img/ef8370c4-ed8e-497f-9e10-185de4d01fe9.jpg','/goods-img/ef8370c4-ed8e-497f-9e10-185de4d01fe9.jpg','

商品介绍加载中...

',1799,1599,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10836,'小米9SE 骁龙712 索尼4800万超广角三摄 5.97英寸舒适握感','全息幻彩紫 8GB+128GB 游戏智能拍照手机',51,'/goods-img/f436d00b-2253-4dcc-8b4a-d82e99af275a.jpg','/goods-img/f436d00b-2253-4dcc-8b4a-d82e99af275a.jpg','

商品介绍加载中...

',2099,1999,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10837,'小米MIX3 骁龙845AIE AI 双摄','磁动力滑盖全面屏 三星 AMOLED屏幕 黑色 8GB+128GB 游戏智能拍照手机',51,'/goods-img/3bfc7c72-b56a-4088-8acf-e01e830ce72a.jpg','/goods-img/3bfc7c72-b56a-4088-8acf-e01e830ce72a.jpg','

商品介绍加载中...

',2599,2599,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10838,'Redmi K20 索尼4800万超广角三摄 AMOLED弹出式全面屏','第七代屏下指纹 6GB+128GB 冰川蓝 游戏智能手机 小米 红米',0,'/goods-img/ed860c53-955b-4cfd-b605-a8b4bb959e2f.jpg','/goods-img/ed860c53-955b-4cfd-b605-a8b4bb959e2f.jpg','

商品介绍加载中...

',1999,1799,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10839,'Redmi K20 索尼4800万超广角三摄 AMOLED弹出式全面屏','第七代屏下指纹 6GB+128GB 火焰红 游戏智能手机 小米 红米',0,'/goods-img/8e64ea39-5477-482c-a200-2c12fdeff004.jpg','/goods-img/8e64ea39-5477-482c-a200-2c12fdeff004.jpg','

商品介绍加载中...

',1999,1799,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10840,'Redmi K20 索尼4800万超广角三摄 AMOLED弹出式全面屏','第七代屏下指纹 6GB+128GB 碳纤黑 游戏智能手机 小米 红米',0,'/goods-img/38a69084-0bc4-479e-a5ba-aed135dee974.jpg','/goods-img/38a69084-0bc4-479e-a5ba-aed135dee974.jpg','

商品介绍加载中...

',1999,1799,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10841,'红米6A 1300万高清相机 AI人脸解锁 12nm高性能处理器','3GB+32GB 流沙金 游戏智能手机 小米',0,'/goods-img/6c77e8f9-11d8-42c3-925e-4396d0d3709f.jpg','/goods-img/6c77e8f9-11d8-42c3-925e-4396d0d3709f.jpg','

商品介绍加载中...

',649,599,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10842,'红米6A 1300万高清相机 AI人脸解锁 12nm高性能处理器','3GB+32GB 铂银灰 游戏智能手机 小米',0,'/goods-img/17b2eb9f-7289-45f8-b26a-114ec29ceb3c.jpg','/goods-img/17b2eb9f-7289-45f8-b26a-114ec29ceb3c.jpg','

商品介绍加载中...

',649,599,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10843,'小米 红米6A 全网通版 2GB内存','樱花粉 16GB 移动联通电信4G手机 双卡双待',51,'/goods-img/1ba819c2-dc89-41d9-86a9-4649418972da.jpg','/goods-img/1ba819c2-dc89-41d9-86a9-4649418972da.jpg','

商品介绍加载中...

',549,549,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10844,'红米6A 1300万高清相机 AI人脸解锁 12nm高性能处理器','3GB+32GB 巴厘蓝 游戏智能手机 小米',0,'/goods-img/1ef84d7e-d804-4064-9140-a53607aa8df2.jpg','/goods-img/1ef84d7e-d804-4064-9140-a53607aa8df2.jpg','

商品介绍加载中...

',649,599,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10845,'小米Max3 5500mAh充电宝级电量 AI双摄 全金属机身','骁龙八核处理器 蓝色 6GB+128GB 游戏智能拍照手机',51,'/goods-img/b6c3eea7-9d34-4ac0-ba66-2fde6f26253b.jpg','/goods-img/b6c3eea7-9d34-4ac0-ba66-2fde6f26253b.jpg','

商品介绍加载中...

',1599,1499,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10846,'小米Max3 5500mAh充电宝级电量 AI双摄 全金属机身','骁龙八核处理器 黑色 6GB+128GB 游戏智能拍照手机',51,'/goods-img/30574476-f5bc-4f3c-80f6-4da22ea48f48.jpg','/goods-img/30574476-f5bc-4f3c-80f6-4da22ea48f48.jpg','

商品介绍加载中...

',1599,1499,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10847,'小米Max3 5500mAh充电宝级电量 AI双摄 全金属机身','骁龙八核处理器 金色 6GB+128GB 游戏智能拍照手机',51,'/goods-img/114e92f8-bf78-481e-8d8a-9936d026d9d4.jpg','/goods-img/114e92f8-bf78-481e-8d8a-9936d026d9d4.jpg','

商品介绍加载中...

',1599,1499,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10848,'Redmi Note8 4800万全场景四摄 4000mAh长续航','高通骁龙665 18W快充 小金刚品质保证 4GB+64GB 梦幻蓝 游戏智能手机 小米 红米',0,'/goods-img/8d3ebf2d-8da7-478c-bd6c-e7a869fdde97.jpg','/goods-img/8d3ebf2d-8da7-478c-bd6c-e7a869fdde97.jpg','

商品介绍加载中...

',999,999,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10849,'Redmi Note8 4800万全场景四摄 4000mAh长续航','高通骁龙665 18W快充 小金刚品质保证 4GB+64GB 皓月白 游戏智能手机 小米 红米',0,'/goods-img/b4ff98bc-ad00-48f7-ac64-0d52780d4c48.jpg','/goods-img/b4ff98bc-ad00-48f7-ac64-0d52780d4c48.jpg','

商品介绍加载中...

',999,999,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10850,'Redmi Note8 4800万全场景四摄 4000mAh长续航','高通骁龙665 18W快充 小金刚品质保证 4GB+64GB 曜石黑 游戏智能手机 小米 红米',0,'/goods-img/b82cc8fd-075b-44d3-b211-8ea633fe2ffe.jpg','/goods-img/b82cc8fd-075b-44d3-b211-8ea633fe2ffe.jpg','

商品介绍加载中...

',999,999,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10851,'小米(MI) 小米8青春版 手机 深空灰','全网通 6G+128G',51,'/goods-img/52425573-6311-4877-bad8-1c04bf01e9d3.jpg','/goods-img/52425573-6311-4877-bad8-1c04bf01e9d3.jpg','

商品介绍加载中...

',1599,1168,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10852,'小米(MI) 小米8青春版 手机 梦幻蓝','全网通 4G+128G',51,'/goods-img/8c1c9fb2-26aa-4fa0-b9ce-cf278d827fa6.jpg','/goods-img/8c1c9fb2-26aa-4fa0-b9ce-cf278d827fa6.jpg','

商品介绍加载中...

',1599,1599,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10853,'小米(MI) 小米8青春版 手机 暮光金','全网通 6G+64G',51,'/goods-img/bd94d7e0-f56f-4b7f-8653-b8a4e267bd15.jpg','/goods-img/bd94d7e0-f56f-4b7f-8653-b8a4e267bd15.jpg','

商品介绍加载中...

',1299,1068,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10854,'小米 红米Note8 pro 手机【6400万四摄','液冷游戏芯】 冰翡翠 全网通6+128',51,'/goods-img/42913aa4-4a49-4121-9c80-3434c12d0ac9.jpg','/goods-img/42913aa4-4a49-4121-9c80-3434c12d0ac9.jpg','

商品介绍加载中...

',1799,1599,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10855,'小米 红米Note8 pro 手机【6400万四摄','液冷游戏芯】 贝母白 全网通6+128',51,'/goods-img/777ebd38-965d-4c77-970e-f1e25022255f.jpg','/goods-img/777ebd38-965d-4c77-970e-f1e25022255f.jpg','

商品介绍加载中...

',1799,1599,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10856,'小米 红米Note8 pro 手机【6400万四摄','液冷游戏芯】 电光灰 全网通6+128',51,'/goods-img/db21f41b-34ac-4bc7-a50f-1f812b1522d1.jpg','/goods-img/db21f41b-34ac-4bc7-a50f-1f812b1522d1.jpg','

商品介绍加载中...

',1799,1599,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10857,'小米(MI) 小米8 游戏手机 黑','6GB+64GB',51,'/goods-img/63588dfb-f85f-41a2-8198-c7ae66aa0261.png','/goods-img/63588dfb-f85f-41a2-8198-c7ae66aa0261.png','

商品介绍加载中...

',1698,1568,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10858,'小米(MI) 小米8 游戏手机 白','6GB+64GB',51,'/goods-img/d55d6e4a-99e7-4a3d-86a4-9b3899a63b42.png','/goods-img/d55d6e4a-99e7-4a3d-86a4-9b3899a63b42.png','

商品介绍加载中...

',1698,1568,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10859,'小米(MI) 小米8 游戏手机 蓝','8GB+128GB',51,'/goods-img/5a2a90aa-fe2c-4bb0-8d8d-1ac1613f453a.png','/goods-img/5a2a90aa-fe2c-4bb0-8d8d-1ac1613f453a.png','

商品介绍加载中...

',1998,1868,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10860,'小米(MI) 小米8 游戏手机 金','6GB+128GB',51,'/goods-img/c1cdb555-f605-4226-906a-022483612319.png','/goods-img/c1cdb555-f605-4226-906a-022483612319.png','

商品介绍加载中...

',1898,1838,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10861,'小米(MI) 小米8青春版 手机 深空灰','全网通(6G+128G)',51,'/goods-img/fafda3af-7741-47f2-936e-c0d9030fbf5b.png','/goods-img/fafda3af-7741-47f2-936e-c0d9030fbf5b.png','

商品介绍加载中...

',1188,1188,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10862,'小米(MI) 小米8青春版 手机 梦幻蓝','全网通(6G+64G)',51,'/goods-img/ef5ac8cb-5d4e-4dc6-bece-27c9ff5a2e1c.png','/goods-img/ef5ac8cb-5d4e-4dc6-bece-27c9ff5a2e1c.png','

商品介绍加载中...

',1388,1388,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10863,'小米(MI) 小米8青春版 手机 暮光金','全网通(6G+128G)',51,'/goods-img/d8b30b9f-faa4-4a0d-84bc-53b9c4745977.png','/goods-img/d8b30b9f-faa4-4a0d-84bc-53b9c4745977.png','

商品介绍加载中...

',1578,1578,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10864,'小米 红米Redmi 7 全网通4G','双卡双待 幻彩渐变AI双摄 水滴全面屏拍照游戏智能手机 梦幻蓝 4GB+64GB',51,'/goods-img/18ce5224-c98d-4a9c-a024-5ac5b6f9a2d7.jpg','/goods-img/18ce5224-c98d-4a9c-a024-5ac5b6f9a2d7.jpg','

商品介绍加载中...

',1200,808,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10865,'小米 红米Redmi 7 全网通4G','双卡双待 幻彩渐变AI双摄 水滴全面屏拍照游戏智能手机 亮黑色 4GB+64GB',51,'/goods-img/f7a9a98d-9e3f-4443-b8a7-5612bcd7c1d0.jpg','/goods-img/f7a9a98d-9e3f-4443-b8a7-5612bcd7c1d0.jpg','

商品介绍加载中...

',1200,818,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10866,'小米 红米Redmi 7 全网通4G','双卡双待 幻彩渐变AI双摄 水滴全面屏拍照游戏智能手机 魅夜红 4GB+64GB',51,'/goods-img/02523f49-742b-4c45-b59b-f550fe5a60ae.jpg','/goods-img/02523f49-742b-4c45-b59b-f550fe5a60ae.jpg','

商品介绍加载中...

',1200,818,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10867,'小米 小米8屏幕指纹版 手机 黑色','全网通(6G + 128G )',51,'/goods-img/35b9c185-2ca6-4052-af40-2abd2157f200.png','/goods-img/35b9c185-2ca6-4052-af40-2abd2157f200.png','产品信息Product Information',2099,1808,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10868,'小米 小米8屏幕指纹版 手机 透明版','全网通(8G + 128G)',51,'/goods-img/fcd1faf9-10b5-4318-b92b-36105be8752f.png','/goods-img/fcd1faf9-10b5-4318-b92b-36105be8752f.png','产品信息Product Information',2499,2028,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10869,'小米 小米8屏幕指纹版 手机 暮光金','全网通(6G + 128G )',51,'/goods-img/e9818435-c510-4042-91e1-734a818a2577.png','/goods-img/e9818435-c510-4042-91e1-734a818a2577.png','产品信息Product Information',2099,2099,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10870,'小米 红米6 全网通版 3GB内存','流沙金 32GB 移动联通电信4G手机 双卡双待',51,'/goods-img/515706fb-a5f8-4d72-a08e-7523cf4ea113.jpg','/goods-img/515706fb-a5f8-4d72-a08e-7523cf4ea113.jpg','

商品介绍加载中...

',699,699,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10871,'小米 红米6 3GB+32GB 铂银灰','全网通4G手机 双卡双待 老人机 智能拍照手机',51,'/goods-img/bcec0048-e992-4e57-9aaf-ddbd9fe852ce.jpg','/goods-img/bcec0048-e992-4e57-9aaf-ddbd9fe852ce.jpg','

商品介绍加载中...

',699,699,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10872,'小米(MI) 小米8屏幕指纹版 全面屏游戏手机 曜石黑(屏幕指纹版)','6G+128G',51,'/goods-img/e1c2b06f-fd06-4242-acb7-9ebd7179181b.png','/goods-img/e1c2b06f-fd06-4242-acb7-9ebd7179181b.png','

商品介绍加载中...

',2199,1818,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10873,'小米(MI) 小米8屏幕指纹版 全面屏游戏手机 透明版(屏幕指纹版)','8G+128G',51,'/goods-img/314274fc-1ee0-474d-bbb5-b9c70a8a9573.png','/goods-img/314274fc-1ee0-474d-bbb5-b9c70a8a9573.png','

商品介绍加载中...

',2599,2018,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10874,'小米(MI) 小米8屏幕指纹版 全面屏游戏手机 暮光金(屏幕指纹版)','8G+128G',51,'/goods-img/c2905bd8-bd68-4672-bada-b8a202a9327e.png','/goods-img/c2905bd8-bd68-4672-bada-b8a202a9327e.png','

商品介绍加载中...

',2599,2058,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10875,'小米8 游戏手机 全面屏 黑色','全网通(6G+64G)',51,'/goods-img/5afd1749-a3bc-41c2-90b2-928ede8aedda.jpg','/goods-img/5afd1749-a3bc-41c2-90b2-928ede8aedda.jpg','

商品介绍加载中...

',1799,1558,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10876,'小米8 游戏手机 全面屏 白色','全网通(6G+64G)',51,'/goods-img/a96dd5bc-2d74-4d57-9336-45a8ac09a363.jpg','/goods-img/a96dd5bc-2d74-4d57-9336-45a8ac09a363.jpg','

商品介绍加载中...

',1799,1550,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10877,'小米8 游戏手机 全面屏 白色','全网通(6G+128G)',51,'/goods-img/25e44283-a440-4e64-bb27-1887370c3d2e.jpg','/goods-img/25e44283-a440-4e64-bb27-1887370c3d2e.jpg','

商品介绍加载中...

',1999,1798,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10878,'小米8 游戏手机 全面屏 金色','全网通(6G+128G)',51,'/goods-img/6b5e5711-8ae6-4f66-bd22-30c9be85d3c6.jpg','/goods-img/6b5e5711-8ae6-4f66-bd22-30c9be85d3c6.jpg','

商品介绍加载中...

',1999,1849,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10879,'小米8 游戏手机 全面屏 黑色','全网通(6G+128G)',51,'/goods-img/040a3aa6-1699-4eca-ac67-5021cc419979.jpg','/goods-img/040a3aa6-1699-4eca-ac67-5021cc419979.jpg','

商品介绍加载中...

',1999,1849,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10880,'小米8 游戏手机 全面屏 金色','全网通(6G+64G)',51,'/goods-img/47c28778-88a4-42fd-bb4d-c93fe8df36b5.jpg','/goods-img/47c28778-88a4-42fd-bb4d-c93fe8df36b5.jpg','

商品介绍加载中...

',1799,1598,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10881,'小米8 游戏手机 全面屏 屏幕指纹版','暮光金 全网通(8G+128G)',51,'http://localhost:28089/admin/dist/img/no-img.png','http://localhost:28089/admin/dist/img/no-img.png','

商品介绍加载中...

',3799,2199,1000,'1',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10882,'小米8 游戏手机 全面屏 蓝色','全网通(6G+64G)',51,'/admin/dist/img/no-img.png','/admin/dist/img/no-img.png','

商品介绍加载中...

',1799,1598,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10883,'小米8 游戏手机 全面屏 金色','全网通(6G+256G)',51,'/admin/dist/img/no-img.png','/admin/dist/img/no-img.png','

商品介绍加载中...

',3199,2158,1000,'新品',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10884,'小米8 游戏手机 全面屏 白色','全网通(6G+256G)',51,'/admin/dist/img/no-img.png','/admin/dist/img/no-img.png','

商品介绍加载中...

',3199,2158,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10885,'小米8 游戏手机 全面屏 蓝色','全网通(6G+256G)',51,'/admin/dist/img/no-img.png','/admin/dist/img/no-img.png','

商品介绍加载中...

',3199,2158,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10886,'小米8 游戏手机 全面屏 黑色','全网通(6G+256G)',51,'/admin/dist/img/no-img.png','/admin/dist/img/no-img.png','

商品介绍加载中...

',3199,3199,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10887,'小米8 游戏手机 全面屏 透明探索版','全网通(8G+128G)',51,'/admin/dist/img/no-img.png','/admin/dist/img/no-img.png','

商品介绍加载中...

',4299,4299,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10888,'小米8 游戏手机 全面屏 屏幕指纹版','暮光金 全网通(6G+128G)',51,'/admin/dist/img/no-img.png','/admin/dist/img/no-img.png','

商品介绍加载中...

',3399,3399,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10889,'小米8 游戏手机 全面屏 蓝色','全网通(6G+128G)',51,'/admin/dist/img/no-img.png','/admin/dist/img/no-img.png','

商品介绍加载中...

',1849,1849,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10890,'小米 红米7 手机 Redmi7','AI双摄 拍照游戏手机 全网通双卡双待 亮黑色 4G+64G 全网通',51,'/goods-img/b6084354-1841-4241-ba7b-7e97186a9076.jpg','/goods-img/b6084354-1841-4241-ba7b-7e97186a9076.jpg','

商品介绍加载中...

',1299,808,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10891,'小米 红米7 手机 Redmi7','AI双摄 拍照游戏手机 全网通双卡双待 魅夜红 4G+64G 全网通',51,'/goods-img/7b4e03b1-eca7-42f5-8dda-14d02d3ab318.jpg','/goods-img/7b4e03b1-eca7-42f5-8dda-14d02d3ab318.jpg','

商品介绍加载中...

',1009,818,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10892,'小米 红米7 手机 Redmi7','AI双摄 拍照游戏手机 全网通双卡双待 梦幻蓝 3G+32G 全网通',51,'/goods-img/7bca8b59-35f3-480a-a95d-99efcbb8cfda.jpg','/goods-img/7bca8b59-35f3-480a-a95d-99efcbb8cfda.jpg','

商品介绍加载中...

',787,715,1000,'',0,0,'2019-09-18 13:38:32',0,'2020-10-13 10:41:59'), (10893,'HUAWEI Mate 30 Pro 双4000万徕卡电影四摄','超曲面OLED环幕屏 8GB+256GB 全网通4G版(星河银)',46,'/goods-img/mate30p2.png','/goods-img/mate30p2.png','
\r\n
\r\n
\r\n
\r\n
\r\n
\r\n
\r\n \r\n
\r\n
',5399,5399,1000,'重构想象',0,0,'2019-09-19 23:17:39',0,'2020-10-13 10:41:59'), (10894,'HUAWEI Mate 30 Pro','超曲面OLED环幕屏 8GB+128GB 全网通4G版(翡冷翠)',46,'/goods-img/mate30p3.png','/goods-img/mate30p3.png','
\r\n
\r\n
\r\n
\r\n
\r\n
\r\n
\r\n \r\n
\r\n
',5399,5399,995,'重构想象',0,0,'2019-09-19 23:20:24',0,'2020-10-13 10:41:59'), (10895,'HUAWEI Mate 30 4000万超感光徕卡影像','OLED全面屏 8GB+128GB 全网通4G版 (罗兰紫)',46,'/goods-img/mate30-3.png','/goods-img/mate30-3.png','
\n
\n
\n
\n
\n
\n
\n \n
\n
',3999,3999,964,'重构想象',0,0,'2019-09-19 23:22:22',0,'2020-10-13 10:41:59'), (10903,'华为 HUAWEI P40 冰霜银 全网通5G手机','麒麟990 5G SoC芯片 5000万超感知徕卡三摄 30倍数字变焦 6GB+128GB',46,'https://newbee-mall.oss-cn-beijing.aliyuncs.com/images/p40-silver.png','https://newbee-mall.oss-cn-beijing.aliyuncs.com/images/p40-silver.png','\"\"',4399,4399,1997,'超感知影像',0,0,'2020-03-27 10:07:37',0,'2020-10-13 10:41:59'), (10905,'Apple iPhone12 (A2404) 蓝色 支持移动联通电信5G 双卡双待手机','A14仿生芯片,6.1英寸超视网膜XDR显示屏,超瓷晶面板,升维大提速,现实力登场!',47,'https://newbee-mall.oss-cn-beijing.aliyuncs.com/images/iPhone12-blue.png','https://newbee-mall.oss-cn-beijing.aliyuncs.com/images/iPhone12-blue.png','\"\"',6299,6299,1000,'升维,大提速。',0,0,'2020-10-14 10:30:06',0,'2020-10-14 10:30:06'), (10906,'Apple iPhone12 Pro (A2408) 128GB 海蓝色 支持移动联通电信5G 双卡双待手机','A14仿生芯片,6.1英寸超视网膜XDR显示屏,激光雷达扫描仪,超瓷晶面板,现实力登场!',47,'https://newbee-mall.oss-cn-beijing.aliyuncs.com/images/iphone-12-pro-blue-hero.png','https://newbee-mall.oss-cn-beijing.aliyuncs.com/images/iphone-12-pro-blue-hero.png','\"\"',8499,8499,2000,'自我再飞跃',0,0,'2020-10-14 10:32:55',0,'2020-10-14 10:32:55'); -- ---------------------------- -- author 13 -- qq交流群 796794009 -- email 2449207463@qq.com -- link https://github.com/newbee-ltd -- Table structure for tb_newbee_mall_index_config -- ---------------------------- DROP TABLE IF EXISTS `tb_newbee_mall_index_config`; CREATE TABLE `tb_newbee_mall_index_config` ( `config_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '首页配置项主键id', `config_name` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '显示字符(配置搜索时不可为空,其他可为空)', `config_type` tinyint(4) NOT NULL DEFAULT 0 COMMENT '1-搜索框热搜 2-搜索下拉框热搜 3-(首页)热销商品 4-(首页)新品上线 5-(首页)为你推荐', `goods_id` bigint(20) NOT NULL DEFAULT 0 COMMENT '商品id 默认为0', `redirect_url` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '##' COMMENT '点击后的跳转地址(默认不跳转)', `config_rank` int(11) NOT NULL DEFAULT 0 COMMENT '排序值(字段越大越靠前)', `is_deleted` tinyint(4) NOT NULL DEFAULT 0 COMMENT '删除标识字段(0-未删除 1-已删除)', `create_time` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `create_user` int(11) NOT NULL DEFAULT 0 COMMENT '创建者id', `update_time` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '最新修改时间', `update_user` int(11) NULL DEFAULT 0 COMMENT '修改者id', PRIMARY KEY (`config_id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 25 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; -- ---------------------------- -- author 13 -- qq交流群 796794009 -- email 2449207463@qq.com -- link https://github.com/newbee-ltd -- Records of tb_newbee_mall_index_config -- ---------------------------- INSERT INTO `tb_newbee_mall_index_config` VALUES (1, '热销商品 iPhone XR', 3, 10284, '##', 10, 0, '2019-09-18 17:04:56', 0, '2019-09-18 17:04:56', 0); INSERT INTO `tb_newbee_mall_index_config` VALUES (2, '热销商品 华为 Mate20', 3, 10779, '##', 100, 0, '2019-09-18 17:05:27', 0, '2019-09-18 17:05:27', 0); INSERT INTO `tb_newbee_mall_index_config` VALUES (3, '热销商品 荣耀8X', 3, 10700, '##', 300, 0, '2019-09-18 17:08:02', 0, '2019-09-18 17:08:02', 0); INSERT INTO `tb_newbee_mall_index_config` VALUES (4, '热销商品 Apple AirPods', 3, 10159, '##', 101, 0, '2019-09-18 17:08:56', 0, '2019-09-18 17:08:56', 0); INSERT INTO `tb_newbee_mall_index_config` VALUES (5, '新品上线 Macbook Pro', 4, 10269, '##', 100, 0, '2019-09-18 17:10:36', 0, '2019-09-18 17:10:36', 0); INSERT INTO `tb_newbee_mall_index_config` VALUES (6, '新品上线 荣耀 9X Pro', 4, 10755, '##', 100, 0, '2019-09-18 17:11:05', 0, '2019-09-18 17:11:05', 0); INSERT INTO `tb_newbee_mall_index_config` VALUES (7, '新品上线 iPhone 11', 4, 10283, '##', 102, 0, '2019-09-18 17:11:44', 0, '2019-09-18 17:11:44', 0); INSERT INTO `tb_newbee_mall_index_config` VALUES (8, '新品上线 iPhone 11 Pro', 4, 10320, '##', 101, 0, '2019-09-18 17:11:58', 0, '2019-09-18 17:11:58', 0); INSERT INTO `tb_newbee_mall_index_config` VALUES (9, '新品上线 华为无线耳机', 4, 10186, '##', 100, 0, '2019-09-18 17:12:29', 0, '2019-09-18 17:12:29', 0); INSERT INTO `tb_newbee_mall_index_config` VALUES (10, '纪梵希高定香榭天鹅绒唇膏', 5, 10233, '##', 98, 0, '2019-09-18 17:47:23', 0, '2019-09-18 17:47:23', 0); INSERT INTO `tb_newbee_mall_index_config` VALUES (11, 'MAC 磨砂系列', 5, 10237, '##', 100, 0, '2019-09-18 17:47:44', 0, '2019-09-18 17:47:44', 0); INSERT INTO `tb_newbee_mall_index_config` VALUES (12, '索尼 WH-1000XM3', 5, 10195, '##', 102, 0, '2019-09-18 17:48:00', 0, '2019-09-18 17:48:00', 0); INSERT INTO `tb_newbee_mall_index_config` VALUES (13, 'Apple AirPods', 5, 10180, '##', 101, 0, '2019-09-18 17:49:11', 0, '2019-09-18 17:49:11', 0); INSERT INTO `tb_newbee_mall_index_config` VALUES (14, '小米 Redmi AirDots', 5, 10160, '##', 100, 0, '2019-09-18 17:49:28', 0, '2019-09-18 17:49:28', 0); INSERT INTO `tb_newbee_mall_index_config` VALUES (15, '2019 MacBookAir 13', 5, 10254, '##', 100, 0, '2019-09-18 17:50:18', 0, '2019-09-18 17:50:18', 0); INSERT INTO `tb_newbee_mall_index_config` VALUES (16, '女式粗棉线条纹长袖T恤', 5, 10158, '##', 99, 0, '2019-09-18 17:52:03', 0, '2019-09-18 17:52:03', 0); INSERT INTO `tb_newbee_mall_index_config` VALUES (17, '塑料浴室座椅', 5, 10154, '##', 100, 0, '2019-09-18 17:52:19', 0, '2019-09-18 17:52:19', 0); INSERT INTO `tb_newbee_mall_index_config` VALUES (18, '靠垫', 5, 10147, '##', 101, 0, '2019-09-18 17:52:50', 0, '2019-09-18 17:52:50', 0); INSERT INTO `tb_newbee_mall_index_config` VALUES (19, '小型超声波香薰机', 5, 10113, '##', 100, 0, '2019-09-18 17:54:07', 0, '2019-09-18 17:54:07', 0); INSERT INTO `tb_newbee_mall_index_config` VALUES (20, '11', 5, 1, '##', 0, 1, '2019-09-19 08:31:11', 0, '2019-09-19 08:31:20', 0); INSERT INTO `tb_newbee_mall_index_config` VALUES (21, '热销商品 华为 P30', 3, 10742, '##', 200, 0, '2019-09-19 23:23:38', 0, '2019-09-19 23:23:38', 0); INSERT INTO `tb_newbee_mall_index_config` VALUES (22, '新品上线 华为Mate30 Pro', 4, 10893, '##', 200, 0, '2019-09-19 23:26:05', 0, '2019-09-19 23:26:05', 0); INSERT INTO `tb_newbee_mall_index_config` VALUES (23, '新品上线 华为 Mate 30', 4, 10895, '##', 199, 0, '2019-09-19 23:26:32', 0, '2019-09-19 23:26:32', 0); INSERT INTO `tb_newbee_mall_index_config` VALUES (24, '华为 Mate 30 Pro', 5, 10894, '##', 101, 0, '2019-09-19 23:27:00', 0, '2019-09-19 23:27:00', 0); -- ---------------------------- -- author 13 -- qq交流群 796794009 -- email 2449207463@qq.com -- link https://github.com/newbee-ltd -- Table structure for tb_newbee_mall_order -- ---------------------------- DROP TABLE IF EXISTS `tb_newbee_mall_order`; CREATE TABLE `tb_newbee_mall_order` ( `order_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '订单表主键id', `order_no` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '订单号', `user_id` bigint(20) NOT NULL DEFAULT 0 COMMENT '用户主键id', `total_price` int(11) NOT NULL DEFAULT 1 COMMENT '订单总价', `pay_status` tinyint(4) NOT NULL DEFAULT 0 COMMENT '支付状态:0.未支付,1.支付成功,-1:支付失败', `pay_type` tinyint(4) NOT NULL DEFAULT 0 COMMENT '0.无 1.支付宝支付 2.微信支付', `pay_time` datetime(0) NULL DEFAULT NULL COMMENT '支付时间', `order_status` tinyint(4) NOT NULL DEFAULT 0 COMMENT '订单状态:0.待支付 1.已支付 2.配货完成 3:出库成功 4.交易成功 -1.手动关闭 -2.超时关闭 -3.商家关闭', `extra_info` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '订单body', `user_name` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '收货人姓名', `user_phone` varchar(11) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '收货人手机号', `user_address` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '收货人收货地址', `is_deleted` tinyint(4) NOT NULL DEFAULT 0 COMMENT '删除标识字段(0-未删除 1-已删除)', `create_time` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `update_time` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '最新修改时间', PRIMARY KEY (`order_id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 21 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; -- ---------------------------- -- author 13 -- qq交流群 796794009 -- email 2449207463@qq.com -- link https://github.com/newbee-ltd -- Records of tb_newbee_mall_order -- ---------------------------- INSERT INTO `tb_newbee_mall_order` VALUES (1, '15688187285093508', 1, 2492, 1, 2, '2019-09-18 23:00:18', -1, '', '', '', 'xafsdufhpwe', 0, '2019-09-18 22:53:07', '2019-09-18 22:55:32'); INSERT INTO `tb_newbee_mall_order` VALUES (2, '15688188616936181', 1, 135, 1, 1, '2019-09-18 23:01:06', 1, '', '', '', 'xafsdufhpwe', 0, '2019-09-18 22:55:20', '2019-09-18 23:01:06'); INSERT INTO `tb_newbee_mall_order` VALUES (3, '15689089426956979', 1, 15487, 1, 1, '2019-09-20 00:16:03', 3, '', '', '', '杭州市西湖区xx小区x幢419 十三 137xxxx2703', 0, '2019-09-19 23:56:40', '2019-09-20 00:10:39'); INSERT INTO `tb_newbee_mall_order` VALUES (4, '15689090398492576', 1, 8499, 0, 0, NULL, 0, '', '', '', '杭州市西湖区xx小区x幢419 十三 137xxxx2703', 0, '2019-09-19 23:58:18', '2019-09-19 23:58:18'); INSERT INTO `tb_newbee_mall_order` VALUES (5, '15689096266448452', 1, 115, 1, 2, '2019-09-20 00:13:52', 1, '', '', '', '杭州市西湖区xx小区x幢419 十三 137xxxx2703', 0, '2019-09-20 00:08:04', '2019-09-20 00:13:52'); INSERT INTO `tb_newbee_mall_order` VALUES (6, '15691645776131562', 7, 7998, 1, 1, '2019-09-22 23:05:53', 1, '', '', '', '杭州市西湖区xx小区x幢419 十三 137xxxx2703', 0, '2019-09-22 22:57:15', '2019-09-22 23:05:53'); INSERT INTO `tb_newbee_mall_order` VALUES (7, '15691648465397435', 7, 13998, 1, 2, '2019-09-22 23:07:38', -1, '', '', '', '杭州市西湖区xx小区x幢419 十三 137xxxx2703', 0, '2019-09-22 23:01:44', '2019-09-22 23:02:10'); INSERT INTO `tb_newbee_mall_order` VALUES (8, '15691649071896878', 7, 1246, 1, 1, '2019-09-22 23:08:31', 1, '', '', '', '杭州市西湖区xx小区x幢419 十三 137xxxx2703', 0, '2019-09-22 23:02:45', '2019-09-22 23:08:31'); INSERT INTO `tb_newbee_mall_order` VALUES (9, '15691649748362177', 7, 25656, 1, 1, '2019-09-22 23:09:39', 4, '', '', '', '杭州市西湖区xx小区x幢419 十三 137xxxx2703', 0, '2019-09-22 23:03:52', '2019-09-22 23:50:45'); INSERT INTO `tb_newbee_mall_order` VALUES (10, '15691652286194502', 7, 16197, 0, 0, NULL, 0, '', '', '', '杭州市西湖区xx小区x幢419 十三 137xxxx2703', 0, '2019-09-22 23:08:06', '2019-09-22 23:08:06'); INSERT INTO `tb_newbee_mall_order` VALUES (11, '15692210075967186', 6, 5999, 1, 2, '2019-09-23 17:03:05', 1, '', '', '', '杭州市西湖区xx小区x幢419 十三 137xxxx2703', 0, '2019-09-23 14:43:27', '2019-09-23 17:03:05'); INSERT INTO `tb_newbee_mall_order` VALUES (12, '15692218454123239', 6, 7245, 0, 0, NULL, 0, '', '', '', '杭州市西湖区xx小区x幢419 十三 137xxxx2703', 0, '2019-09-23 14:57:25', '2019-09-23 14:57:25'); INSERT INTO `tb_newbee_mall_order` VALUES (13, '15692225252983527', 6, 5488, 1, 2, '2019-09-23 15:38:54', 1, '', '', '', '杭州市西湖区xx小区x幢419 十三 137xxxx2703', 0, '2019-09-23 15:08:45', '2019-09-23 15:38:54'); INSERT INTO `tb_newbee_mall_order` VALUES (14, '15692291639125640', 6, 9046, 1, 2, '2019-09-23 16:59:32', -1, '', '', '', '杭州市西湖区xx小区x幢419 十三 137xxxx2709', 0, '2019-09-23 16:59:23', '2019-09-23 16:59:40'); INSERT INTO `tb_newbee_mall_order` VALUES (15, '15692295348262843', 6, 65, 1, 2, '2019-09-23 17:06:17', 1, '', '', '', '杭州市西湖区xx小区x幢419 十三 137xxxx2709', 0, '2019-09-23 17:05:34', '2019-09-23 17:06:17'); INSERT INTO `tb_newbee_mall_order` VALUES (16, '15692298037679052', 6, 15233, 1, 2, '2019-09-23 17:10:08', 1, '', '', '', '上海浦东新区XX路XX号 999 137xxxx7797', 0, '2019-09-23 17:10:03', '2019-09-23 17:10:08'); INSERT INTO `tb_newbee_mall_order` VALUES (17, '15694781962831307', 7, 1246, 1, 2, '2019-09-26 14:10:12', -1, '', '', '', '杭州市西湖区xx小区x幢419 十三 137xxxx2703', 0, '2019-09-26 14:09:56', '2019-09-26 14:10:22'); INSERT INTO `tb_newbee_mall_order` VALUES (18, '15698039249771093', 7, 3199, 0, 0, NULL, 0, '', '', '', '杭州市西湖区xx小区x幢419 十三 137xxxx2703', 0, '2019-09-30 08:38:26', '2019-09-30 08:38:26'); INSERT INTO `tb_newbee_mall_order` VALUES (19, '15702783557537865', 7, 6819, 0, 0, NULL, 0, '', '', '', '杭州市西湖区xx小区x幢419 十三 137xxxx2703', 0, '2019-10-05 20:20:10', '2019-10-05 20:20:10'); INSERT INTO `tb_newbee_mall_order` VALUES (20, '15702847670935185', 6, 3999, 1, 2, '2019-10-05 22:13:03', 1, '', '', '', '上海浦东新区XX路XX号 999 137xxxx7797', 0, '2019-10-05 22:12:47', '2019-10-05 22:13:03'); -- ---------------------------- -- author 13 -- qq交流群 796794009 -- email 2449207463@qq.com -- link https://github.com/newbee-ltd -- Table structure for tb_newbee_mall_order_item -- ---------------------------- DROP TABLE IF EXISTS `tb_newbee_mall_order_item`; CREATE TABLE `tb_newbee_mall_order_item` ( `order_item_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '订单关联购物项主键id', `order_id` bigint(20) NOT NULL DEFAULT 0 COMMENT '订单主键id', `goods_id` bigint(20) NOT NULL DEFAULT 0 COMMENT '关联商品id', `goods_name` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '下单时商品的名称(订单快照)', `goods_cover_img` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '下单时商品的主图(订单快照)', `selling_price` int(11) NOT NULL DEFAULT 1 COMMENT '下单时商品的价格(订单快照)', `goods_count` int(11) NOT NULL DEFAULT 1 COMMENT '数量(订单快照)', `create_time` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', PRIMARY KEY (`order_item_id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 35 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; -- ---------------------------- -- author 13 -- qq交流群 796794009 -- email 2449207463@qq.com -- link https://github.com/newbee-ltd -- Records of tb_newbee_mall_order_item -- ---------------------------- INSERT INTO `tb_newbee_mall_order_item` VALUES (1, 1, 10180, 'Apple AirPods 配充电盒', '/goods-img/64768a8d-0664-4b29-88c9-2626578ffbd1.jpg', 1246, 2, '2019-09-18 22:53:07'); INSERT INTO `tb_newbee_mall_order_item` VALUES (2, 2, 10147, 'MUJI 羽毛 靠垫', '/goods-img/0f701215-b782-40c7-8bbd-97b51be56461.jpg', 65, 1, '2019-09-18 22:55:20'); INSERT INTO `tb_newbee_mall_order_item` VALUES (3, 2, 10158, '无印良品 女式粗棉线条纹长袖T恤', 'http://localhost:28089/goods-img/5488564b-8335-4b0c-a5a4-52f3f03ee728.jpg', 70, 1, '2019-09-18 22:55:20'); INSERT INTO `tb_newbee_mall_order_item` VALUES (4, 3, 10742, '华为 HUAWEI P30 Pro', '/goods-img/dda1d575-cdac-4eb4-a118-3834490166f7.jpg', 5488, 1, '2019-09-19 23:56:40'); INSERT INTO `tb_newbee_mall_order_item` VALUES (5, 3, 10320, 'Apple iPhone 11 Pro', '/goods-img/0025ad55-e260-4a00-be79-fa5b8c5ac0de.jpg', 9999, 1, '2019-09-19 23:56:40'); INSERT INTO `tb_newbee_mall_order_item` VALUES (6, 4, 10254, 'Apple 2019款 MacBook Air 13.3', '/goods-img/7810bc9d-236f-4386-a0ef-45a831b49bf2.jpg', 8499, 1, '2019-09-19 23:58:18'); INSERT INTO `tb_newbee_mall_order_item` VALUES (7, 5, 10104, '无印良品 MUJI 修正带', '/goods-img/98ce17e1-890e-4eaf-856a-7fce8ffebc4c.jpg', 15, 1, '2019-09-20 00:08:04'); INSERT INTO `tb_newbee_mall_order_item` VALUES (8, 5, 10110, '无印良品 MUJI 基础润肤乳霜', '/goods-img/30036561-a150-4ea7-9106-29bbea278909.jpg', 100, 1, '2019-09-20 00:08:04'); INSERT INTO `tb_newbee_mall_order_item` VALUES (9, 6, 10895, 'HUAWEI Mate 30 4000万超感光徕卡影像', '/goods-img/mate30-3.png', 3999, 2, '2019-09-22 22:57:15'); INSERT INTO `tb_newbee_mall_order_item` VALUES (10, 7, 10895, 'HUAWEI Mate 30 4000万超感光徕卡影像', '/goods-img/mate30-3.png', 3999, 1, '2019-09-22 23:01:44'); INSERT INTO `tb_newbee_mall_order_item` VALUES (11, 7, 10320, 'Apple iPhone 11 Pro', '/goods-img/0025ad55-e260-4a00-be79-fa5b8c5ac0de.jpg', 9999, 1, '2019-09-22 23:01:44'); INSERT INTO `tb_newbee_mall_order_item` VALUES (12, 8, 10180, 'Apple AirPods 配充电盒', '/goods-img/64768a8d-0664-4b29-88c9-2626578ffbd1.jpg', 1246, 1, '2019-09-22 23:02:45'); INSERT INTO `tb_newbee_mall_order_item` VALUES (13, 9, 10237, 'MAC 雾面丝绒哑光子弹头口红', 'http://localhost:28089/goods-img/1930d79b-88bd-4c5c-8510-0697c9ad2578.jpg', 165, 4, '2019-09-22 23:03:52'); INSERT INTO `tb_newbee_mall_order_item` VALUES (14, 9, 10254, 'Apple 2019款 MacBook Air 13.3', '/goods-img/7810bc9d-236f-4386-a0ef-45a831b49bf2.jpg', 8499, 2, '2019-09-22 23:03:52'); INSERT INTO `tb_newbee_mall_order_item` VALUES (15, 9, 10195, '索尼 WH-1000XM3 头戴式耳机', 'http://localhost:28089/goods-img/0dc503b2-90a2-4971-9723-c085a1844b76.jpg', 2599, 1, '2019-09-22 23:03:52'); INSERT INTO `tb_newbee_mall_order_item` VALUES (16, 9, 10894, 'HUAWEI Mate 30 Pro', '/goods-img/mate30p3.png', 5399, 1, '2019-09-22 23:03:52'); INSERT INTO `tb_newbee_mall_order_item` VALUES (17, 10, 10894, 'HUAWEI Mate 30 Pro', '/goods-img/mate30p3.png', 5399, 3, '2019-09-22 23:08:06'); INSERT INTO `tb_newbee_mall_order_item` VALUES (18, 11, 10279, 'Apple iPhone 11 (A2223)', '/goods-img/a0d09f94-9c46-4ee1-aaef-dfd132e7543e.jpg', 5999, 1, '2019-09-23 14:43:27'); INSERT INTO `tb_newbee_mall_order_item` VALUES (19, 12, 10279, 'Apple iPhone 11 (A2223)', '/goods-img/a0d09f94-9c46-4ee1-aaef-dfd132e7543e.jpg', 5999, 1, '2019-09-23 14:57:26'); INSERT INTO `tb_newbee_mall_order_item` VALUES (20, 12, 10159, 'Apple AirPods 配充电盒', '/goods-img/53c9f268-7cd4-4fac-909c-2dc066625655.jpg', 1246, 1, '2019-09-23 14:57:26'); INSERT INTO `tb_newbee_mall_order_item` VALUES (21, 13, 10742, '华为 HUAWEI P30 Pro', '/goods-img/dda1d575-cdac-4eb4-a118-3834490166f7.jpg', 5488, 1, '2019-09-23 15:08:46'); INSERT INTO `tb_newbee_mall_order_item` VALUES (22, 14, 10158, '无印良品 女式粗棉线条纹长袖T恤', '/goods-img/5488564b-8335-4b0c-a5a4-52f3f03ee728.jpg', 70, 1, '2019-09-23 16:59:24'); INSERT INTO `tb_newbee_mall_order_item` VALUES (23, 14, 10704, '华为 HUAWEI P30 超感光徕卡三摄麒麟980AI...', '/goods-img/b9e6d770-06dd-40f4-9ae5-31103cec6e5f.jpg', 3988, 1, '2019-09-23 16:59:24'); INSERT INTO `tb_newbee_mall_order_item` VALUES (24, 14, 10739, '华为 HUAWEI P30 Pro', '/goods-img/65c8e729-aeca-4780-977b-4d0d39d4aa2e.jpg', 4988, 1, '2019-09-23 16:59:24'); INSERT INTO `tb_newbee_mall_order_item` VALUES (25, 15, 10147, 'MUJI 羽毛 靠垫', '/goods-img/0f701215-b782-40c7-8bbd-97b51be56461.jpg', 65, 1, '2019-09-23 17:05:34'); INSERT INTO `tb_newbee_mall_order_item` VALUES (26, 16, 10742, '华为 HUAWEI P30 Pro', '/goods-img/dda1d575-cdac-4eb4-a118-3834490166f7.jpg', 5488, 1, '2019-09-23 17:10:03'); INSERT INTO `tb_newbee_mall_order_item` VALUES (27, 16, 10159, 'Apple AirPods 配充电盒', '/goods-img/53c9f268-7cd4-4fac-909c-2dc066625655.jpg', 1246, 1, '2019-09-23 17:10:03'); INSERT INTO `tb_newbee_mall_order_item` VALUES (28, 16, 10254, 'Apple 2019款 MacBook Air 13.3', '/goods-img/7810bc9d-236f-4386-a0ef-45a831b49bf2.jpg', 8499, 1, '2019-09-23 17:10:03'); INSERT INTO `tb_newbee_mall_order_item` VALUES (29, 17, 10180, 'Apple AirPods 配充电盒', '/goods-img/64768a8d-0664-4b29-88c9-2626578ffbd1.jpg', 1246, 1, '2019-09-26 14:09:56'); INSERT INTO `tb_newbee_mall_order_item` VALUES (30, 18, 10779, '华为 HUAWEI Mate 20', '/goods-img/08f9a912-f049-4cf8-a839-115fc6582398.jpg', 3199, 1, '2019-09-30 08:38:26'); INSERT INTO `tb_newbee_mall_order_item` VALUES (31, 19, 10742, '华为 HUAWEI P30 Pro', '/goods-img/dda1d575-cdac-4eb4-a118-3834490166f7.jpg', 5488, 1, '2019-10-05 20:20:10'); INSERT INTO `tb_newbee_mall_order_item` VALUES (32, 19, 10154, '无印良品 MUJI 塑料浴室座椅', '/goods-img/15395057-94e9-4545-a8ee-8aee025f40c5.jpg', 85, 1, '2019-10-05 20:20:10'); INSERT INTO `tb_newbee_mall_order_item` VALUES (33, 19, 10159, 'Apple AirPods 配充电盒', '/goods-img/53c9f268-7cd4-4fac-909c-2dc066625655.jpg', 1246, 1, '2019-10-05 20:20:10'); INSERT INTO `tb_newbee_mall_order_item` VALUES (34, 20, 10895, 'HUAWEI Mate 30 4000万超感光徕卡影像', '/goods-img/mate30-3.png', 3999, 1, '2019-10-05 22:12:47'); -- ---------------------------- -- author 13 -- qq交流群 796794009 -- email 2449207463@qq.com -- link https://github.com/newbee-ltd -- Table structure for tb_newbee_mall_shopping_cart_item -- ---------------------------- DROP TABLE IF EXISTS `tb_newbee_mall_shopping_cart_item`; CREATE TABLE `tb_newbee_mall_shopping_cart_item` ( `cart_item_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '购物项主键id', `user_id` bigint(20) NOT NULL COMMENT '用户主键id', `goods_id` bigint(20) NOT NULL DEFAULT 0 COMMENT '关联商品id', `goods_count` int(11) NOT NULL DEFAULT 1 COMMENT '数量(最大为5)', `is_deleted` tinyint(4) NOT NULL DEFAULT 0 COMMENT '删除标识字段(0-未删除 1-已删除)', `create_time` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `update_time` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '最新修改时间', PRIMARY KEY (`cart_item_id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 69 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; -- ---------------------------- -- author 13 -- qq交流群 796794009 -- email 2449207463@qq.com -- link https://github.com/newbee-ltd -- Table structure for tb_newbee_mall_user -- ---------------------------- DROP TABLE IF EXISTS `tb_newbee_mall_user`; CREATE TABLE `tb_newbee_mall_user` ( `user_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '用户主键id', `nick_name` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '用户昵称', `login_name` varchar(11) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '登陆名称(默认为手机号)', `password_md5` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT 'MD5加密后的密码', `introduce_sign` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '个性签名', `address` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '收货地址', `is_deleted` tinyint(4) NOT NULL DEFAULT 0 COMMENT '注销标识字段(0-正常 1-已注销)', `locked_flag` tinyint(4) NOT NULL DEFAULT 0 COMMENT '锁定标识字段(0-未锁定 1-已锁定)', `create_time` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '注册时间', PRIMARY KEY (`user_id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 9 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; -- ---------------------------- -- author 13 -- qq交流群 796794009 -- email 2449207463@qq.com -- link https://github.com/newbee-ltd -- Records of tb_newbee_mall_user -- ---------------------------- INSERT INTO `tb_newbee_mall_user` VALUES (1, '十三', '13700002703', 'e10adc3949ba59abbe56e057f20f883e', '我不怕千万人阻挡,只怕自己投降', '杭州市西湖区xx小区x幢419 十三 137xxxx2703', 0, 0, '2019-09-22 08:44:57'); INSERT INTO `tb_newbee_mall_user` VALUES (6, '测试用户1', '13711113333', 'dda01dc6d334badcd031102be6bee182', '测试用户1', '上海浦东新区XX路XX号 999 137xxxx7797', 0, 0, '2019-08-29 10:51:39'); INSERT INTO `tb_newbee_mall_user` VALUES (7, '测试用户2测试用户2测试用户2测试用户2', '13811113333', 'dda01dc6d334badcd031102be6bee182', '测试用户2', '杭州市西湖区xx小区x幢419 十三 137xxxx2703', 0, 0, '2019-08-29 10:55:08'); INSERT INTO `tb_newbee_mall_user` VALUES (8, '测试用户3', '13911113333', 'dda01dc6d334badcd031102be6bee182', '测试用户3', '杭州市西湖区xx小区x幢419 十三 137xxxx2703', 0, 0, '2019-08-29 10:55:16'); ================================================ FILE: src/main/resources/static/admin/dist/css/adminlte.css ================================================ /*! * AdminLTE v3.0.0-alpha * Author: Abdullah Almsaeed * Website: AdminLTE.io * License: Open source - MIT */ /*! * Bootstrap v4.1.0 (https://getbootstrap.com/) * Copyright 2011-2018 The Bootstrap Authors * Copyright 2011-2018 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) */ :root { --blue: #007bff; --indigo: #6610f2; --purple: #6f42c1; --pink: #e83e8c; --red: #dc3545; --orange: #fd7e14; --yellow: #ffc107; --green: #28a745; --teal: #20c997; --cyan: #17a2b8; --white: #ffffff; --gray: #6c757d; --gray-dark: #343a40; --primary: #007bff; --secondary: #6c757d; --success: #28a745; --info: #17a2b8; --warning: #ffc107; --danger: #dc3545; --light: #f8f9fa; --dark: #343a40; --breakpoint-xs: 0; --breakpoint-sm: 576px; --breakpoint-md: 768px; --breakpoint-lg: 992px; --breakpoint-xl: 1200px; --font-family-sans-serif: "Source Sans Pro", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; --font-family-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; } *, *::before, *::after { box-sizing: border-box; } html { font-family: sans-serif; line-height: 1.15; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; -ms-overflow-style: scrollbar; -webkit-tap-highlight-color: transparent; } @-ms-viewport { width: device-width; } article, aside, dialog, figcaption, figure, footer, header, hgroup, main, nav, section { display: block; } body { margin: 0; font-family: "Source Sans Pro", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 1rem; font-weight: 400; line-height: 1.5; color: #212529; text-align: left; background-color: #ffffff; } [tabindex="-1"]:focus { outline: 0 !important; } hr { box-sizing: content-box; height: 0; overflow: visible; } h1, h2, h3, h4, h5, h6 { margin-top: 0; margin-bottom: 0.5rem; } p { margin-top: 0; margin-bottom: 1rem; } abbr[title], abbr[data-original-title] { text-decoration: underline; text-decoration: underline dotted; cursor: help; border-bottom: 0; } address { margin-bottom: 1rem; font-style: normal; line-height: inherit; } ol, ul, dl { margin-top: 0; margin-bottom: 1rem; } ol ol, ul ul, ol ul, ul ol { margin-bottom: 0; } dt { font-weight: 700; } dd { margin-bottom: .5rem; margin-left: 0; } blockquote { margin: 0 0 1rem; } dfn { font-style: italic; } b, strong { font-weight: bolder; } small { font-size: 80%; } sub, sup { position: relative; font-size: 75%; line-height: 0; vertical-align: baseline; } sub { bottom: -.25em; } sup { top: -.5em; } a { color: #007bff; text-decoration: none; background-color: transparent; -webkit-text-decoration-skip: objects; } a:hover { color: #0056b3; text-decoration: none; } a:not([href]):not([tabindex]) { color: inherit; text-decoration: none; } a:not([href]):not([tabindex]):hover, a:not([href]):not([tabindex]):focus { color: inherit; text-decoration: none; } a:not([href]):not([tabindex]):focus { outline: 0; } pre, code, kbd, samp { font-family: monospace, monospace; font-size: 1em; } pre { margin-top: 0; margin-bottom: 1rem; overflow: auto; -ms-overflow-style: scrollbar; } figure { margin: 0 0 1rem; } img { vertical-align: middle; border-style: none; } svg:not(:root) { overflow: hidden; } table { border-collapse: collapse; } caption { padding-top: 0.75rem; padding-bottom: 0.75rem; color: #6c757d; text-align: left; caption-side: bottom; } th { text-align: inherit; } label { display: inline-block; margin-bottom: 0.5rem; } button { border-radius: 0; } button:focus { outline: 1px dotted; outline: 5px auto -webkit-focus-ring-color; } input, button, select, optgroup, textarea { margin: 0; font-family: inherit; font-size: inherit; line-height: inherit; } button, input { overflow: visible; } button, select { text-transform: none; } button, html [type="button"], [type="reset"], [type="submit"] { -webkit-appearance: button; } button::-moz-focus-inner, [type="button"]::-moz-focus-inner, [type="reset"]::-moz-focus-inner, [type="submit"]::-moz-focus-inner { padding: 0; border-style: none; } input[type="radio"], input[type="checkbox"] { box-sizing: border-box; padding: 0; } input[type="date"], input[type="time"], input[type="datetime-local"], input[type="month"] { -webkit-appearance: listbox; } textarea { overflow: auto; resize: vertical; } fieldset { min-width: 0; padding: 0; margin: 0; border: 0; } legend { display: block; width: 100%; max-width: 100%; padding: 0; margin-bottom: .5rem; font-size: 1.5rem; line-height: inherit; color: inherit; white-space: normal; } progress { vertical-align: baseline; } [type="number"]::-webkit-inner-spin-button, [type="number"]::-webkit-outer-spin-button { height: auto; } [type="search"] { outline-offset: -2px; -webkit-appearance: none; } [type="search"]::-webkit-search-cancel-button, [type="search"]::-webkit-search-decoration { -webkit-appearance: none; } ::-webkit-file-upload-button { font: inherit; -webkit-appearance: button; } output { display: inline-block; } summary { display: list-item; cursor: pointer; } template { display: none; } [hidden] { display: none !important; } h1, h2, h3, h4, h5, h6, .h1, .h2, .h3, .h4, .h5, .h6 { margin-bottom: 0.5rem; font-family: inherit; font-weight: 500; line-height: 1.2; color: inherit; } h1, .h1 { font-size: 2.5rem; } h2, .h2 { font-size: 2rem; } h3, .h3 { font-size: 1.75rem; } h4, .h4 { font-size: 1.5rem; } h5, .h5 { font-size: 1.25rem; } h6, .h6 { font-size: 1rem; } .lead { font-size: 1.25rem; font-weight: 300; } .display-1 { font-size: 6rem; font-weight: 300; line-height: 1.2; } .display-2 { font-size: 5.5rem; font-weight: 300; line-height: 1.2; } .display-3 { font-size: 4.5rem; font-weight: 300; line-height: 1.2; } .display-4 { font-size: 3.5rem; font-weight: 300; line-height: 1.2; } hr { margin-top: 1rem; margin-bottom: 1rem; border: 0; border-top: 1px solid rgba(0, 0, 0, 0.1); } small, .small { font-size: 80%; font-weight: 400; } mark, .mark { padding: 0.2em; background-color: #fcf8e3; } .list-unstyled, .chart-legend, .contacts-list, .users-list, .mailbox-attachments { padding-left: 0; list-style: none; } .list-inline { padding-left: 0; list-style: none; } .list-inline-item { display: inline-block; } .list-inline-item:not(:last-child) { margin-right: 0.5rem; } .initialism { font-size: 90%; text-transform: uppercase; } .blockquote { margin-bottom: 1rem; font-size: 1.25rem; } .blockquote-footer { display: block; font-size: 80%; color: #6c757d; } .blockquote-footer::before { content: "\2014 \00A0"; } .img-fluid { max-width: 100%; height: auto; } .img-thumbnail { padding: 0.25rem; background-color: #ffffff; border: 1px solid #dee2e6; border-radius: 0.25rem; box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075); max-width: 100%; height: auto; } .figure { display: inline-block; } .figure-img { margin-bottom: 0.5rem; line-height: 1; } .figure-caption { font-size: 90%; color: #6c757d; } code, kbd, pre, samp { font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; } code { font-size: 87.5%; color: #e83e8c; word-break: break-word; } a > code { color: inherit; } kbd { padding: 0.2rem 0.4rem; font-size: 87.5%; color: #ffffff; background-color: #212529; border-radius: 0.2rem; box-shadow: inset 0 -0.1rem 0 rgba(0, 0, 0, 0.25); } kbd kbd { padding: 0; font-size: 100%; font-weight: 700; box-shadow: none; } pre { display: block; font-size: 87.5%; color: #212529; } pre code { font-size: inherit; color: inherit; word-break: normal; } .pre-scrollable { max-height: 340px; overflow-y: scroll; } .container { width: 100%; padding-right: 7.5px; padding-left: 7.5px; margin-right: auto; margin-left: auto; } @media (min-width: 576px) { .container { max-width: 540px; } } @media (min-width: 768px) { .container { max-width: 720px; } } @media (min-width: 992px) { .container { max-width: 960px; } } @media (min-width: 1200px) { .container { max-width: 1140px; } } .container-fluid { width: 100%; padding-right: 7.5px; padding-left: 7.5px; margin-right: auto; margin-left: auto; } .row { display: flex; flex-wrap: wrap; margin-right: -7.5px; margin-left: -7.5px; } .no-gutters { margin-right: 0; margin-left: 0; } .no-gutters > .col, .no-gutters > [class*="col-"] { padding-right: 0; padding-left: 0; } .col-1, .col-2, .col-3, .col-4, .col-5, .col-6, .col-7, .col-8, .col-9, .col-10, .col-11, .col-12, .col, .col-auto, .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, .col-sm, .col-sm-auto, .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, .col-md, .col-md-auto, .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, .col-lg, .col-lg-auto, .col-xl-1, .col-xl-2, .col-xl-3, .col-xl-4, .col-xl-5, .col-xl-6, .col-xl-7, .col-xl-8, .col-xl-9, .col-xl-10, .col-xl-11, .col-xl-12, .col-xl, .col-xl-auto { position: relative; width: 100%; min-height: 1px; padding-right: 7.5px; padding-left: 7.5px; } .col { flex-basis: 0; flex-grow: 1; max-width: 100%; } .col-auto { flex: 0 0 auto; width: auto; max-width: none; } .col-1 { flex: 0 0 8.333333%; max-width: 8.333333%; } .col-2 { flex: 0 0 16.666667%; max-width: 16.666667%; } .col-3 { flex: 0 0 25%; max-width: 25%; } .col-4 { flex: 0 0 33.333333%; max-width: 33.333333%; } .col-5 { flex: 0 0 41.666667%; max-width: 41.666667%; } .col-6 { flex: 0 0 50%; max-width: 50%; } .col-7 { flex: 0 0 58.333333%; max-width: 58.333333%; } .col-8 { flex: 0 0 66.666667%; max-width: 66.666667%; } .col-9 { flex: 0 0 75%; max-width: 75%; } .col-10 { flex: 0 0 83.333333%; max-width: 83.333333%; } .col-11 { flex: 0 0 91.666667%; max-width: 91.666667%; } .col-12 { flex: 0 0 100%; max-width: 100%; } .order-first { order: -1; } .order-last { order: 13; } .order-0 { order: 0; } .order-1 { order: 1; } .order-2 { order: 2; } .order-3 { order: 3; } .order-4 { order: 4; } .order-5 { order: 5; } .order-6 { order: 6; } .order-7 { order: 7; } .order-8 { order: 8; } .order-9 { order: 9; } .order-10 { order: 10; } .order-11 { order: 11; } .order-12 { order: 12; } .offset-1 { margin-left: 8.333333%; } .offset-2 { margin-left: 16.666667%; } .offset-3 { margin-left: 25%; } .offset-4 { margin-left: 33.333333%; } .offset-5 { margin-left: 41.666667%; } .offset-6 { margin-left: 50%; } .offset-7 { margin-left: 58.333333%; } .offset-8 { margin-left: 66.666667%; } .offset-9 { margin-left: 75%; } .offset-10 { margin-left: 83.333333%; } .offset-11 { margin-left: 91.666667%; } @media (min-width: 576px) { .col-sm { flex-basis: 0; flex-grow: 1; max-width: 100%; } .col-sm-auto { flex: 0 0 auto; width: auto; max-width: none; } .col-sm-1 { flex: 0 0 8.333333%; max-width: 8.333333%; } .col-sm-2 { flex: 0 0 16.666667%; max-width: 16.666667%; } .col-sm-3 { flex: 0 0 25%; max-width: 25%; } .col-sm-4 { flex: 0 0 33.333333%; max-width: 33.333333%; } .col-sm-5 { flex: 0 0 41.666667%; max-width: 41.666667%; } .col-sm-6 { flex: 0 0 50%; max-width: 50%; } .col-sm-7 { flex: 0 0 58.333333%; max-width: 58.333333%; } .col-sm-8 { flex: 0 0 66.666667%; max-width: 66.666667%; } .col-sm-9 { flex: 0 0 75%; max-width: 75%; } .col-sm-10 { flex: 0 0 83.333333%; max-width: 83.333333%; } .col-sm-11 { flex: 0 0 91.666667%; max-width: 91.666667%; } .col-sm-12 { flex: 0 0 100%; max-width: 100%; } .order-sm-first { order: -1; } .order-sm-last { order: 13; } .order-sm-0 { order: 0; } .order-sm-1 { order: 1; } .order-sm-2 { order: 2; } .order-sm-3 { order: 3; } .order-sm-4 { order: 4; } .order-sm-5 { order: 5; } .order-sm-6 { order: 6; } .order-sm-7 { order: 7; } .order-sm-8 { order: 8; } .order-sm-9 { order: 9; } .order-sm-10 { order: 10; } .order-sm-11 { order: 11; } .order-sm-12 { order: 12; } .offset-sm-0 { margin-left: 0; } .offset-sm-1 { margin-left: 8.333333%; } .offset-sm-2 { margin-left: 16.666667%; } .offset-sm-3 { margin-left: 25%; } .offset-sm-4 { margin-left: 33.333333%; } .offset-sm-5 { margin-left: 41.666667%; } .offset-sm-6 { margin-left: 50%; } .offset-sm-7 { margin-left: 58.333333%; } .offset-sm-8 { margin-left: 66.666667%; } .offset-sm-9 { margin-left: 75%; } .offset-sm-10 { margin-left: 83.333333%; } .offset-sm-11 { margin-left: 91.666667%; } } @media (min-width: 768px) { .col-md { flex-basis: 0; flex-grow: 1; max-width: 100%; } .col-md-auto { flex: 0 0 auto; width: auto; max-width: none; } .col-md-1 { flex: 0 0 8.333333%; max-width: 8.333333%; } .col-md-2 { flex: 0 0 16.666667%; max-width: 16.666667%; } .col-md-3 { flex: 0 0 25%; max-width: 25%; } .col-md-4 { flex: 0 0 33.333333%; max-width: 33.333333%; } .col-md-5 { flex: 0 0 41.666667%; max-width: 41.666667%; } .col-md-6 { flex: 0 0 50%; max-width: 50%; } .col-md-7 { flex: 0 0 58.333333%; max-width: 58.333333%; } .col-md-8 { flex: 0 0 66.666667%; max-width: 66.666667%; } .col-md-9 { flex: 0 0 75%; max-width: 75%; } .col-md-10 { flex: 0 0 83.333333%; max-width: 83.333333%; } .col-md-11 { flex: 0 0 91.666667%; max-width: 91.666667%; } .col-md-12 { flex: 0 0 100%; max-width: 100%; } .order-md-first { order: -1; } .order-md-last { order: 13; } .order-md-0 { order: 0; } .order-md-1 { order: 1; } .order-md-2 { order: 2; } .order-md-3 { order: 3; } .order-md-4 { order: 4; } .order-md-5 { order: 5; } .order-md-6 { order: 6; } .order-md-7 { order: 7; } .order-md-8 { order: 8; } .order-md-9 { order: 9; } .order-md-10 { order: 10; } .order-md-11 { order: 11; } .order-md-12 { order: 12; } .offset-md-0 { margin-left: 0; } .offset-md-1 { margin-left: 8.333333%; } .offset-md-2 { margin-left: 16.666667%; } .offset-md-3 { margin-left: 25%; } .offset-md-4 { margin-left: 33.333333%; } .offset-md-5 { margin-left: 41.666667%; } .offset-md-6 { margin-left: 50%; } .offset-md-7 { margin-left: 58.333333%; } .offset-md-8 { margin-left: 66.666667%; } .offset-md-9 { margin-left: 75%; } .offset-md-10 { margin-left: 83.333333%; } .offset-md-11 { margin-left: 91.666667%; } } @media (min-width: 992px) { .col-lg { flex-basis: 0; flex-grow: 1; max-width: 100%; } .col-lg-auto { flex: 0 0 auto; width: auto; max-width: none; } .col-lg-1 { flex: 0 0 8.333333%; max-width: 8.333333%; } .col-lg-2 { flex: 0 0 16.666667%; max-width: 16.666667%; } .col-lg-3 { flex: 0 0 25%; max-width: 25%; } .col-lg-4 { flex: 0 0 33.333333%; max-width: 33.333333%; } .col-lg-5 { flex: 0 0 41.666667%; max-width: 41.666667%; } .col-lg-6 { flex: 0 0 50%; max-width: 50%; } .col-lg-7 { flex: 0 0 58.333333%; max-width: 58.333333%; } .col-lg-8 { flex: 0 0 66.666667%; max-width: 66.666667%; } .col-lg-9 { flex: 0 0 75%; max-width: 75%; } .col-lg-10 { flex: 0 0 83.333333%; max-width: 83.333333%; } .col-lg-11 { flex: 0 0 91.666667%; max-width: 91.666667%; } .col-lg-12 { flex: 0 0 100%; max-width: 100%; } .order-lg-first { order: -1; } .order-lg-last { order: 13; } .order-lg-0 { order: 0; } .order-lg-1 { order: 1; } .order-lg-2 { order: 2; } .order-lg-3 { order: 3; } .order-lg-4 { order: 4; } .order-lg-5 { order: 5; } .order-lg-6 { order: 6; } .order-lg-7 { order: 7; } .order-lg-8 { order: 8; } .order-lg-9 { order: 9; } .order-lg-10 { order: 10; } .order-lg-11 { order: 11; } .order-lg-12 { order: 12; } .offset-lg-0 { margin-left: 0; } .offset-lg-1 { margin-left: 8.333333%; } .offset-lg-2 { margin-left: 16.666667%; } .offset-lg-3 { margin-left: 25%; } .offset-lg-4 { margin-left: 33.333333%; } .offset-lg-5 { margin-left: 41.666667%; } .offset-lg-6 { margin-left: 50%; } .offset-lg-7 { margin-left: 58.333333%; } .offset-lg-8 { margin-left: 66.666667%; } .offset-lg-9 { margin-left: 75%; } .offset-lg-10 { margin-left: 83.333333%; } .offset-lg-11 { margin-left: 91.666667%; } } @media (min-width: 1200px) { .col-xl { flex-basis: 0; flex-grow: 1; max-width: 100%; } .col-xl-auto { flex: 0 0 auto; width: auto; max-width: none; } .col-xl-1 { flex: 0 0 8.333333%; max-width: 8.333333%; } .col-xl-2 { flex: 0 0 16.666667%; max-width: 16.666667%; } .col-xl-3 { flex: 0 0 25%; max-width: 25%; } .col-xl-4 { flex: 0 0 33.333333%; max-width: 33.333333%; } .col-xl-5 { flex: 0 0 41.666667%; max-width: 41.666667%; } .col-xl-6 { flex: 0 0 50%; max-width: 50%; } .col-xl-7 { flex: 0 0 58.333333%; max-width: 58.333333%; } .col-xl-8 { flex: 0 0 66.666667%; max-width: 66.666667%; } .col-xl-9 { flex: 0 0 75%; max-width: 75%; } .col-xl-10 { flex: 0 0 83.333333%; max-width: 83.333333%; } .col-xl-11 { flex: 0 0 91.666667%; max-width: 91.666667%; } .col-xl-12 { flex: 0 0 100%; max-width: 100%; } .order-xl-first { order: -1; } .order-xl-last { order: 13; } .order-xl-0 { order: 0; } .order-xl-1 { order: 1; } .order-xl-2 { order: 2; } .order-xl-3 { order: 3; } .order-xl-4 { order: 4; } .order-xl-5 { order: 5; } .order-xl-6 { order: 6; } .order-xl-7 { order: 7; } .order-xl-8 { order: 8; } .order-xl-9 { order: 9; } .order-xl-10 { order: 10; } .order-xl-11 { order: 11; } .order-xl-12 { order: 12; } .offset-xl-0 { margin-left: 0; } .offset-xl-1 { margin-left: 8.333333%; } .offset-xl-2 { margin-left: 16.666667%; } .offset-xl-3 { margin-left: 25%; } .offset-xl-4 { margin-left: 33.333333%; } .offset-xl-5 { margin-left: 41.666667%; } .offset-xl-6 { margin-left: 50%; } .offset-xl-7 { margin-left: 58.333333%; } .offset-xl-8 { margin-left: 66.666667%; } .offset-xl-9 { margin-left: 75%; } .offset-xl-10 { margin-left: 83.333333%; } .offset-xl-11 { margin-left: 91.666667%; } } .table { width: 100%; max-width: 100%; margin-bottom: 1rem; background-color: transparent; } .table th, .table td { padding: 0.75rem; vertical-align: top; border-top: 1px solid #dee2e6; } .table thead th { vertical-align: bottom; border-bottom: 2px solid #dee2e6; } .table tbody + tbody { border-top: 2px solid #dee2e6; } .table .table { background-color: #ffffff; } .table-sm th, .table-sm td { padding: 0.3rem; } .table-bordered { border: 1px solid #dee2e6; } .table-bordered th, .table-bordered td { border: 1px solid #dee2e6; } .table-bordered thead th, .table-bordered thead td { border-bottom-width: 2px; } .table-borderless th, .table-borderless td, .table-borderless thead th, .table-borderless tbody + tbody { border: 0; } .table-striped tbody tr:nth-of-type(odd) { background-color: rgba(0, 0, 0, 0.05); } .table-hover tbody tr:hover { background-color: rgba(0, 0, 0, 0.075); } .table-primary, .table-primary > th, .table-primary > td { background-color: #b8daff; } .table-hover .table-primary:hover { background-color: #9fcdff; } .table-hover .table-primary:hover > td, .table-hover .table-primary:hover > th { background-color: #9fcdff; } .table-secondary, .table-secondary > th, .table-secondary > td { background-color: #d6d8db; } .table-hover .table-secondary:hover { background-color: #c8cbcf; } .table-hover .table-secondary:hover > td, .table-hover .table-secondary:hover > th { background-color: #c8cbcf; } .table-success, .table-success > th, .table-success > td { background-color: #c3e6cb; } .table-hover .table-success:hover { background-color: #b1dfbb; } .table-hover .table-success:hover > td, .table-hover .table-success:hover > th { background-color: #b1dfbb; } .table-info, .table-info > th, .table-info > td { background-color: #bee5eb; } .table-hover .table-info:hover { background-color: #abdde5; } .table-hover .table-info:hover > td, .table-hover .table-info:hover > th { background-color: #abdde5; } .table-warning, .table-warning > th, .table-warning > td { background-color: #ffeeba; } .table-hover .table-warning:hover { background-color: #ffe8a1; } .table-hover .table-warning:hover > td, .table-hover .table-warning:hover > th { background-color: #ffe8a1; } .table-danger, .table-danger > th, .table-danger > td { background-color: #f5c6cb; } .table-hover .table-danger:hover { background-color: #f1b0b7; } .table-hover .table-danger:hover > td, .table-hover .table-danger:hover > th { background-color: #f1b0b7; } .table-light, .table-light > th, .table-light > td { background-color: #fdfdfe; } .table-hover .table-light:hover { background-color: #ececf6; } .table-hover .table-light:hover > td, .table-hover .table-light:hover > th { background-color: #ececf6; } .table-dark, .table-dark > th, .table-dark > td { background-color: #c6c8ca; } .table-hover .table-dark:hover { background-color: #b9bbbe; } .table-hover .table-dark:hover > td, .table-hover .table-dark:hover > th { background-color: #b9bbbe; } .table-active, .table-active > th, .table-active > td { background-color: rgba(0, 0, 0, 0.075); } .table-hover .table-active:hover { background-color: rgba(0, 0, 0, 0.075); } .table-hover .table-active:hover > td, .table-hover .table-active:hover > th { background-color: rgba(0, 0, 0, 0.075); } .table .thead-dark th { color: #ffffff; background-color: #212529; border-color: #32383e; } .table .thead-light th { color: #495057; background-color: #e9ecef; border-color: #dee2e6; } .table-dark { color: #ffffff; background-color: #212529; } .table-dark th, .table-dark td, .table-dark thead th { border-color: #32383e; } .table-dark.table-bordered { border: 0; } .table-dark.table-striped tbody tr:nth-of-type(odd) { background-color: rgba(255, 255, 255, 0.05); } .table-dark.table-hover tbody tr:hover { background-color: rgba(255, 255, 255, 0.075); } @media (max-width: 575.98px) { .table-responsive-sm { display: block; width: 100%; overflow-x: auto; -webkit-overflow-scrolling: touch; -ms-overflow-style: -ms-autohiding-scrollbar; } .table-responsive-sm > .table-bordered { border: 0; } } @media (max-width: 767.98px) { .table-responsive-md { display: block; width: 100%; overflow-x: auto; -webkit-overflow-scrolling: touch; -ms-overflow-style: -ms-autohiding-scrollbar; } .table-responsive-md > .table-bordered { border: 0; } } @media (max-width: 991.98px) { .table-responsive-lg { display: block; width: 100%; overflow-x: auto; -webkit-overflow-scrolling: touch; -ms-overflow-style: -ms-autohiding-scrollbar; } .table-responsive-lg > .table-bordered { border: 0; } } @media (max-width: 1199.98px) { .table-responsive-xl { display: block; width: 100%; overflow-x: auto; -webkit-overflow-scrolling: touch; -ms-overflow-style: -ms-autohiding-scrollbar; } .table-responsive-xl > .table-bordered { border: 0; } } .table-responsive { display: block; width: 100%; overflow-x: auto; -webkit-overflow-scrolling: touch; -ms-overflow-style: -ms-autohiding-scrollbar; } .table-responsive > .table-bordered { border: 0; } .form-control { display: block; width: 100%; padding: 0.375rem 0.75rem; font-size: 1rem; line-height: 1.5; color: #495057; background-color: #ffffff; background-clip: padding-box; border: 1px solid #ced4da; border-radius: 0.25rem; box-shadow: inset 0 0 0 transparent; transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; } @media screen and (prefers-reduced-motion: reduce) { .form-control { transition: none; } } .form-control::-ms-expand { background-color: transparent; border: 0; } .form-control:focus { color: #495057; background-color: #ffffff; border-color: #80bdff; outline: 0; box-shadow: inset 0 0 0 transparent, 0 0 0 0.2rem rgba(0, 123, 255, 0.25); } .form-control::placeholder { color: #6c757d; opacity: 1; } .form-control:disabled, .form-control[readonly] { background-color: #e9ecef; opacity: 1; } select.form-control:not([size]):not([multiple]) { height: calc(2.25rem + 2px); } select.form-control:focus::-ms-value { color: #495057; background-color: #ffffff; } .form-control-file, .form-control-range { display: block; width: 100%; } .col-form-label { padding-top: calc(0.375rem + 1px); padding-bottom: calc(0.375rem + 1px); margin-bottom: 0; font-size: inherit; line-height: 1.5; } .col-form-label-lg { padding-top: calc(0.5rem + 1px); padding-bottom: calc(0.5rem + 1px); font-size: 1.25rem; line-height: 1.5; } .col-form-label-sm { padding-top: calc(0.25rem + 1px); padding-bottom: calc(0.25rem + 1px); font-size: 0.875rem; line-height: 1.5; } .form-control-plaintext { display: block; width: 100%; padding-top: 0.375rem; padding-bottom: 0.375rem; margin-bottom: 0; line-height: 1.5; color: #212529; background-color: transparent; border: solid transparent; border-width: 1px 0; } .form-control-plaintext.form-control-sm, .input-group-sm > .form-control-plaintext.form-control, .input-group-sm > .input-group-prepend > .form-control-plaintext.input-group-text, .input-group-sm > .input-group-append > .form-control-plaintext.input-group-text, .input-group-sm > .input-group-prepend > .form-control-plaintext.btn, .input-group-sm > .input-group-append > .form-control-plaintext.btn, .form-control-plaintext.form-control-lg, .input-group-lg > .form-control-plaintext.form-control, .input-group-lg > .input-group-prepend > .form-control-plaintext.input-group-text, .input-group-lg > .input-group-append > .form-control-plaintext.input-group-text, .input-group-lg > .input-group-prepend > .form-control-plaintext.btn, .input-group-lg > .input-group-append > .form-control-plaintext.btn { padding-right: 0; padding-left: 0; } .form-control-sm, .input-group-sm > .form-control, .input-group-sm > .input-group-prepend > .input-group-text, .input-group-sm > .input-group-append > .input-group-text, .input-group-sm > .input-group-prepend > .btn, .input-group-sm > .input-group-append > .btn { padding: 0.25rem 0.5rem; font-size: 0.875rem; line-height: 1.5; border-radius: 0.2rem; } select.form-control-sm:not([size]):not([multiple]), .input-group-sm > select.form-control:not([size]):not([multiple]), .input-group-sm > .input-group-prepend > select.input-group-text:not([size]):not([multiple]), .input-group-sm > .input-group-append > select.input-group-text:not([size]):not([multiple]), .input-group-sm > .input-group-prepend > select.btn:not([size]):not([multiple]), .input-group-sm > .input-group-append > select.btn:not([size]):not([multiple]) { height: calc(1.8125rem + 2px); } .form-control-lg, .input-group-lg > .form-control, .input-group-lg > .input-group-prepend > .input-group-text, .input-group-lg > .input-group-append > .input-group-text, .input-group-lg > .input-group-prepend > .btn, .input-group-lg > .input-group-append > .btn { padding: 0.5rem 1rem; font-size: 1.25rem; line-height: 1.5; border-radius: 0.3rem; } select.form-control-lg:not([size]):not([multiple]), .input-group-lg > select.form-control:not([size]):not([multiple]), .input-group-lg > .input-group-prepend > select.input-group-text:not([size]):not([multiple]), .input-group-lg > .input-group-append > select.input-group-text:not([size]):not([multiple]), .input-group-lg > .input-group-prepend > select.btn:not([size]):not([multiple]), .input-group-lg > .input-group-append > select.btn:not([size]):not([multiple]) { height: calc(2.875rem + 2px); } .form-group { margin-bottom: 1rem; } .form-text { display: block; margin-top: 0.25rem; } .form-row { display: flex; flex-wrap: wrap; margin-right: -5px; margin-left: -5px; } .form-row > .col, .form-row > [class*="col-"] { padding-right: 5px; padding-left: 5px; } .form-check { position: relative; display: block; padding-left: 1.25rem; } .form-check-input { position: absolute; margin-top: 0.3rem; margin-left: -1.25rem; } .form-check-input:disabled ~ .form-check-label { color: #6c757d; } .form-check-label { margin-bottom: 0; } .form-check-inline { display: inline-flex; align-items: center; padding-left: 0; margin-right: 0.75rem; } .form-check-inline .form-check-input { position: static; margin-top: 0; margin-right: 0.3125rem; margin-left: 0; } .valid-feedback { display: none; width: 100%; margin-top: 0.25rem; font-size: 80%; color: #28a745; } .valid-tooltip { position: absolute; top: 100%; z-index: 5; display: none; max-width: 100%; padding: .5rem; margin-top: .1rem; font-size: .875rem; line-height: 1; color: #ffffff; background-color: rgba(40, 167, 69, 0.8); border-radius: .2rem; } .was-validated .form-control:valid, .form-control.is-valid, .was-validated .custom-select:valid, .custom-select.is-valid { border-color: #28a745; } .was-validated .form-control:valid:focus, .form-control.is-valid:focus, .was-validated .custom-select:valid:focus, .custom-select.is-valid:focus { border-color: #28a745; box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25); } .was-validated .form-control:valid ~ .valid-feedback, .was-validated .form-control:valid ~ .valid-tooltip, .form-control.is-valid ~ .valid-feedback, .form-control.is-valid ~ .valid-tooltip, .was-validated .custom-select:valid ~ .valid-feedback, .was-validated .custom-select:valid ~ .valid-tooltip, .custom-select.is-valid ~ .valid-feedback, .custom-select.is-valid ~ .valid-tooltip { display: block; } .was-validated .form-check-input:valid ~ .form-check-label, .form-check-input.is-valid ~ .form-check-label { color: #28a745; } .was-validated .form-check-input:valid ~ .valid-feedback, .was-validated .form-check-input:valid ~ .valid-tooltip, .form-check-input.is-valid ~ .valid-feedback, .form-check-input.is-valid ~ .valid-tooltip { display: block; } .was-validated .custom-control-input:valid ~ .custom-control-label, .custom-control-input.is-valid ~ .custom-control-label { color: #28a745; } .was-validated .custom-control-input:valid ~ .custom-control-label::before, .custom-control-input.is-valid ~ .custom-control-label::before { background-color: #71dd8a; } .was-validated .custom-control-input:valid ~ .valid-feedback, .was-validated .custom-control-input:valid ~ .valid-tooltip, .custom-control-input.is-valid ~ .valid-feedback, .custom-control-input.is-valid ~ .valid-tooltip { display: block; } .was-validated .custom-control-input:valid:checked ~ .custom-control-label::before, .custom-control-input.is-valid:checked ~ .custom-control-label::before { background-color: #34ce57; } .was-validated .custom-control-input:valid:focus ~ .custom-control-label::before, .custom-control-input.is-valid:focus ~ .custom-control-label::before { box-shadow: 0 0 0 1px #ffffff, 0 0 0 0.2rem rgba(40, 167, 69, 0.25); } .was-validated .custom-file-input:valid ~ .custom-file-label, .custom-file-input.is-valid ~ .custom-file-label { border-color: #28a745; } .was-validated .custom-file-input:valid ~ .custom-file-label::before, .custom-file-input.is-valid ~ .custom-file-label::before { border-color: inherit; } .was-validated .custom-file-input:valid ~ .valid-feedback, .was-validated .custom-file-input:valid ~ .valid-tooltip, .custom-file-input.is-valid ~ .valid-feedback, .custom-file-input.is-valid ~ .valid-tooltip { display: block; } .was-validated .custom-file-input:valid:focus ~ .custom-file-label, .custom-file-input.is-valid:focus ~ .custom-file-label { box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25); } .invalid-feedback { display: none; width: 100%; margin-top: 0.25rem; font-size: 80%; color: #dc3545; } .invalid-tooltip { position: absolute; top: 100%; z-index: 5; display: none; max-width: 100%; padding: .5rem; margin-top: .1rem; font-size: .875rem; line-height: 1; color: #ffffff; background-color: rgba(220, 53, 69, 0.8); border-radius: .2rem; } .was-validated .form-control:invalid, .form-control.is-invalid, .was-validated .custom-select:invalid, .custom-select.is-invalid { border-color: #dc3545; } .was-validated .form-control:invalid:focus, .form-control.is-invalid:focus, .was-validated .custom-select:invalid:focus, .custom-select.is-invalid:focus { border-color: #dc3545; box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25); } .was-validated .form-control:invalid ~ .invalid-feedback, .was-validated .form-control:invalid ~ .invalid-tooltip, .form-control.is-invalid ~ .invalid-feedback, .form-control.is-invalid ~ .invalid-tooltip, .was-validated .custom-select:invalid ~ .invalid-feedback, .was-validated .custom-select:invalid ~ .invalid-tooltip, .custom-select.is-invalid ~ .invalid-feedback, .custom-select.is-invalid ~ .invalid-tooltip { display: block; } .was-validated .form-check-input:invalid ~ .form-check-label, .form-check-input.is-invalid ~ .form-check-label { color: #dc3545; } .was-validated .form-check-input:invalid ~ .invalid-feedback, .was-validated .form-check-input:invalid ~ .invalid-tooltip, .form-check-input.is-invalid ~ .invalid-feedback, .form-check-input.is-invalid ~ .invalid-tooltip { display: block; } .was-validated .custom-control-input:invalid ~ .custom-control-label, .custom-control-input.is-invalid ~ .custom-control-label { color: #dc3545; } .was-validated .custom-control-input:invalid ~ .custom-control-label::before, .custom-control-input.is-invalid ~ .custom-control-label::before { background-color: #efa2a9; } .was-validated .custom-control-input:invalid ~ .invalid-feedback, .was-validated .custom-control-input:invalid ~ .invalid-tooltip, .custom-control-input.is-invalid ~ .invalid-feedback, .custom-control-input.is-invalid ~ .invalid-tooltip { display: block; } .was-validated .custom-control-input:invalid:checked ~ .custom-control-label::before, .custom-control-input.is-invalid:checked ~ .custom-control-label::before { background-color: #e4606d; } .was-validated .custom-control-input:invalid:focus ~ .custom-control-label::before, .custom-control-input.is-invalid:focus ~ .custom-control-label::before { box-shadow: 0 0 0 1px #ffffff, 0 0 0 0.2rem rgba(220, 53, 69, 0.25); } .was-validated .custom-file-input:invalid ~ .custom-file-label, .custom-file-input.is-invalid ~ .custom-file-label { border-color: #dc3545; } .was-validated .custom-file-input:invalid ~ .custom-file-label::before, .custom-file-input.is-invalid ~ .custom-file-label::before { border-color: inherit; } .was-validated .custom-file-input:invalid ~ .invalid-feedback, .was-validated .custom-file-input:invalid ~ .invalid-tooltip, .custom-file-input.is-invalid ~ .invalid-feedback, .custom-file-input.is-invalid ~ .invalid-tooltip { display: block; } .was-validated .custom-file-input:invalid:focus ~ .custom-file-label, .custom-file-input.is-invalid:focus ~ .custom-file-label { box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25); } .form-inline { display: flex; flex-flow: row wrap; align-items: center; } .form-inline .form-check { width: 100%; } @media (min-width: 576px) { .form-inline label { display: flex; align-items: center; justify-content: center; margin-bottom: 0; } .form-inline .form-group { display: flex; flex: 0 0 auto; flex-flow: row wrap; align-items: center; margin-bottom: 0; } .form-inline .form-control { display: inline-block; width: auto; vertical-align: middle; } .form-inline .form-control-plaintext { display: inline-block; } .form-inline .input-group, .form-inline .custom-select { width: auto; } .form-inline .form-check { display: flex; align-items: center; justify-content: center; width: auto; padding-left: 0; } .form-inline .form-check-input { position: relative; margin-top: 0; margin-right: 0.25rem; margin-left: 0; } .form-inline .custom-control { align-items: center; justify-content: center; } .form-inline .custom-control-label { margin-bottom: 0; } } .btn { display: inline-block; font-weight: 400; text-align: center; white-space: nowrap; vertical-align: middle; user-select: none; border: 1px solid transparent; padding: 0.375rem 0.75rem; font-size: 1rem; line-height: 1.5; border-radius: 0.25rem; transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; } @media screen and (prefers-reduced-motion: reduce) { .btn { transition: none; } } .btn:hover, .btn:focus { text-decoration: none; } .btn:focus, .btn.focus { outline: 0; box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); } .btn.disabled, .btn:disabled { opacity: 0.65; box-shadow: none; } .btn:not(:disabled):not(.disabled) { cursor: pointer; } .btn:not(:disabled):not(.disabled):active, .btn:not(:disabled):not(.disabled).active { background-image: none; box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); } .btn:not(:disabled):not(.disabled):active:focus, .btn:not(:disabled):not(.disabled).active:focus { box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25), inset 0 3px 5px rgba(0, 0, 0, 0.125); } a.btn.disabled, fieldset:disabled a.btn { pointer-events: none; } .btn-primary { color: #ffffff; background-color: #007bff; border-color: #007bff; box-shadow: 0 1px 1px rgba(0, 0, 0, 0.075); } .btn-primary:hover { color: #ffffff; background-color: #0069d9; border-color: #0062cc; } .btn-primary:focus, .btn-primary.focus { box-shadow: 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 0 0.2rem rgba(0, 123, 255, 0.5); } .btn-primary.disabled, .btn-primary:disabled { color: #ffffff; background-color: #007bff; border-color: #007bff; } .btn-primary:not(:disabled):not(.disabled):active, .btn-primary:not(:disabled):not(.disabled).active, .show > .btn-primary.dropdown-toggle { color: #ffffff; background-color: #0062cc; border-color: #005cbf; } .btn-primary:not(:disabled):not(.disabled):active:focus, .btn-primary:not(:disabled):not(.disabled).active:focus, .show > .btn-primary.dropdown-toggle:focus { box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125), 0 0 0 0.2rem rgba(0, 123, 255, 0.5); } .btn-secondary { color: #ffffff; background-color: #6c757d; border-color: #6c757d; box-shadow: 0 1px 1px rgba(0, 0, 0, 0.075); } .btn-secondary:hover { color: #ffffff; background-color: #5a6268; border-color: #545b62; } .btn-secondary:focus, .btn-secondary.focus { box-shadow: 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 0 0.2rem rgba(108, 117, 125, 0.5); } .btn-secondary.disabled, .btn-secondary:disabled { color: #ffffff; background-color: #6c757d; border-color: #6c757d; } .btn-secondary:not(:disabled):not(.disabled):active, .btn-secondary:not(:disabled):not(.disabled).active, .show > .btn-secondary.dropdown-toggle { color: #ffffff; background-color: #545b62; border-color: #4e555b; } .btn-secondary:not(:disabled):not(.disabled):active:focus, .btn-secondary:not(:disabled):not(.disabled).active:focus, .show > .btn-secondary.dropdown-toggle:focus { box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125), 0 0 0 0.2rem rgba(108, 117, 125, 0.5); } .btn-success { color: #ffffff; background-color: #28a745; border-color: #28a745; box-shadow: 0 1px 1px rgba(0, 0, 0, 0.075); } .btn-success:hover { color: #ffffff; background-color: #218838; border-color: #1e7e34; } .btn-success:focus, .btn-success.focus { box-shadow: 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 0 0.2rem rgba(40, 167, 69, 0.5); } .btn-success.disabled, .btn-success:disabled { color: #ffffff; background-color: #28a745; border-color: #28a745; } .btn-success:not(:disabled):not(.disabled):active, .btn-success:not(:disabled):not(.disabled).active, .show > .btn-success.dropdown-toggle { color: #ffffff; background-color: #1e7e34; border-color: #1c7430; } .btn-success:not(:disabled):not(.disabled):active:focus, .btn-success:not(:disabled):not(.disabled).active:focus, .show > .btn-success.dropdown-toggle:focus { box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125), 0 0 0 0.2rem rgba(40, 167, 69, 0.5); } .btn-info { color: #ffffff; background-color: #17a2b8; border-color: #17a2b8; box-shadow: 0 1px 1px rgba(0, 0, 0, 0.075); } .btn-info:hover { color: #ffffff; background-color: #138496; border-color: #117a8b; } .btn-info:focus, .btn-info.focus { box-shadow: 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 0 0.2rem rgba(23, 162, 184, 0.5); } .btn-info.disabled, .btn-info:disabled { color: #ffffff; background-color: #17a2b8; border-color: #17a2b8; } .btn-info:not(:disabled):not(.disabled):active, .btn-info:not(:disabled):not(.disabled).active, .show > .btn-info.dropdown-toggle { color: #ffffff; background-color: #117a8b; border-color: #10707f; } .btn-info:not(:disabled):not(.disabled):active:focus, .btn-info:not(:disabled):not(.disabled).active:focus, .show > .btn-info.dropdown-toggle:focus { box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125), 0 0 0 0.2rem rgba(23, 162, 184, 0.5); } .btn-warning { color: #1F2D3D; background-color: #ffc107; border-color: #ffc107; box-shadow: 0 1px 1px rgba(0, 0, 0, 0.075); } .btn-warning:hover { color: #1F2D3D; background-color: #e0a800; border-color: #d39e00; } .btn-warning:focus, .btn-warning.focus { box-shadow: 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 0 0.2rem rgba(255, 193, 7, 0.5); } .btn-warning.disabled, .btn-warning:disabled { color: #1F2D3D; background-color: #ffc107; border-color: #ffc107; } .btn-warning:not(:disabled):not(.disabled):active, .btn-warning:not(:disabled):not(.disabled).active, .show > .btn-warning.dropdown-toggle { color: #1F2D3D; background-color: #d39e00; border-color: #c69500; } .btn-warning:not(:disabled):not(.disabled):active:focus, .btn-warning:not(:disabled):not(.disabled).active:focus, .show > .btn-warning.dropdown-toggle:focus { box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125), 0 0 0 0.2rem rgba(255, 193, 7, 0.5); } .btn-danger { color: #ffffff; background-color: #dc3545; border-color: #dc3545; box-shadow: 0 1px 1px rgba(0, 0, 0, 0.075); } .btn-danger:hover { color: #ffffff; background-color: #c82333; border-color: #bd2130; } .btn-danger:focus, .btn-danger.focus { box-shadow: 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 0 0.2rem rgba(220, 53, 69, 0.5); } .btn-danger.disabled, .btn-danger:disabled { color: #ffffff; background-color: #dc3545; border-color: #dc3545; } .btn-danger:not(:disabled):not(.disabled):active, .btn-danger:not(:disabled):not(.disabled).active, .show > .btn-danger.dropdown-toggle { color: #ffffff; background-color: #bd2130; border-color: #b21f2d; } .btn-danger:not(:disabled):not(.disabled):active:focus, .btn-danger:not(:disabled):not(.disabled).active:focus, .show > .btn-danger.dropdown-toggle:focus { box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125), 0 0 0 0.2rem rgba(220, 53, 69, 0.5); } .btn-light { color: #1F2D3D; background-color: #f8f9fa; border-color: #f8f9fa; box-shadow: 0 1px 1px rgba(0, 0, 0, 0.075); } .btn-light:hover { color: #1F2D3D; background-color: #e2e6ea; border-color: #dae0e5; } .btn-light:focus, .btn-light.focus { box-shadow: 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 0 0.2rem rgba(248, 249, 250, 0.5); } .btn-light.disabled, .btn-light:disabled { color: #1F2D3D; background-color: #f8f9fa; border-color: #f8f9fa; } .btn-light:not(:disabled):not(.disabled):active, .btn-light:not(:disabled):not(.disabled).active, .show > .btn-light.dropdown-toggle { color: #1F2D3D; background-color: #dae0e5; border-color: #d3d9df; } .btn-light:not(:disabled):not(.disabled):active:focus, .btn-light:not(:disabled):not(.disabled).active:focus, .show > .btn-light.dropdown-toggle:focus { box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125), 0 0 0 0.2rem rgba(248, 249, 250, 0.5); } .btn-dark { color: #ffffff; background-color: #343a40; border-color: #343a40; box-shadow: 0 1px 1px rgba(0, 0, 0, 0.075); } .btn-dark:hover { color: #ffffff; background-color: #23272b; border-color: #1d2124; } .btn-dark:focus, .btn-dark.focus { box-shadow: 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 0 0.2rem rgba(52, 58, 64, 0.5); } .btn-dark.disabled, .btn-dark:disabled { color: #ffffff; background-color: #343a40; border-color: #343a40; } .btn-dark:not(:disabled):not(.disabled):active, .btn-dark:not(:disabled):not(.disabled).active, .show > .btn-dark.dropdown-toggle { color: #ffffff; background-color: #1d2124; border-color: #171a1d; } .btn-dark:not(:disabled):not(.disabled):active:focus, .btn-dark:not(:disabled):not(.disabled).active:focus, .show > .btn-dark.dropdown-toggle:focus { box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125), 0 0 0 0.2rem rgba(52, 58, 64, 0.5); } .btn-outline-primary { color: #007bff; background-color: transparent; background-image: none; border-color: #007bff; } .btn-outline-primary:hover { color: #ffffff; background-color: #007bff; border-color: #007bff; } .btn-outline-primary:focus, .btn-outline-primary.focus { box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5); } .btn-outline-primary.disabled, .btn-outline-primary:disabled { color: #007bff; background-color: transparent; } .btn-outline-primary:not(:disabled):not(.disabled):active, .btn-outline-primary:not(:disabled):not(.disabled).active, .show > .btn-outline-primary.dropdown-toggle { color: #ffffff; background-color: #007bff; border-color: #007bff; } .btn-outline-primary:not(:disabled):not(.disabled):active:focus, .btn-outline-primary:not(:disabled):not(.disabled).active:focus, .show > .btn-outline-primary.dropdown-toggle:focus { box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125), 0 0 0 0.2rem rgba(0, 123, 255, 0.5); } .btn-outline-secondary { color: #6c757d; background-color: transparent; background-image: none; border-color: #6c757d; } .btn-outline-secondary:hover { color: #ffffff; background-color: #6c757d; border-color: #6c757d; } .btn-outline-secondary:focus, .btn-outline-secondary.focus { box-shadow: 0 0 0 0.2rem rgba(108, 117, 125, 0.5); } .btn-outline-secondary.disabled, .btn-outline-secondary:disabled { color: #6c757d; background-color: transparent; } .btn-outline-secondary:not(:disabled):not(.disabled):active, .btn-outline-secondary:not(:disabled):not(.disabled).active, .show > .btn-outline-secondary.dropdown-toggle { color: #ffffff; background-color: #6c757d; border-color: #6c757d; } .btn-outline-secondary:not(:disabled):not(.disabled):active:focus, .btn-outline-secondary:not(:disabled):not(.disabled).active:focus, .show > .btn-outline-secondary.dropdown-toggle:focus { box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125), 0 0 0 0.2rem rgba(108, 117, 125, 0.5); } .btn-outline-success { color: #28a745; background-color: transparent; background-image: none; border-color: #28a745; } .btn-outline-success:hover { color: #ffffff; background-color: #28a745; border-color: #28a745; } .btn-outline-success:focus, .btn-outline-success.focus { box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5); } .btn-outline-success.disabled, .btn-outline-success:disabled { color: #28a745; background-color: transparent; } .btn-outline-success:not(:disabled):not(.disabled):active, .btn-outline-success:not(:disabled):not(.disabled).active, .show > .btn-outline-success.dropdown-toggle { color: #ffffff; background-color: #28a745; border-color: #28a745; } .btn-outline-success:not(:disabled):not(.disabled):active:focus, .btn-outline-success:not(:disabled):not(.disabled).active:focus, .show > .btn-outline-success.dropdown-toggle:focus { box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125), 0 0 0 0.2rem rgba(40, 167, 69, 0.5); } .btn-outline-info { color: #17a2b8; background-color: transparent; background-image: none; border-color: #17a2b8; } .btn-outline-info:hover { color: #ffffff; background-color: #17a2b8; border-color: #17a2b8; } .btn-outline-info:focus, .btn-outline-info.focus { box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5); } .btn-outline-info.disabled, .btn-outline-info:disabled { color: #17a2b8; background-color: transparent; } .btn-outline-info:not(:disabled):not(.disabled):active, .btn-outline-info:not(:disabled):not(.disabled).active, .show > .btn-outline-info.dropdown-toggle { color: #ffffff; background-color: #17a2b8; border-color: #17a2b8; } .btn-outline-info:not(:disabled):not(.disabled):active:focus, .btn-outline-info:not(:disabled):not(.disabled).active:focus, .show > .btn-outline-info.dropdown-toggle:focus { box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125), 0 0 0 0.2rem rgba(23, 162, 184, 0.5); } .btn-outline-warning { color: #ffc107; background-color: transparent; background-image: none; border-color: #ffc107; } .btn-outline-warning:hover { color: #1F2D3D; background-color: #ffc107; border-color: #ffc107; } .btn-outline-warning:focus, .btn-outline-warning.focus { box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5); } .btn-outline-warning.disabled, .btn-outline-warning:disabled { color: #ffc107; background-color: transparent; } .btn-outline-warning:not(:disabled):not(.disabled):active, .btn-outline-warning:not(:disabled):not(.disabled).active, .show > .btn-outline-warning.dropdown-toggle { color: #1F2D3D; background-color: #ffc107; border-color: #ffc107; } .btn-outline-warning:not(:disabled):not(.disabled):active:focus, .btn-outline-warning:not(:disabled):not(.disabled).active:focus, .show > .btn-outline-warning.dropdown-toggle:focus { box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125), 0 0 0 0.2rem rgba(255, 193, 7, 0.5); } .btn-outline-danger { color: #dc3545; background-color: transparent; background-image: none; border-color: #dc3545; } .btn-outline-danger:hover { color: #ffffff; background-color: #dc3545; border-color: #dc3545; } .btn-outline-danger:focus, .btn-outline-danger.focus { box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5); } .btn-outline-danger.disabled, .btn-outline-danger:disabled { color: #dc3545; background-color: transparent; } .btn-outline-danger:not(:disabled):not(.disabled):active, .btn-outline-danger:not(:disabled):not(.disabled).active, .show > .btn-outline-danger.dropdown-toggle { color: #ffffff; background-color: #dc3545; border-color: #dc3545; } .btn-outline-danger:not(:disabled):not(.disabled):active:focus, .btn-outline-danger:not(:disabled):not(.disabled).active:focus, .show > .btn-outline-danger.dropdown-toggle:focus { box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125), 0 0 0 0.2rem rgba(220, 53, 69, 0.5); } .btn-outline-light { color: #f8f9fa; background-color: transparent; background-image: none; border-color: #f8f9fa; } .btn-outline-light:hover { color: #1F2D3D; background-color: #f8f9fa; border-color: #f8f9fa; } .btn-outline-light:focus, .btn-outline-light.focus { box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5); } .btn-outline-light.disabled, .btn-outline-light:disabled { color: #f8f9fa; background-color: transparent; } .btn-outline-light:not(:disabled):not(.disabled):active, .btn-outline-light:not(:disabled):not(.disabled).active, .show > .btn-outline-light.dropdown-toggle { color: #1F2D3D; background-color: #f8f9fa; border-color: #f8f9fa; } .btn-outline-light:not(:disabled):not(.disabled):active:focus, .btn-outline-light:not(:disabled):not(.disabled).active:focus, .show > .btn-outline-light.dropdown-toggle:focus { box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125), 0 0 0 0.2rem rgba(248, 249, 250, 0.5); } .btn-outline-dark { color: #343a40; background-color: transparent; background-image: none; border-color: #343a40; } .btn-outline-dark:hover { color: #ffffff; background-color: #343a40; border-color: #343a40; } .btn-outline-dark:focus, .btn-outline-dark.focus { box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5); } .btn-outline-dark.disabled, .btn-outline-dark:disabled { color: #343a40; background-color: transparent; } .btn-outline-dark:not(:disabled):not(.disabled):active, .btn-outline-dark:not(:disabled):not(.disabled).active, .show > .btn-outline-dark.dropdown-toggle { color: #ffffff; background-color: #343a40; border-color: #343a40; } .btn-outline-dark:not(:disabled):not(.disabled):active:focus, .btn-outline-dark:not(:disabled):not(.disabled).active:focus, .show > .btn-outline-dark.dropdown-toggle:focus { box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125), 0 0 0 0.2rem rgba(52, 58, 64, 0.5); } .btn-link { font-weight: 400; color: #007bff; background-color: transparent; } .btn-link:hover { color: #0056b3; text-decoration: none; background-color: transparent; border-color: transparent; } .btn-link:focus, .btn-link.focus { text-decoration: none; border-color: transparent; box-shadow: none; } .btn-link:disabled, .btn-link.disabled { color: #6c757d; pointer-events: none; } .btn-lg, .btn-group-lg > .btn { padding: 0.5rem 1rem; font-size: 1.25rem; line-height: 1.5; border-radius: 0.3rem; } .btn-sm, .btn-group-sm > .btn { padding: 0.25rem 0.5rem; font-size: 0.875rem; line-height: 1.5; border-radius: 0.2rem; } .btn-block { display: block; width: 100%; } .btn-block + .btn-block { margin-top: 0.5rem; } input[type="submit"].btn-block, input[type="reset"].btn-block, input[type="button"].btn-block { width: 100%; } .fade { transition: opacity 0.15s linear; } @media screen and (prefers-reduced-motion: reduce) { .fade { transition: none; } } .fade:not(.show) { opacity: 0; } .collapse:not(.show) { display: none; } .collapsing { position: relative; height: 0; overflow: hidden; transition: height 0.35s ease; } @media screen and (prefers-reduced-motion: reduce) { .collapsing { transition: none; } } .dropup, .dropright, .dropdown, .dropleft { position: relative; } .dropdown-toggle::after { display: inline-block; width: 0; height: 0; margin-left: 0.255em; vertical-align: 0.255em; content: ""; border-top: 0.3em solid; border-right: 0.3em solid transparent; border-bottom: 0; border-left: 0.3em solid transparent; } .dropdown-toggle:empty::after { margin-left: 0; } .dropdown-menu { position: absolute; top: 100%; left: 0; z-index: 1000; display: none; float: left; min-width: 10rem; padding: 0.5rem 0; margin: 0.125rem 0 0; font-size: 1rem; color: #212529; text-align: left; list-style: none; background-color: #ffffff; background-clip: padding-box; border: 1px solid rgba(0, 0, 0, 0.15); border-radius: 0.25rem; box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.175); } .dropdown-menu-right { right: 0; left: auto; } .dropup .dropdown-menu { top: auto; bottom: 100%; margin-top: 0; margin-bottom: 0.125rem; } .dropup .dropdown-toggle::after { display: inline-block; width: 0; height: 0; margin-left: 0.255em; vertical-align: 0.255em; content: ""; border-top: 0; border-right: 0.3em solid transparent; border-bottom: 0.3em solid; border-left: 0.3em solid transparent; } .dropup .dropdown-toggle:empty::after { margin-left: 0; } .dropright .dropdown-menu { top: 0; right: auto; left: 100%; margin-top: 0; margin-left: 0.125rem; } .dropright .dropdown-toggle::after { display: inline-block; width: 0; height: 0; margin-left: 0.255em; vertical-align: 0.255em; content: ""; border-top: 0.3em solid transparent; border-right: 0; border-bottom: 0.3em solid transparent; border-left: 0.3em solid; } .dropright .dropdown-toggle:empty::after { margin-left: 0; } .dropright .dropdown-toggle::after { vertical-align: 0; } .dropleft .dropdown-menu { top: 0; right: 100%; left: auto; margin-top: 0; margin-right: 0.125rem; } .dropleft .dropdown-toggle::after { display: inline-block; width: 0; height: 0; margin-left: 0.255em; vertical-align: 0.255em; content: ""; } .dropleft .dropdown-toggle::after { display: none; } .dropleft .dropdown-toggle::before { display: inline-block; width: 0; height: 0; margin-right: 0.255em; vertical-align: 0.255em; content: ""; border-top: 0.3em solid transparent; border-right: 0.3em solid; border-bottom: 0.3em solid transparent; } .dropleft .dropdown-toggle:empty::after { margin-left: 0; } .dropleft .dropdown-toggle::before { vertical-align: 0; } .dropdown-menu[x-placement^="top"], .dropdown-menu[x-placement^="right"], .dropdown-menu[x-placement^="bottom"], .dropdown-menu[x-placement^="left"] { right: auto; bottom: auto; } .dropdown-divider { height: 0; margin: 0.5rem 0; overflow: hidden; border-top: 1px solid #e9ecef; } .dropdown-item { display: block; width: 100%; padding: 0.25rem 1rem; clear: both; font-weight: 400; color: #212529; text-align: inherit; white-space: nowrap; background-color: transparent; border: 0; } .dropdown-item:hover, .dropdown-item:focus { color: #16181b; text-decoration: none; background-color: #f8f9fa; } .dropdown-item.active, .dropdown-item:active { color: #ffffff; text-decoration: none; background-color: #007bff; } .dropdown-item.disabled, .dropdown-item:disabled { color: #6c757d; background-color: transparent; } .dropdown-menu.show { display: block; } .dropdown-header { display: block; padding: 0.5rem 1rem; margin-bottom: 0; font-size: 0.875rem; color: #6c757d; white-space: nowrap; } .dropdown-item-text { display: block; padding: 0.25rem 1rem; color: #212529; } .btn-group, .btn-group-vertical { position: relative; display: inline-flex; vertical-align: middle; } .btn-group > .btn, .btn-group-vertical > .btn { position: relative; flex: 0 1 auto; } .btn-group > .btn:hover, .btn-group-vertical > .btn:hover { z-index: 1; } .btn-group > .btn:focus, .btn-group > .btn:active, .btn-group > .btn.active, .btn-group-vertical > .btn:focus, .btn-group-vertical > .btn:active, .btn-group-vertical > .btn.active { z-index: 1; } .btn-group .btn + .btn, .btn-group .btn + .btn-group, .btn-group .btn-group + .btn, .btn-group .btn-group + .btn-group, .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-left: -1px; } .btn-toolbar { display: flex; flex-wrap: wrap; justify-content: flex-start; } .btn-toolbar .input-group { width: auto; } .btn-group > .btn:first-child { margin-left: 0; } .btn-group > .btn:not(:last-child):not(.dropdown-toggle), .btn-group > .btn-group:not(:last-child) > .btn { border-top-right-radius: 0; border-bottom-right-radius: 0; } .btn-group > .btn:not(:first-child), .btn-group > .btn-group:not(:first-child) > .btn { border-top-left-radius: 0; border-bottom-left-radius: 0; } .dropdown-toggle-split { padding-right: 0.5625rem; padding-left: 0.5625rem; } .dropdown-toggle-split::after, .dropup .dropdown-toggle-split::after, .dropright .dropdown-toggle-split::after { margin-left: 0; } .dropleft .dropdown-toggle-split::before { margin-right: 0; } .btn-sm + .dropdown-toggle-split, .btn-group-sm > .btn + .dropdown-toggle-split { padding-right: 0.375rem; padding-left: 0.375rem; } .btn-lg + .dropdown-toggle-split, .btn-group-lg > .btn + .dropdown-toggle-split { padding-right: 0.75rem; padding-left: 0.75rem; } .btn-group.show .dropdown-toggle { box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); } .btn-group.show .dropdown-toggle.btn-link { box-shadow: none; } .btn-group-vertical { flex-direction: column; align-items: flex-start; justify-content: center; } .btn-group-vertical .btn, .btn-group-vertical .btn-group { width: 100%; } .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(:last-child):not(.dropdown-toggle), .btn-group-vertical > .btn-group:not(:last-child) > .btn { border-bottom-right-radius: 0; border-bottom-left-radius: 0; } .btn-group-vertical > .btn:not(:first-child), .btn-group-vertical > .btn-group:not(:first-child) > .btn { border-top-left-radius: 0; border-top-right-radius: 0; } .btn-group-toggle > .btn, .btn-group-toggle > .btn-group > .btn { margin-bottom: 0; } .btn-group-toggle > .btn input[type="radio"], .btn-group-toggle > .btn input[type="checkbox"], .btn-group-toggle > .btn-group > .btn input[type="radio"], .btn-group-toggle > .btn-group > .btn input[type="checkbox"] { position: absolute; clip: rect(0, 0, 0, 0); pointer-events: none; } .input-group { position: relative; display: flex; flex-wrap: wrap; align-items: stretch; width: 100%; } .input-group > .form-control, .input-group > .custom-select, .input-group > .custom-file { position: relative; flex: 1 1 auto; width: 1%; margin-bottom: 0; } .input-group > .form-control:focus, .input-group > .custom-select:focus, .input-group > .custom-file:focus { z-index: 3; } .input-group > .form-control + .form-control, .input-group > .form-control + .custom-select, .input-group > .form-control + .custom-file, .input-group > .custom-select + .form-control, .input-group > .custom-select + .custom-select, .input-group > .custom-select + .custom-file, .input-group > .custom-file + .form-control, .input-group > .custom-file + .custom-select, .input-group > .custom-file + .custom-file { margin-left: -1px; } .input-group > .form-control:not(:last-child), .input-group > .custom-select:not(:last-child) { border-top-right-radius: 0; border-bottom-right-radius: 0; } .input-group > .form-control:not(:first-child), .input-group > .custom-select:not(:first-child) { border-top-left-radius: 0; border-bottom-left-radius: 0; } .input-group > .custom-file { display: flex; align-items: center; } .input-group > .custom-file:not(:last-child) .custom-file-label, .input-group > .custom-file:not(:last-child) .custom-file-label::after { border-top-right-radius: 0; border-bottom-right-radius: 0; } .input-group > .custom-file:not(:first-child) .custom-file-label, .input-group > .custom-file:not(:first-child) .custom-file-label::after { border-top-left-radius: 0; border-bottom-left-radius: 0; } .input-group-prepend, .input-group-append { display: flex; } .input-group-prepend .btn, .input-group-append .btn { position: relative; z-index: 2; } .input-group-prepend .btn + .btn, .input-group-prepend .btn + .input-group-text, .input-group-prepend .input-group-text + .input-group-text, .input-group-prepend .input-group-text + .btn, .input-group-append .btn + .btn, .input-group-append .btn + .input-group-text, .input-group-append .input-group-text + .input-group-text, .input-group-append .input-group-text + .btn { margin-left: -1px; } .input-group-prepend { margin-right: -1px; } .input-group-append { margin-left: -1px; } .input-group-text { display: flex; align-items: center; padding: 0.375rem 0.75rem; margin-bottom: 0; font-size: 1rem; font-weight: 400; line-height: 1.5; color: #495057; text-align: center; white-space: nowrap; background-color: #e9ecef; border: 1px solid #ced4da; border-radius: 0.25rem; } .input-group-text input[type="radio"], .input-group-text input[type="checkbox"] { margin-top: 0; } .input-group > .input-group-prepend > .btn, .input-group > .input-group-prepend > .input-group-text, .input-group > .input-group-append:not(:last-child) > .btn, .input-group > .input-group-append:not(:last-child) > .input-group-text, .input-group > .input-group-append:last-child > .btn:not(:last-child):not(.dropdown-toggle), .input-group > .input-group-append:last-child > .input-group-text:not(:last-child) { border-top-right-radius: 0; border-bottom-right-radius: 0; } .input-group > .input-group-append > .btn, .input-group > .input-group-append > .input-group-text, .input-group > .input-group-prepend:not(:first-child) > .btn, .input-group > .input-group-prepend:not(:first-child) > .input-group-text, .input-group > .input-group-prepend:first-child > .btn:not(:first-child), .input-group > .input-group-prepend:first-child > .input-group-text:not(:first-child) { border-top-left-radius: 0; border-bottom-left-radius: 0; } .custom-control { position: relative; display: block; min-height: 1.5rem; padding-left: 1.5rem; } .custom-control-inline { display: inline-flex; margin-right: 1rem; } .custom-control-input { position: absolute; z-index: -1; opacity: 0; } .custom-control-input:checked ~ .custom-control-label::before { color: #ffffff; background-color: #007bff; box-shadow: none; } .custom-control-input:focus ~ .custom-control-label::before { box-shadow: 0 0 0 1px #ffffff, 0 0 0 0.2rem rgba(0, 123, 255, 0.25); } .custom-control-input:active ~ .custom-control-label::before { color: #ffffff; background-color: #b3d7ff; box-shadow: none; } .custom-control-input:disabled ~ .custom-control-label { color: #6c757d; } .custom-control-input:disabled ~ .custom-control-label::before { background-color: #e9ecef; } .custom-control-label { margin-bottom: 0; } .custom-control-label::before { position: absolute; top: 0.25rem; left: 0; display: block; width: 1rem; height: 1rem; pointer-events: none; content: ""; user-select: none; background-color: #dee2e6; box-shadow: inset 0 0.25rem 0.25rem rgba(0, 0, 0, 0.1); } .custom-control-label::after { position: absolute; top: 0.25rem; left: 0; display: block; width: 1rem; height: 1rem; content: ""; background-repeat: no-repeat; background-position: center center; background-size: 50% 50%; } .custom-checkbox .custom-control-label::before { border-radius: 0.25rem; } .custom-checkbox .custom-control-input:checked ~ .custom-control-label::before { background-color: #007bff; } .custom-checkbox .custom-control-input:checked ~ .custom-control-label::after { background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23ffffff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E"); } .custom-checkbox .custom-control-input:indeterminate ~ .custom-control-label::before { background-color: #007bff; box-shadow: none; } .custom-checkbox .custom-control-input:indeterminate ~ .custom-control-label::after { background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 4'%3E%3Cpath stroke='%23ffffff' d='M0 2h4'/%3E%3C/svg%3E"); } .custom-checkbox .custom-control-input:disabled:checked ~ .custom-control-label::before { background-color: rgba(0, 123, 255, 0.5); } .custom-checkbox .custom-control-input:disabled:indeterminate ~ .custom-control-label::before { background-color: rgba(0, 123, 255, 0.5); } .custom-radio .custom-control-label::before { border-radius: 50%; } .custom-radio .custom-control-input:checked ~ .custom-control-label::before { background-color: #007bff; } .custom-radio .custom-control-input:checked ~ .custom-control-label::after { background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23ffffff'/%3E%3C/svg%3E"); } .custom-radio .custom-control-input:disabled:checked ~ .custom-control-label::before { background-color: rgba(0, 123, 255, 0.5); } .custom-select { display: inline-block; width: 100%; height: calc(2.25rem + 2px); padding: 0.375rem 1.75rem 0.375rem 0.75rem; line-height: 1.5; color: #495057; vertical-align: middle; background: #ffffff url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3E%3Cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3E%3C/svg%3E") no-repeat right 0.75rem center; background-size: 8px 10px; border: 1px solid #ced4da; border-radius: 0.25rem; appearance: none; } .custom-select:focus { border-color: #80bdff; outline: 0; box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.075), 0 0 5px rgba(128, 189, 255, 0.5); } .custom-select:focus::-ms-value { color: #495057; background-color: #ffffff; } .custom-select[multiple], .custom-select[size]:not([size="1"]) { height: auto; padding-right: 0.75rem; background-image: none; } .custom-select:disabled { color: #6c757d; background-color: #e9ecef; } .custom-select::-ms-expand { opacity: 0; } .custom-select-sm { height: calc(1.8125rem + 2px); padding-top: 0.375rem; padding-bottom: 0.375rem; font-size: 75%; } .custom-select-lg { height: calc(2.875rem + 2px); padding-top: 0.375rem; padding-bottom: 0.375rem; font-size: 125%; } .custom-file { position: relative; display: inline-block; width: 100%; height: calc(2.25rem + 2px); margin-bottom: 0; } .custom-file-input { position: relative; z-index: 2; width: 100%; height: calc(2.25rem + 2px); margin: 0; opacity: 0; } .custom-file-input:focus ~ .custom-file-label { border-color: #80bdff; box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); } .custom-file-input:focus ~ .custom-file-label::after { border-color: #80bdff; } .custom-file-input:lang(en) ~ .custom-file-label::after { content: "Browse"; } .custom-file-label { position: absolute; top: 0; right: 0; left: 0; z-index: 1; height: calc(2.25rem + 2px); padding: 0.375rem 0.75rem; line-height: 1.5; color: #495057; background-color: #ffffff; border: 1px solid #ced4da; border-radius: 0.25rem; box-shadow: inset 0 0 0 transparent; } .custom-file-label::after { position: absolute; top: 0; right: 0; bottom: 0; z-index: 3; display: block; height: calc(calc(2.25rem + 2px) - 1px * 2); padding: 0.375rem 0.75rem; line-height: 1.5; color: #495057; content: "Browse"; background-color: #e9ecef; border-left: 1px solid #ced4da; border-radius: 0 0.25rem 0.25rem 0; } .custom-range { width: 100%; padding-left: 0; background-color: transparent; appearance: none; } .custom-range:focus { outline: none; } .custom-range::-moz-focus-outer { border: 0; } .custom-range::-webkit-slider-thumb { width: 1rem; height: 1rem; margin-top: -0.25rem; background-color: #007bff; border: 0; border-radius: 1rem; box-shadow: 0 0.1rem 0.25rem rgba(0, 0, 0, 0.1); appearance: none; } .custom-range::-webkit-slider-thumb:focus { outline: none; box-shadow: 0 0 0 1px #ffffff, 0 0 0 0.2rem rgba(0, 123, 255, 0.25); } .custom-range::-webkit-slider-thumb:active { background-color: #b3d7ff; } .custom-range::-webkit-slider-runnable-track { width: 100%; height: 0.5rem; color: transparent; cursor: pointer; background-color: #dee2e6; border-color: transparent; border-radius: 1rem; box-shadow: inset 0 0.25rem 0.25rem rgba(0, 0, 0, 0.1); } .custom-range::-moz-range-thumb { width: 1rem; height: 1rem; background-color: #007bff; border: 0; border-radius: 1rem; box-shadow: 0 0.1rem 0.25rem rgba(0, 0, 0, 0.1); appearance: none; } .custom-range::-moz-range-thumb:focus { outline: none; box-shadow: 0 0 0 1px #ffffff, 0 0 0 0.2rem rgba(0, 123, 255, 0.25); } .custom-range::-moz-range-thumb:active { background-color: #b3d7ff; } .custom-range::-moz-range-track { width: 100%; height: 0.5rem; color: transparent; cursor: pointer; background-color: #dee2e6; border-color: transparent; border-radius: 1rem; box-shadow: inset 0 0.25rem 0.25rem rgba(0, 0, 0, 0.1); } .custom-range::-ms-thumb { width: 1rem; height: 1rem; background-color: #007bff; border: 0; border-radius: 1rem; box-shadow: 0 0.1rem 0.25rem rgba(0, 0, 0, 0.1); appearance: none; } .custom-range::-ms-thumb:focus { outline: none; box-shadow: 0 0 0 1px #ffffff, 0 0 0 0.2rem rgba(0, 123, 255, 0.25); } .custom-range::-ms-thumb:active { background-color: #b3d7ff; } .custom-range::-ms-track { width: 100%; height: 0.5rem; color: transparent; cursor: pointer; background-color: transparent; border-color: transparent; border-width: 0.5rem; box-shadow: inset 0 0.25rem 0.25rem rgba(0, 0, 0, 0.1); } .custom-range::-ms-fill-lower { background-color: #dee2e6; border-radius: 1rem; } .custom-range::-ms-fill-upper { margin-right: 15px; background-color: #dee2e6; border-radius: 1rem; } .nav { display: flex; flex-wrap: wrap; padding-left: 0; margin-bottom: 0; list-style: none; } .nav-link { display: block; padding: 0.5rem 1rem; } .nav-link:hover, .nav-link:focus { text-decoration: none; } .nav-link.disabled { color: #6c757d; } .nav-tabs { border-bottom: 1px solid #dee2e6; } .nav-tabs .nav-item { margin-bottom: -1px; } .nav-tabs .nav-link { border: 1px solid transparent; border-top-left-radius: 0.25rem; border-top-right-radius: 0.25rem; } .nav-tabs .nav-link:hover, .nav-tabs .nav-link:focus { border-color: #e9ecef #e9ecef #dee2e6; } .nav-tabs .nav-link.disabled { color: #6c757d; background-color: transparent; border-color: transparent; } .nav-tabs .nav-link.active, .nav-tabs .nav-item.show .nav-link { color: #495057; background-color: #ffffff; border-color: #dee2e6 #dee2e6 #ffffff; } .nav-tabs .dropdown-menu { margin-top: -1px; border-top-left-radius: 0; border-top-right-radius: 0; } .nav-pills .nav-link { border-radius: 0.25rem; } .nav-pills .nav-link.active, .nav-pills .show > .nav-link { color: #ffffff; background-color: #007bff; } .nav-fill .nav-item { flex: 1 1 auto; text-align: center; } .nav-justified .nav-item { flex-basis: 0; flex-grow: 1; text-align: center; } .tab-content > .tab-pane { display: none; } .tab-content > .active { display: block; } .navbar { position: relative; display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; padding: 0.5rem 0.5rem; } .navbar > .container, .navbar > .container-fluid { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; } .navbar-brand { display: inline-block; padding-top: 0.3125rem; padding-bottom: 0.3125rem; margin-right: 0.5rem; font-size: 1.25rem; line-height: inherit; white-space: nowrap; } .navbar-brand:hover, .navbar-brand:focus { text-decoration: none; } .navbar-nav { display: flex; flex-direction: column; padding-left: 0; margin-bottom: 0; list-style: none; } .navbar-nav .nav-link { padding-right: 0; padding-left: 0; } .navbar-nav .dropdown-menu { position: static; float: none; } .navbar-text { display: inline-block; padding-top: 0.5rem; padding-bottom: 0.5rem; } .navbar-collapse { flex-basis: 100%; flex-grow: 1; align-items: center; } .navbar-toggler { padding: 0.25rem 0.75rem; font-size: 1.25rem; line-height: 1; background-color: transparent; border: 1px solid transparent; border-radius: 0.25rem; } .navbar-toggler:hover, .navbar-toggler:focus { text-decoration: none; } .navbar-toggler:not(:disabled):not(.disabled) { cursor: pointer; } .navbar-toggler-icon { display: inline-block; width: 1.5em; height: 1.5em; vertical-align: middle; content: ""; background: no-repeat center center; background-size: 100% 100%; } @media (max-width: 575.98px) { .navbar-expand-sm > .container, .navbar-expand-sm > .container-fluid { padding-right: 0; padding-left: 0; } } @media (min-width: 576px) { .navbar-expand-sm { flex-flow: row nowrap; justify-content: flex-start; } .navbar-expand-sm .navbar-nav { flex-direction: row; } .navbar-expand-sm .navbar-nav .dropdown-menu { position: absolute; } .navbar-expand-sm .navbar-nav .nav-link { padding-right: 1rem; padding-left: 1rem; } .navbar-expand-sm > .container, .navbar-expand-sm > .container-fluid { flex-wrap: nowrap; } .navbar-expand-sm .navbar-collapse { display: flex !important; flex-basis: auto; } .navbar-expand-sm .navbar-toggler { display: none; } } @media (max-width: 767.98px) { .navbar-expand-md > .container, .navbar-expand-md > .container-fluid { padding-right: 0; padding-left: 0; } } @media (min-width: 768px) { .navbar-expand-md { flex-flow: row nowrap; justify-content: flex-start; } .navbar-expand-md .navbar-nav { flex-direction: row; } .navbar-expand-md .navbar-nav .dropdown-menu { position: absolute; } .navbar-expand-md .navbar-nav .nav-link { padding-right: 1rem; padding-left: 1rem; } .navbar-expand-md > .container, .navbar-expand-md > .container-fluid { flex-wrap: nowrap; } .navbar-expand-md .navbar-collapse { display: flex !important; flex-basis: auto; } .navbar-expand-md .navbar-toggler { display: none; } } @media (max-width: 991.98px) { .navbar-expand-lg > .container, .navbar-expand-lg > .container-fluid { padding-right: 0; padding-left: 0; } } @media (min-width: 992px) { .navbar-expand-lg { flex-flow: row nowrap; justify-content: flex-start; } .navbar-expand-lg .navbar-nav { flex-direction: row; } .navbar-expand-lg .navbar-nav .dropdown-menu { position: absolute; } .navbar-expand-lg .navbar-nav .nav-link { padding-right: 1rem; padding-left: 1rem; } .navbar-expand-lg > .container, .navbar-expand-lg > .container-fluid { flex-wrap: nowrap; } .navbar-expand-lg .navbar-collapse { display: flex !important; flex-basis: auto; } .navbar-expand-lg .navbar-toggler { display: none; } } @media (max-width: 1199.98px) { .navbar-expand-xl > .container, .navbar-expand-xl > .container-fluid { padding-right: 0; padding-left: 0; } } @media (min-width: 1200px) { .navbar-expand-xl { flex-flow: row nowrap; justify-content: flex-start; } .navbar-expand-xl .navbar-nav { flex-direction: row; } .navbar-expand-xl .navbar-nav .dropdown-menu { position: absolute; } .navbar-expand-xl .navbar-nav .nav-link { padding-right: 1rem; padding-left: 1rem; } .navbar-expand-xl > .container, .navbar-expand-xl > .container-fluid { flex-wrap: nowrap; } .navbar-expand-xl .navbar-collapse { display: flex !important; flex-basis: auto; } .navbar-expand-xl .navbar-toggler { display: none; } } .navbar-expand { flex-flow: row nowrap; justify-content: flex-start; } .navbar-expand > .container, .navbar-expand > .container-fluid { padding-right: 0; padding-left: 0; } .navbar-expand .navbar-nav { flex-direction: row; } .navbar-expand .navbar-nav .dropdown-menu { position: absolute; } .navbar-expand .navbar-nav .nav-link { padding-right: 1rem; padding-left: 1rem; } .navbar-expand > .container, .navbar-expand > .container-fluid { flex-wrap: nowrap; } .navbar-expand .navbar-collapse { display: flex !important; flex-basis: auto; } .navbar-expand .navbar-toggler { display: none; } .navbar-light .navbar-brand { color: rgba(0, 0, 0, 0.9); } .navbar-light .navbar-brand:hover, .navbar-light .navbar-brand:focus { color: rgba(0, 0, 0, 0.9); } .navbar-light .navbar-nav .nav-link { color: rgba(0, 0, 0, 0.5); } .navbar-light .navbar-nav .nav-link:hover, .navbar-light .navbar-nav .nav-link:focus { color: rgba(0, 0, 0, 0.7); } .navbar-light .navbar-nav .nav-link.disabled { color: rgba(0, 0, 0, 0.3); } .navbar-light .navbar-nav .show > .nav-link, .navbar-light .navbar-nav .active > .nav-link, .navbar-light .navbar-nav .nav-link.show, .navbar-light .navbar-nav .nav-link.active { color: rgba(0, 0, 0, 0.9); } .navbar-light .navbar-toggler { color: rgba(0, 0, 0, 0.5); border-color: rgba(0, 0, 0, 0.1); } .navbar-light .navbar-toggler-icon { background-image: url("data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='rgba(0, 0, 0, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E"); } .navbar-light .navbar-text { color: rgba(0, 0, 0, 0.5); } .navbar-light .navbar-text a { color: rgba(0, 0, 0, 0.9); } .navbar-light .navbar-text a:hover, .navbar-light .navbar-text a:focus { color: rgba(0, 0, 0, 0.9); } .navbar-dark .navbar-brand { color: #ffffff; } .navbar-dark .navbar-brand:hover, .navbar-dark .navbar-brand:focus { color: #ffffff; } .navbar-dark .navbar-nav .nav-link { color: rgba(255, 255, 255, 0.75); } .navbar-dark .navbar-nav .nav-link:hover, .navbar-dark .navbar-nav .nav-link:focus { color: white; } .navbar-dark .navbar-nav .nav-link.disabled { color: rgba(255, 255, 255, 0.25); } .navbar-dark .navbar-nav .show > .nav-link, .navbar-dark .navbar-nav .active > .nav-link, .navbar-dark .navbar-nav .nav-link.show, .navbar-dark .navbar-nav .nav-link.active { color: #ffffff; } .navbar-dark .navbar-toggler { color: rgba(255, 255, 255, 0.75); border-color: rgba(255, 255, 255, 0.1); } .navbar-dark .navbar-toggler-icon { background-image: url("data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='rgba(255, 255, 255, 0.75)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E"); } .navbar-dark .navbar-text { color: rgba(255, 255, 255, 0.75); } .navbar-dark .navbar-text a { color: #ffffff; } .navbar-dark .navbar-text a:hover, .navbar-dark .navbar-text a:focus { color: #ffffff; } .card { position: relative; display: flex; flex-direction: column; min-width: 0; word-wrap: break-word; background-color: #ffffff; background-clip: border-box; border: 0 solid rgba(0, 0, 0, 0.125); border-radius: 0.25rem; } .card > hr { margin-right: 0; margin-left: 0; } .card > .list-group:first-child .list-group-item:first-child { border-top-left-radius: 0.25rem; border-top-right-radius: 0.25rem; } .card > .list-group:last-child .list-group-item:last-child { border-bottom-right-radius: 0.25rem; border-bottom-left-radius: 0.25rem; } .card-body { flex: 1 1 auto; padding: 1.25rem; } .card-title { margin-bottom: 0.75rem; } .card-subtitle { margin-top: -0.375rem; margin-bottom: 0; } .card-text:last-child { margin-bottom: 0; } .card-link:hover { text-decoration: none; } .card-link + .card-link { margin-left: 1.25rem; } .card-header { padding: 0.75rem 1.25rem; margin-bottom: 0; background-color: rgba(0, 0, 0, 0.03); border-bottom: 0 solid rgba(0, 0, 0, 0.125); } .card-header:first-child { border-radius: calc(0.25rem - 0) calc(0.25rem - 0) 0 0; } .card-header + .list-group .list-group-item:first-child { border-top: 0; } .card-footer { padding: 0.75rem 1.25rem; background-color: rgba(0, 0, 0, 0.03); border-top: 0 solid rgba(0, 0, 0, 0.125); } .card-footer:last-child { border-radius: 0 0 calc(0.25rem - 0) calc(0.25rem - 0); } .card-header-tabs { margin-right: -0.625rem; margin-bottom: -0.75rem; margin-left: -0.625rem; border-bottom: 0; } .card-header-pills { margin-right: -0.625rem; margin-left: -0.625rem; } .card-img-overlay { position: absolute; top: 0; right: 0; bottom: 0; left: 0; padding: 1.25rem; } .card-img { width: 100%; border-radius: calc(0.25rem - 0); } .card-img-top { width: 100%; border-top-left-radius: calc(0.25rem - 0); border-top-right-radius: calc(0.25rem - 0); } .card-img-bottom { width: 100%; border-bottom-right-radius: calc(0.25rem - 0); border-bottom-left-radius: calc(0.25rem - 0); } .card-deck { display: flex; flex-direction: column; } .card-deck .card { margin-bottom: 7.5px; } @media (min-width: 576px) { .card-deck { flex-flow: row wrap; margin-right: -7.5px; margin-left: -7.5px; } .card-deck .card { display: flex; flex: 1 0 0%; flex-direction: column; margin-right: 7.5px; margin-bottom: 0; margin-left: 7.5px; } } .card-group { display: flex; flex-direction: column; } .card-group > .card { margin-bottom: 7.5px; } @media (min-width: 576px) { .card-group { flex-flow: row wrap; } .card-group > .card { flex: 1 0 0%; margin-bottom: 0; } .card-group > .card + .card { margin-left: 0; border-left: 0; } .card-group > .card:first-child { border-top-right-radius: 0; border-bottom-right-radius: 0; } .card-group > .card:first-child .card-img-top, .card-group > .card:first-child .card-header { border-top-right-radius: 0; } .card-group > .card:first-child .card-img-bottom, .card-group > .card:first-child .card-footer { border-bottom-right-radius: 0; } .card-group > .card:last-child { border-top-left-radius: 0; border-bottom-left-radius: 0; } .card-group > .card:last-child .card-img-top, .card-group > .card:last-child .card-header { border-top-left-radius: 0; } .card-group > .card:last-child .card-img-bottom, .card-group > .card:last-child .card-footer { border-bottom-left-radius: 0; } .card-group > .card:only-child { border-radius: 0.25rem; } .card-group > .card:only-child .card-img-top, .card-group > .card:only-child .card-header { border-top-left-radius: 0.25rem; border-top-right-radius: 0.25rem; } .card-group > .card:only-child .card-img-bottom, .card-group > .card:only-child .card-footer { border-bottom-right-radius: 0.25rem; border-bottom-left-radius: 0.25rem; } .card-group > .card:not(:first-child):not(:last-child):not(:only-child) { border-radius: 0; } .card-group > .card:not(:first-child):not(:last-child):not(:only-child) .card-img-top, .card-group > .card:not(:first-child):not(:last-child):not(:only-child) .card-img-bottom, .card-group > .card:not(:first-child):not(:last-child):not(:only-child) .card-header, .card-group > .card:not(:first-child):not(:last-child):not(:only-child) .card-footer { border-radius: 0; } } .card-columns .card { margin-bottom: 0.75rem; } @media (min-width: 576px) { .card-columns { column-count: 3; column-gap: 1.25rem; orphans: 1; widows: 1; } .card-columns .card { display: inline-block; width: 100%; } } .accordion .card:not(:first-of-type):not(:last-of-type) { border-bottom: 0; border-radius: 0; } .accordion .card:not(:first-of-type) .card-header:first-child { border-radius: 0; } .accordion .card:first-of-type { border-bottom: 0; border-bottom-right-radius: 0; border-bottom-left-radius: 0; } .accordion .card:last-of-type { border-top-left-radius: 0; border-top-right-radius: 0; } .breadcrumb { display: flex; flex-wrap: wrap; padding: 0.75rem 1rem; margin-bottom: 1rem; list-style: none; background-color: #e9ecef; border-radius: 0.25rem; } .breadcrumb-item + .breadcrumb-item { padding-left: 0.5rem; } .breadcrumb-item + .breadcrumb-item::before { display: inline-block; padding-right: 0.5rem; color: #6c757d; content: "/"; } .breadcrumb-item + .breadcrumb-item:hover::before { text-decoration: underline; } .breadcrumb-item + .breadcrumb-item:hover::before { text-decoration: none; } .breadcrumb-item.active { color: #6c757d; } .pagination { display: flex; padding-left: 0; list-style: none; border-radius: 0.25rem; } .page-link { position: relative; display: block; padding: 0.5rem 0.75rem; margin-left: -1px; line-height: 1.25; color: #007bff; background-color: #ffffff; border: 1px solid #dee2e6; } .page-link:hover { z-index: 2; color: #0056b3; text-decoration: none; background-color: #e9ecef; border-color: #dee2e6; } .page-link:focus { z-index: 2; outline: 0; box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); } .page-link:not(:disabled):not(.disabled) { cursor: pointer; } .page-item:first-child .page-link { margin-left: 0; border-top-left-radius: 0.25rem; border-bottom-left-radius: 0.25rem; } .page-item:last-child .page-link { border-top-right-radius: 0.25rem; border-bottom-right-radius: 0.25rem; } .page-item.active .page-link { z-index: 1; color: #ffffff; background-color: #007bff; border-color: #007bff; } .page-item.disabled .page-link { color: #6c757d; pointer-events: none; cursor: auto; background-color: #ffffff; border-color: #dee2e6; } .pagination-lg .page-link { padding: 0.75rem 1.5rem; font-size: 1.25rem; line-height: 1.5; } .pagination-lg .page-item:first-child .page-link { border-top-left-radius: 0.3rem; border-bottom-left-radius: 0.3rem; } .pagination-lg .page-item:last-child .page-link { border-top-right-radius: 0.3rem; border-bottom-right-radius: 0.3rem; } .pagination-sm .page-link { padding: 0.25rem 0.5rem; font-size: 0.875rem; line-height: 1.5; } .pagination-sm .page-item:first-child .page-link { border-top-left-radius: 0.2rem; border-bottom-left-radius: 0.2rem; } .pagination-sm .page-item:last-child .page-link { border-top-right-radius: 0.2rem; border-bottom-right-radius: 0.2rem; } .badge { display: inline-block; padding: 0.25em 0.4em; font-size: 75%; font-weight: 700; line-height: 1; text-align: center; white-space: nowrap; vertical-align: baseline; border-radius: 0.25rem; } .badge:empty { display: none; } .btn .badge { position: relative; top: -1px; } .badge-pill { padding-right: 0.6em; padding-left: 0.6em; border-radius: 10rem; } .badge-primary { color: #ffffff; background-color: #007bff; } .badge-primary[href]:hover, .badge-primary[href]:focus { color: #ffffff; text-decoration: none; background-color: #0062cc; } .badge-secondary { color: #ffffff; background-color: #6c757d; } .badge-secondary[href]:hover, .badge-secondary[href]:focus { color: #ffffff; text-decoration: none; background-color: #545b62; } .badge-success { color: #ffffff; background-color: #28a745; } .badge-success[href]:hover, .badge-success[href]:focus { color: #ffffff; text-decoration: none; background-color: #1e7e34; } .badge-info { color: #ffffff; background-color: #17a2b8; } .badge-info[href]:hover, .badge-info[href]:focus { color: #ffffff; text-decoration: none; background-color: #117a8b; } .badge-warning { color: #1F2D3D; background-color: #ffc107; } .badge-warning[href]:hover, .badge-warning[href]:focus { color: #1F2D3D; text-decoration: none; background-color: #d39e00; } .badge-danger { color: #ffffff; background-color: #dc3545; } .badge-danger[href]:hover, .badge-danger[href]:focus { color: #ffffff; text-decoration: none; background-color: #bd2130; } .badge-light { color: #1F2D3D; background-color: #f8f9fa; } .badge-light[href]:hover, .badge-light[href]:focus { color: #1F2D3D; text-decoration: none; background-color: #dae0e5; } .badge-dark { color: #ffffff; background-color: #343a40; } .badge-dark[href]:hover, .badge-dark[href]:focus { color: #ffffff; text-decoration: none; background-color: #1d2124; } .jumbotron { padding: 2rem 1rem; margin-bottom: 2rem; background-color: #e9ecef; border-radius: 0.3rem; } @media (min-width: 576px) { .jumbotron { padding: 4rem 2rem; } } .jumbotron-fluid { padding-right: 0; padding-left: 0; border-radius: 0; } .alert { position: relative; padding: 0.75rem 1.25rem; margin-bottom: 1rem; border: 1px solid transparent; border-radius: 0.25rem; } .alert-heading { color: inherit; } .alert-link { font-weight: 700; } .alert-dismissible { padding-right: 4rem; } .alert-dismissible .close, .alert-dismissible .mailbox-attachment-close { position: absolute; top: 0; right: 0; padding: 0.75rem 1.25rem; color: inherit; } .alert-primary { color: #004085; background-color: #cce5ff; border-color: #b8daff; } .alert-primary hr { border-top-color: #9fcdff; } .alert-primary .alert-link { color: #002752; } .alert-secondary { color: #383d41; background-color: #e2e3e5; border-color: #d6d8db; } .alert-secondary hr { border-top-color: #c8cbcf; } .alert-secondary .alert-link { color: #202326; } .alert-success { color: #155724; background-color: #d4edda; border-color: #c3e6cb; } .alert-success hr { border-top-color: #b1dfbb; } .alert-success .alert-link { color: #0b2e13; } .alert-info { color: #0c5460; background-color: #d1ecf1; border-color: #bee5eb; } .alert-info hr { border-top-color: #abdde5; } .alert-info .alert-link { color: #062c33; } .alert-warning { color: #856404; background-color: #fff3cd; border-color: #ffeeba; } .alert-warning hr { border-top-color: #ffe8a1; } .alert-warning .alert-link { color: #533f03; } .alert-danger { color: #721c24; background-color: #f8d7da; border-color: #f5c6cb; } .alert-danger hr { border-top-color: #f1b0b7; } .alert-danger .alert-link { color: #491217; } .alert-light { color: #818182; background-color: #fefefe; border-color: #fdfdfe; } .alert-light hr { border-top-color: #ececf6; } .alert-light .alert-link { color: #686868; } .alert-dark { color: #1b1e21; background-color: #d6d8d9; border-color: #c6c8ca; } .alert-dark hr { border-top-color: #b9bbbe; } .alert-dark .alert-link { color: #040505; } @keyframes progress-bar-stripes { from { background-position: 1rem 0; } to { background-position: 0 0; } } .progress { display: flex; height: 1rem; overflow: hidden; font-size: 0.75rem; background-color: #e9ecef; border-radius: 0.25rem; box-shadow: inset 0 0.1rem 0.1rem rgba(0, 0, 0, 0.1); } .progress-bar { display: flex; flex-direction: column; justify-content: center; color: #ffffff; text-align: center; white-space: nowrap; background-color: #007bff; transition: width 0.6s ease; } @media screen and (prefers-reduced-motion: reduce) { .progress-bar { transition: none; } } .progress-bar-striped { 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); background-size: 1rem 1rem; } .progress-bar-animated { animation: progress-bar-stripes 1s linear infinite; } .media { display: flex; align-items: flex-start; } .media-body { flex: 1; } .list-group { display: flex; flex-direction: column; padding-left: 0; margin-bottom: 0; } .list-group-item-action { width: 100%; color: #495057; text-align: inherit; } .list-group-item-action:hover, .list-group-item-action:focus { color: #495057; text-decoration: none; background-color: #f8f9fa; } .list-group-item-action:active { color: #212529; background-color: #e9ecef; } .list-group-item { position: relative; display: block; padding: 0.75rem 1.25rem; margin-bottom: -1px; background-color: #ffffff; border: 1px solid rgba(0, 0, 0, 0.125); } .list-group-item:first-child { border-top-left-radius: 0.25rem; border-top-right-radius: 0.25rem; } .list-group-item:last-child { margin-bottom: 0; border-bottom-right-radius: 0.25rem; border-bottom-left-radius: 0.25rem; } .list-group-item:hover, .list-group-item:focus { z-index: 1; text-decoration: none; } .list-group-item.disabled, .list-group-item:disabled { color: #6c757d; background-color: #ffffff; } .list-group-item.active { z-index: 2; color: #ffffff; background-color: #007bff; border-color: #007bff; } .list-group-flush .list-group-item { border-right: 0; border-left: 0; border-radius: 0; } .list-group-flush:first-child .list-group-item:first-child { border-top: 0; } .list-group-flush:last-child .list-group-item:last-child { border-bottom: 0; } .list-group-item-primary { color: #004085; background-color: #b8daff; } .list-group-item-primary.list-group-item-action:hover, .list-group-item-primary.list-group-item-action:focus { color: #004085; background-color: #9fcdff; } .list-group-item-primary.list-group-item-action.active { color: #ffffff; background-color: #004085; border-color: #004085; } .list-group-item-secondary { color: #383d41; background-color: #d6d8db; } .list-group-item-secondary.list-group-item-action:hover, .list-group-item-secondary.list-group-item-action:focus { color: #383d41; background-color: #c8cbcf; } .list-group-item-secondary.list-group-item-action.active { color: #ffffff; background-color: #383d41; border-color: #383d41; } .list-group-item-success { color: #155724; background-color: #c3e6cb; } .list-group-item-success.list-group-item-action:hover, .list-group-item-success.list-group-item-action:focus { color: #155724; background-color: #b1dfbb; } .list-group-item-success.list-group-item-action.active { color: #ffffff; background-color: #155724; border-color: #155724; } .list-group-item-info { color: #0c5460; background-color: #bee5eb; } .list-group-item-info.list-group-item-action:hover, .list-group-item-info.list-group-item-action:focus { color: #0c5460; background-color: #abdde5; } .list-group-item-info.list-group-item-action.active { color: #ffffff; background-color: #0c5460; border-color: #0c5460; } .list-group-item-warning { color: #856404; background-color: #ffeeba; } .list-group-item-warning.list-group-item-action:hover, .list-group-item-warning.list-group-item-action:focus { color: #856404; background-color: #ffe8a1; } .list-group-item-warning.list-group-item-action.active { color: #ffffff; background-color: #856404; border-color: #856404; } .list-group-item-danger { color: #721c24; background-color: #f5c6cb; } .list-group-item-danger.list-group-item-action:hover, .list-group-item-danger.list-group-item-action:focus { color: #721c24; background-color: #f1b0b7; } .list-group-item-danger.list-group-item-action.active { color: #ffffff; background-color: #721c24; border-color: #721c24; } .list-group-item-light { color: #818182; background-color: #fdfdfe; } .list-group-item-light.list-group-item-action:hover, .list-group-item-light.list-group-item-action:focus { color: #818182; background-color: #ececf6; } .list-group-item-light.list-group-item-action.active { color: #ffffff; background-color: #818182; border-color: #818182; } .list-group-item-dark { color: #1b1e21; background-color: #c6c8ca; } .list-group-item-dark.list-group-item-action:hover, .list-group-item-dark.list-group-item-action:focus { color: #1b1e21; background-color: #b9bbbe; } .list-group-item-dark.list-group-item-action.active { color: #ffffff; background-color: #1b1e21; border-color: #1b1e21; } .close, .mailbox-attachment-close { float: right; font-size: 1.5rem; font-weight: 700; line-height: 1; color: #000; text-shadow: 0 1px 0 #ffffff; opacity: .5; } .close:hover, .mailbox-attachment-close:hover, .close:focus, .mailbox-attachment-close:focus { color: #000; text-decoration: none; opacity: .75; } .close:not(:disabled):not(.disabled), .mailbox-attachment-close:not(:disabled):not(.disabled) { cursor: pointer; } button.close, button.mailbox-attachment-close { padding: 0; background-color: transparent; border: 0; -webkit-appearance: none; } .modal-open { overflow: hidden; } .modal { position: fixed; top: 0; right: 0; bottom: 0; left: 0; z-index: 1050; display: none; overflow: hidden; outline: 0; } .modal-open .modal { overflow-x: hidden; overflow-y: auto; } .modal-dialog { position: relative; width: auto; margin: 0.5rem; pointer-events: none; } .modal.fade .modal-dialog { transition: transform 0.3s ease-out; transform: translate(0, -25%); } @media screen and (prefers-reduced-motion: reduce) { .modal.fade .modal-dialog { transition: none; } } .modal.show .modal-dialog { transform: translate(0, 0); } .modal-dialog-centered { display: flex; align-items: center; min-height: calc(100% - (0.5rem * 2)); } .modal-content { position: relative; display: flex; flex-direction: column; width: 100%; pointer-events: auto; background-color: #ffffff; background-clip: padding-box; border: 1px solid rgba(0, 0, 0, 0.2); border-radius: 0.3rem; box-shadow: 0 0.25rem 0.5rem rgba(0, 0, 0, 0.5); outline: 0; } .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.show { opacity: 0.5; } .modal-header { display: flex; align-items: flex-start; justify-content: space-between; padding: 1rem; border-bottom: 1px solid #e9ecef; border-top-left-radius: 0.3rem; border-top-right-radius: 0.3rem; } .modal-header .close, .modal-header .mailbox-attachment-close { padding: 1rem; margin: -1rem -1rem -1rem auto; } .modal-title { margin-bottom: 0; line-height: 1.5; } .modal-body { position: relative; flex: 1 1 auto; padding: 1rem; } .modal-footer { display: flex; align-items: center; justify-content: flex-end; padding: 1rem; border-top: 1px solid #e9ecef; } .modal-footer > :not(:first-child) { margin-left: .25rem; } .modal-footer > :not(:last-child) { margin-right: .25rem; } .modal-scrollbar-measure { position: absolute; top: -9999px; width: 50px; height: 50px; overflow: scroll; } @media (min-width: 576px) { .modal-dialog { max-width: 500px; margin: 1.75rem auto; } .modal-dialog-centered { min-height: calc(100% - (1.75rem * 2)); } .modal-content { box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.5); } .modal-sm { max-width: 300px; } } @media (min-width: 992px) { .modal-lg { max-width: 800px; } } .tooltip { position: absolute; z-index: 1070; display: block; margin: 0; font-family: "Source Sans Pro", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-style: normal; font-weight: 400; line-height: 1.5; text-align: left; text-align: start; text-decoration: none; text-shadow: none; text-transform: none; letter-spacing: normal; word-break: normal; word-spacing: normal; white-space: normal; line-break: auto; font-size: 0.875rem; word-wrap: break-word; opacity: 0; } .tooltip.show { opacity: 0.9; } .tooltip .arrow { position: absolute; display: block; width: 0.8rem; height: 0.4rem; } .tooltip .arrow::before { position: absolute; content: ""; border-color: transparent; border-style: solid; } .bs-tooltip-top, .bs-tooltip-auto[x-placement^="top"] { padding: 0.4rem 0; } .bs-tooltip-top .arrow, .bs-tooltip-auto[x-placement^="top"] .arrow { bottom: 0; } .bs-tooltip-top .arrow::before, .bs-tooltip-auto[x-placement^="top"] .arrow::before { top: 0; border-width: 0.4rem 0.4rem 0; border-top-color: #000; } .bs-tooltip-right, .bs-tooltip-auto[x-placement^="right"] { padding: 0 0.4rem; } .bs-tooltip-right .arrow, .bs-tooltip-auto[x-placement^="right"] .arrow { left: 0; width: 0.4rem; height: 0.8rem; } .bs-tooltip-right .arrow::before, .bs-tooltip-auto[x-placement^="right"] .arrow::before { right: 0; border-width: 0.4rem 0.4rem 0.4rem 0; border-right-color: #000; } .bs-tooltip-bottom, .bs-tooltip-auto[x-placement^="bottom"] { padding: 0.4rem 0; } .bs-tooltip-bottom .arrow, .bs-tooltip-auto[x-placement^="bottom"] .arrow { top: 0; } .bs-tooltip-bottom .arrow::before, .bs-tooltip-auto[x-placement^="bottom"] .arrow::before { bottom: 0; border-width: 0 0.4rem 0.4rem; border-bottom-color: #000; } .bs-tooltip-left, .bs-tooltip-auto[x-placement^="left"] { padding: 0 0.4rem; } .bs-tooltip-left .arrow, .bs-tooltip-auto[x-placement^="left"] .arrow { right: 0; width: 0.4rem; height: 0.8rem; } .bs-tooltip-left .arrow::before, .bs-tooltip-auto[x-placement^="left"] .arrow::before { left: 0; border-width: 0.4rem 0 0.4rem 0.4rem; border-left-color: #000; } .tooltip-inner { max-width: 200px; padding: 0.25rem 0.5rem; color: #ffffff; text-align: center; background-color: #000; border-radius: 0.25rem; } .popover { position: absolute; top: 0; left: 0; z-index: 1060; display: block; max-width: 276px; font-family: "Source Sans Pro", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-style: normal; font-weight: 400; line-height: 1.5; text-align: left; text-align: start; text-decoration: none; text-shadow: none; text-transform: none; letter-spacing: normal; word-break: normal; word-spacing: normal; white-space: normal; line-break: auto; font-size: 0.875rem; word-wrap: break-word; background-color: #ffffff; background-clip: padding-box; border: 1px solid rgba(0, 0, 0, 0.2); border-radius: 0.3rem; box-shadow: 0 0.25rem 0.5rem rgba(0, 0, 0, 0.2); } .popover .arrow { position: absolute; display: block; width: 1rem; height: 0.5rem; margin: 0 0.3rem; } .popover .arrow::before, .popover .arrow::after { position: absolute; display: block; content: ""; border-color: transparent; border-style: solid; } .bs-popover-top, .bs-popover-auto[x-placement^="top"] { margin-bottom: 0.5rem; } .bs-popover-top .arrow, .bs-popover-auto[x-placement^="top"] .arrow { bottom: calc((0.5rem + 1px) * -1); } .bs-popover-top .arrow::before, .bs-popover-auto[x-placement^="top"] .arrow::before, .bs-popover-top .arrow::after, .bs-popover-auto[x-placement^="top"] .arrow::after { border-width: 0.5rem 0.5rem 0; } .bs-popover-top .arrow::before, .bs-popover-auto[x-placement^="top"] .arrow::before { bottom: 0; border-top-color: rgba(0, 0, 0, 0.25); } .bs-popover-top .arrow::after, .bs-popover-auto[x-placement^="top"] .arrow::after { bottom: 1px; border-top-color: #ffffff; } .bs-popover-right, .bs-popover-auto[x-placement^="right"] { margin-left: 0.5rem; } .bs-popover-right .arrow, .bs-popover-auto[x-placement^="right"] .arrow { left: calc((0.5rem + 1px) * -1); width: 0.5rem; height: 1rem; margin: 0.3rem 0; } .bs-popover-right .arrow::before, .bs-popover-auto[x-placement^="right"] .arrow::before, .bs-popover-right .arrow::after, .bs-popover-auto[x-placement^="right"] .arrow::after { border-width: 0.5rem 0.5rem 0.5rem 0; } .bs-popover-right .arrow::before, .bs-popover-auto[x-placement^="right"] .arrow::before { left: 0; border-right-color: rgba(0, 0, 0, 0.25); } .bs-popover-right .arrow::after, .bs-popover-auto[x-placement^="right"] .arrow::after { left: 1px; border-right-color: #ffffff; } .bs-popover-bottom, .bs-popover-auto[x-placement^="bottom"] { margin-top: 0.5rem; } .bs-popover-bottom .arrow, .bs-popover-auto[x-placement^="bottom"] .arrow { top: calc((0.5rem + 1px) * -1); } .bs-popover-bottom .arrow::before, .bs-popover-auto[x-placement^="bottom"] .arrow::before, .bs-popover-bottom .arrow::after, .bs-popover-auto[x-placement^="bottom"] .arrow::after { border-width: 0 0.5rem 0.5rem 0.5rem; } .bs-popover-bottom .arrow::before, .bs-popover-auto[x-placement^="bottom"] .arrow::before { top: 0; border-bottom-color: rgba(0, 0, 0, 0.25); } .bs-popover-bottom .arrow::after, .bs-popover-auto[x-placement^="bottom"] .arrow::after { top: 1px; border-bottom-color: #ffffff; } .bs-popover-bottom .popover-header::before, .bs-popover-auto[x-placement^="bottom"] .popover-header::before { position: absolute; top: 0; left: 50%; display: block; width: 1rem; margin-left: -0.5rem; content: ""; border-bottom: 1px solid #f7f7f7; } .bs-popover-left, .bs-popover-auto[x-placement^="left"] { margin-right: 0.5rem; } .bs-popover-left .arrow, .bs-popover-auto[x-placement^="left"] .arrow { right: calc((0.5rem + 1px) * -1); width: 0.5rem; height: 1rem; margin: 0.3rem 0; } .bs-popover-left .arrow::before, .bs-popover-auto[x-placement^="left"] .arrow::before, .bs-popover-left .arrow::after, .bs-popover-auto[x-placement^="left"] .arrow::after { border-width: 0.5rem 0 0.5rem 0.5rem; } .bs-popover-left .arrow::before, .bs-popover-auto[x-placement^="left"] .arrow::before { right: 0; border-left-color: rgba(0, 0, 0, 0.25); } .bs-popover-left .arrow::after, .bs-popover-auto[x-placement^="left"] .arrow::after { right: 1px; border-left-color: #ffffff; } .popover-header { padding: 0.5rem 0.75rem; margin-bottom: 0; font-size: 1rem; color: inherit; background-color: #f7f7f7; border-bottom: 1px solid #ebebeb; border-top-left-radius: calc(0.3rem - 1px); border-top-right-radius: calc(0.3rem - 1px); } .popover-header:empty { display: none; } .popover-body { padding: 0.5rem 0.75rem; color: #212529; } .carousel { position: relative; } .carousel-inner { position: relative; width: 100%; overflow: hidden; } .carousel-item { position: relative; display: none; align-items: center; width: 100%; transition: transform 0.6s ease; backface-visibility: hidden; perspective: 1000px; } @media screen and (prefers-reduced-motion: reduce) { .carousel-item { transition: none; } } .carousel-item.active, .carousel-item-next, .carousel-item-prev { display: block; } .carousel-item-next, .carousel-item-prev { position: absolute; top: 0; } .carousel-item-next.carousel-item-left, .carousel-item-prev.carousel-item-right { transform: translateX(0); } @supports (transform-style: preserve-3d) { .carousel-item-next.carousel-item-left, .carousel-item-prev.carousel-item-right { transform: translate3d(0, 0, 0); } } .carousel-item-next, .active.carousel-item-right { transform: translateX(100%); } @supports (transform-style: preserve-3d) { .carousel-item-next, .active.carousel-item-right { transform: translate3d(100%, 0, 0); } } .carousel-item-prev, .active.carousel-item-left { transform: translateX(-100%); } @supports (transform-style: preserve-3d) { .carousel-item-prev, .active.carousel-item-left { transform: translate3d(-100%, 0, 0); } } .carousel-fade .carousel-item { opacity: 0; transition-duration: .6s; transition-property: opacity; } .carousel-fade .carousel-item.active, .carousel-fade .carousel-item-next.carousel-item-left, .carousel-fade .carousel-item-prev.carousel-item-right { opacity: 1; } .carousel-fade .active.carousel-item-left, .carousel-fade .active.carousel-item-right { opacity: 0; } .carousel-fade .carousel-item-next, .carousel-fade .carousel-item-prev, .carousel-fade .carousel-item.active, .carousel-fade .active.carousel-item-left, .carousel-fade .active.carousel-item-prev { transform: translateX(0); } @supports (transform-style: preserve-3d) { .carousel-fade .carousel-item-next, .carousel-fade .carousel-item-prev, .carousel-fade .carousel-item.active, .carousel-fade .active.carousel-item-left, .carousel-fade .active.carousel-item-prev { transform: translate3d(0, 0, 0); } } .carousel-control-prev, .carousel-control-next { position: absolute; top: 0; bottom: 0; display: flex; align-items: center; justify-content: center; width: 15%; color: #ffffff; text-align: center; opacity: 0.5; } .carousel-control-prev:hover, .carousel-control-prev:focus, .carousel-control-next:hover, .carousel-control-next:focus { color: #ffffff; text-decoration: none; outline: 0; opacity: .9; } .carousel-control-prev { left: 0; } .carousel-control-next { right: 0; } .carousel-control-prev-icon, .carousel-control-next-icon { display: inline-block; width: 20px; height: 20px; background: transparent no-repeat center center; background-size: 100% 100%; } .carousel-control-prev-icon { background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23ffffff' viewBox='0 0 8 8'%3E%3Cpath d='M5.25 0l-4 4 4 4 1.5-1.5-2.5-2.5 2.5-2.5-1.5-1.5z'/%3E%3C/svg%3E"); } .carousel-control-next-icon { background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23ffffff' viewBox='0 0 8 8'%3E%3Cpath d='M2.75 0l-1.5 1.5 2.5 2.5-2.5 2.5 1.5 1.5 4-4-4-4z'/%3E%3C/svg%3E"); } .carousel-indicators { position: absolute; right: 0; bottom: 10px; left: 0; z-index: 15; display: flex; justify-content: center; padding-left: 0; margin-right: 15%; margin-left: 15%; list-style: none; } .carousel-indicators li { position: relative; flex: 0 1 auto; width: 30px; height: 3px; margin-right: 3px; margin-left: 3px; text-indent: -999px; background-color: rgba(255, 255, 255, 0.5); } .carousel-indicators li::before { position: absolute; top: -10px; left: 0; display: inline-block; width: 100%; height: 10px; content: ""; } .carousel-indicators li::after { position: absolute; bottom: -10px; left: 0; display: inline-block; width: 100%; height: 10px; content: ""; } .carousel-indicators .active { background-color: #ffffff; } .carousel-caption { position: absolute; right: 15%; bottom: 20px; left: 15%; z-index: 10; padding-top: 20px; padding-bottom: 20px; color: #ffffff; text-align: center; } .align-baseline { vertical-align: baseline !important; } .align-top { vertical-align: top !important; } .align-middle { vertical-align: middle !important; } .align-bottom { vertical-align: bottom !important; } .align-text-bottom { vertical-align: text-bottom !important; } .align-text-top { vertical-align: text-top !important; } .bg-primary, .label-primary { background-color: #007bff !important; } a.bg-primary:hover, a.label-primary:hover, a.bg-primary:focus, a.label-primary:focus, button.bg-primary:hover, button.label-primary:hover, button.bg-primary:focus, button.label-primary:focus { background-color: #0062cc !important; } .bg-secondary { background-color: #6c757d !important; } a.bg-secondary:hover, a.bg-secondary:focus, button.bg-secondary:hover, button.bg-secondary:focus { background-color: #545b62 !important; } .bg-success, .alert-success, .label-success { background-color: #28a745 !important; } a.bg-success:hover, a.alert-success:hover, a.label-success:hover, a.bg-success:focus, a.alert-success:focus, a.label-success:focus, button.bg-success:hover, button.alert-success:hover, button.label-success:hover, button.bg-success:focus, button.alert-success:focus, button.label-success:focus { background-color: #1e7e34 !important; } .bg-info, .alert-info, .label-info { background-color: #17a2b8 !important; } a.bg-info:hover, a.alert-info:hover, a.label-info:hover, a.bg-info:focus, a.alert-info:focus, a.label-info:focus, button.bg-info:hover, button.alert-info:hover, button.label-info:hover, button.bg-info:focus, button.alert-info:focus, button.label-info:focus { background-color: #117a8b !important; } .bg-warning, .alert-warning, .label-warning { background-color: #ffc107 !important; } a.bg-warning:hover, a.alert-warning:hover, a.label-warning:hover, a.bg-warning:focus, a.alert-warning:focus, a.label-warning:focus, button.bg-warning:hover, button.alert-warning:hover, button.label-warning:hover, button.bg-warning:focus, button.alert-warning:focus, button.label-warning:focus { background-color: #d39e00 !important; } .bg-danger, .alert-danger, .alert-error, .label-danger { background-color: #dc3545 !important; } a.bg-danger:hover, a.alert-danger:hover, a.alert-error:hover, a.label-danger:hover, a.bg-danger:focus, a.alert-danger:focus, a.alert-error:focus, a.label-danger:focus, button.bg-danger:hover, button.alert-danger:hover, button.alert-error:hover, button.label-danger:hover, button.bg-danger:focus, button.alert-danger:focus, button.alert-error:focus, button.label-danger:focus { background-color: #bd2130 !important; } .bg-light { background-color: #f8f9fa !important; } a.bg-light:hover, a.bg-light:focus, button.bg-light:hover, button.bg-light:focus { background-color: #dae0e5 !important; } .bg-dark { background-color: #343a40 !important; } a.bg-dark:hover, a.bg-dark:focus, button.bg-dark:hover, button.bg-dark:focus { background-color: #1d2124 !important; } .bg-white { background-color: #ffffff !important; } .bg-transparent { background-color: transparent !important; } .border { border: 1px solid #dee2e6 !important; } .border-top { border-top: 1px solid #dee2e6 !important; } .border-right { border-right: 1px solid #dee2e6 !important; } .border-bottom { border-bottom: 1px solid #dee2e6 !important; } .border-left { border-left: 1px solid #dee2e6 !important; } .border-0 { border: 0 !important; } .border-top-0 { border-top: 0 !important; } .border-right-0 { border-right: 0 !important; } .border-bottom-0 { border-bottom: 0 !important; } .border-left-0 { border-left: 0 !important; } .border-primary { border-color: #007bff !important; } .border-secondary { border-color: #6c757d !important; } .border-success { border-color: #28a745 !important; } .border-info { border-color: #17a2b8 !important; } .border-warning { border-color: #ffc107 !important; } .border-danger { border-color: #dc3545 !important; } .border-light { border-color: #f8f9fa !important; } .border-dark { border-color: #343a40 !important; } .border-white { border-color: #ffffff !important; } .rounded { border-radius: 0.25rem !important; } .rounded-top { border-top-left-radius: 0.25rem !important; border-top-right-radius: 0.25rem !important; } .rounded-right { border-top-right-radius: 0.25rem !important; border-bottom-right-radius: 0.25rem !important; } .rounded-bottom { border-bottom-right-radius: 0.25rem !important; border-bottom-left-radius: 0.25rem !important; } .rounded-left { border-top-left-radius: 0.25rem !important; border-bottom-left-radius: 0.25rem !important; } .rounded-circle { border-radius: 50% !important; } .rounded-0 { border-radius: 0 !important; } .clearfix::after { display: block; clear: both; content: ""; } .d-none { display: none !important; } .d-inline { display: inline !important; } .d-inline-block { display: inline-block !important; } .d-block { display: block !important; } .d-table { display: table !important; } .d-table-row { display: table-row !important; } .d-table-cell { display: table-cell !important; } .d-flex, .info-box, .info-box-icon { display: flex !important; } .d-inline-flex { display: inline-flex !important; } @media (min-width: 576px) { .d-sm-none { display: none !important; } .d-sm-inline { display: inline !important; } .d-sm-inline-block { display: inline-block !important; } .d-sm-block { display: block !important; } .d-sm-table { display: table !important; } .d-sm-table-row { display: table-row !important; } .d-sm-table-cell { display: table-cell !important; } .d-sm-flex { display: flex !important; } .d-sm-inline-flex { display: inline-flex !important; } } @media (min-width: 768px) { .d-md-none { display: none !important; } .d-md-inline { display: inline !important; } .d-md-inline-block { display: inline-block !important; } .d-md-block { display: block !important; } .d-md-table { display: table !important; } .d-md-table-row { display: table-row !important; } .d-md-table-cell { display: table-cell !important; } .d-md-flex { display: flex !important; } .d-md-inline-flex { display: inline-flex !important; } } @media (min-width: 992px) { .d-lg-none { display: none !important; } .d-lg-inline { display: inline !important; } .d-lg-inline-block { display: inline-block !important; } .d-lg-block { display: block !important; } .d-lg-table { display: table !important; } .d-lg-table-row { display: table-row !important; } .d-lg-table-cell { display: table-cell !important; } .d-lg-flex { display: flex !important; } .d-lg-inline-flex { display: inline-flex !important; } } @media (min-width: 1200px) { .d-xl-none { display: none !important; } .d-xl-inline { display: inline !important; } .d-xl-inline-block { display: inline-block !important; } .d-xl-block { display: block !important; } .d-xl-table { display: table !important; } .d-xl-table-row { display: table-row !important; } .d-xl-table-cell { display: table-cell !important; } .d-xl-flex { display: flex !important; } .d-xl-inline-flex { display: inline-flex !important; } } @media print { .d-print-none { display: none !important; } .d-print-inline { display: inline !important; } .d-print-inline-block { display: inline-block !important; } .d-print-block { display: block !important; } .d-print-table { display: table !important; } .d-print-table-row { display: table-row !important; } .d-print-table-cell { display: table-cell !important; } .d-print-flex { display: flex !important; } .d-print-inline-flex { display: inline-flex !important; } } .embed-responsive { position: relative; display: block; width: 100%; padding: 0; overflow: hidden; } .embed-responsive::before { display: block; content: ""; } .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-21by9::before { padding-top: 42.857143%; } .embed-responsive-16by9::before { padding-top: 56.25%; } .embed-responsive-4by3::before { padding-top: 75%; } .embed-responsive-1by1::before { padding-top: 100%; } .flex-row { flex-direction: row !important; } .flex-column { flex-direction: column !important; } .flex-row-reverse { flex-direction: row-reverse !important; } .flex-column-reverse { flex-direction: column-reverse !important; } .flex-wrap { flex-wrap: wrap !important; } .flex-nowrap { flex-wrap: nowrap !important; } .flex-wrap-reverse { flex-wrap: wrap-reverse !important; } .flex-fill { flex: 1 1 auto !important; } .flex-grow-0 { flex-grow: 0 !important; } .flex-grow-1 { flex-grow: 1 !important; } .flex-shrink-0 { flex-shrink: 0 !important; } .flex-shrink-1 { flex-shrink: 1 !important; } .justify-content-start { justify-content: flex-start !important; } .justify-content-end { justify-content: flex-end !important; } .justify-content-center, .info-box-icon { justify-content: center !important; } .justify-content-between { justify-content: space-between !important; } .justify-content-around { justify-content: space-around !important; } .align-items-start { align-items: flex-start !important; } .align-items-end { align-items: flex-end !important; } .align-items-center, .info-box-icon { align-items: center !important; } .align-items-baseline { align-items: baseline !important; } .align-items-stretch { align-items: stretch !important; } .align-content-start { align-content: flex-start !important; } .align-content-end { align-content: flex-end !important; } .align-content-center { align-content: center !important; } .align-content-between { align-content: space-between !important; } .align-content-around { align-content: space-around !important; } .align-content-stretch { align-content: stretch !important; } .align-self-auto { align-self: auto !important; } .align-self-start { align-self: flex-start !important; } .align-self-end { align-self: flex-end !important; } .align-self-center { align-self: center !important; } .align-self-baseline { align-self: baseline !important; } .align-self-stretch { align-self: stretch !important; } @media (min-width: 576px) { .flex-sm-row { flex-direction: row !important; } .flex-sm-column { flex-direction: column !important; } .flex-sm-row-reverse { flex-direction: row-reverse !important; } .flex-sm-column-reverse { flex-direction: column-reverse !important; } .flex-sm-wrap { flex-wrap: wrap !important; } .flex-sm-nowrap { flex-wrap: nowrap !important; } .flex-sm-wrap-reverse { flex-wrap: wrap-reverse !important; } .flex-sm-fill { flex: 1 1 auto !important; } .flex-sm-grow-0 { flex-grow: 0 !important; } .flex-sm-grow-1 { flex-grow: 1 !important; } .flex-sm-shrink-0 { flex-shrink: 0 !important; } .flex-sm-shrink-1 { flex-shrink: 1 !important; } .justify-content-sm-start { justify-content: flex-start !important; } .justify-content-sm-end { justify-content: flex-end !important; } .justify-content-sm-center { justify-content: center !important; } .justify-content-sm-between { justify-content: space-between !important; } .justify-content-sm-around { justify-content: space-around !important; } .align-items-sm-start { align-items: flex-start !important; } .align-items-sm-end { align-items: flex-end !important; } .align-items-sm-center { align-items: center !important; } .align-items-sm-baseline { align-items: baseline !important; } .align-items-sm-stretch { align-items: stretch !important; } .align-content-sm-start { align-content: flex-start !important; } .align-content-sm-end { align-content: flex-end !important; } .align-content-sm-center { align-content: center !important; } .align-content-sm-between { align-content: space-between !important; } .align-content-sm-around { align-content: space-around !important; } .align-content-sm-stretch { align-content: stretch !important; } .align-self-sm-auto { align-self: auto !important; } .align-self-sm-start { align-self: flex-start !important; } .align-self-sm-end { align-self: flex-end !important; } .align-self-sm-center { align-self: center !important; } .align-self-sm-baseline { align-self: baseline !important; } .align-self-sm-stretch { align-self: stretch !important; } } @media (min-width: 768px) { .flex-md-row { flex-direction: row !important; } .flex-md-column { flex-direction: column !important; } .flex-md-row-reverse { flex-direction: row-reverse !important; } .flex-md-column-reverse { flex-direction: column-reverse !important; } .flex-md-wrap { flex-wrap: wrap !important; } .flex-md-nowrap { flex-wrap: nowrap !important; } .flex-md-wrap-reverse { flex-wrap: wrap-reverse !important; } .flex-md-fill { flex: 1 1 auto !important; } .flex-md-grow-0 { flex-grow: 0 !important; } .flex-md-grow-1 { flex-grow: 1 !important; } .flex-md-shrink-0 { flex-shrink: 0 !important; } .flex-md-shrink-1 { flex-shrink: 1 !important; } .justify-content-md-start { justify-content: flex-start !important; } .justify-content-md-end { justify-content: flex-end !important; } .justify-content-md-center { justify-content: center !important; } .justify-content-md-between { justify-content: space-between !important; } .justify-content-md-around { justify-content: space-around !important; } .align-items-md-start { align-items: flex-start !important; } .align-items-md-end { align-items: flex-end !important; } .align-items-md-center { align-items: center !important; } .align-items-md-baseline { align-items: baseline !important; } .align-items-md-stretch { align-items: stretch !important; } .align-content-md-start { align-content: flex-start !important; } .align-content-md-end { align-content: flex-end !important; } .align-content-md-center { align-content: center !important; } .align-content-md-between { align-content: space-between !important; } .align-content-md-around { align-content: space-around !important; } .align-content-md-stretch { align-content: stretch !important; } .align-self-md-auto { align-self: auto !important; } .align-self-md-start { align-self: flex-start !important; } .align-self-md-end { align-self: flex-end !important; } .align-self-md-center { align-self: center !important; } .align-self-md-baseline { align-self: baseline !important; } .align-self-md-stretch { align-self: stretch !important; } } @media (min-width: 992px) { .flex-lg-row { flex-direction: row !important; } .flex-lg-column { flex-direction: column !important; } .flex-lg-row-reverse { flex-direction: row-reverse !important; } .flex-lg-column-reverse { flex-direction: column-reverse !important; } .flex-lg-wrap { flex-wrap: wrap !important; } .flex-lg-nowrap { flex-wrap: nowrap !important; } .flex-lg-wrap-reverse { flex-wrap: wrap-reverse !important; } .flex-lg-fill { flex: 1 1 auto !important; } .flex-lg-grow-0 { flex-grow: 0 !important; } .flex-lg-grow-1 { flex-grow: 1 !important; } .flex-lg-shrink-0 { flex-shrink: 0 !important; } .flex-lg-shrink-1 { flex-shrink: 1 !important; } .justify-content-lg-start { justify-content: flex-start !important; } .justify-content-lg-end { justify-content: flex-end !important; } .justify-content-lg-center { justify-content: center !important; } .justify-content-lg-between { justify-content: space-between !important; } .justify-content-lg-around { justify-content: space-around !important; } .align-items-lg-start { align-items: flex-start !important; } .align-items-lg-end { align-items: flex-end !important; } .align-items-lg-center { align-items: center !important; } .align-items-lg-baseline { align-items: baseline !important; } .align-items-lg-stretch { align-items: stretch !important; } .align-content-lg-start { align-content: flex-start !important; } .align-content-lg-end { align-content: flex-end !important; } .align-content-lg-center { align-content: center !important; } .align-content-lg-between { align-content: space-between !important; } .align-content-lg-around { align-content: space-around !important; } .align-content-lg-stretch { align-content: stretch !important; } .align-self-lg-auto { align-self: auto !important; } .align-self-lg-start { align-self: flex-start !important; } .align-self-lg-end { align-self: flex-end !important; } .align-self-lg-center { align-self: center !important; } .align-self-lg-baseline { align-self: baseline !important; } .align-self-lg-stretch { align-self: stretch !important; } } @media (min-width: 1200px) { .flex-xl-row { flex-direction: row !important; } .flex-xl-column { flex-direction: column !important; } .flex-xl-row-reverse { flex-direction: row-reverse !important; } .flex-xl-column-reverse { flex-direction: column-reverse !important; } .flex-xl-wrap { flex-wrap: wrap !important; } .flex-xl-nowrap { flex-wrap: nowrap !important; } .flex-xl-wrap-reverse { flex-wrap: wrap-reverse !important; } .flex-xl-fill { flex: 1 1 auto !important; } .flex-xl-grow-0 { flex-grow: 0 !important; } .flex-xl-grow-1 { flex-grow: 1 !important; } .flex-xl-shrink-0 { flex-shrink: 0 !important; } .flex-xl-shrink-1 { flex-shrink: 1 !important; } .justify-content-xl-start { justify-content: flex-start !important; } .justify-content-xl-end { justify-content: flex-end !important; } .justify-content-xl-center { justify-content: center !important; } .justify-content-xl-between { justify-content: space-between !important; } .justify-content-xl-around { justify-content: space-around !important; } .align-items-xl-start { align-items: flex-start !important; } .align-items-xl-end { align-items: flex-end !important; } .align-items-xl-center { align-items: center !important; } .align-items-xl-baseline { align-items: baseline !important; } .align-items-xl-stretch { align-items: stretch !important; } .align-content-xl-start { align-content: flex-start !important; } .align-content-xl-end { align-content: flex-end !important; } .align-content-xl-center { align-content: center !important; } .align-content-xl-between { align-content: space-between !important; } .align-content-xl-around { align-content: space-around !important; } .align-content-xl-stretch { align-content: stretch !important; } .align-self-xl-auto { align-self: auto !important; } .align-self-xl-start { align-self: flex-start !important; } .align-self-xl-end { align-self: flex-end !important; } .align-self-xl-center { align-self: center !important; } .align-self-xl-baseline { align-self: baseline !important; } .align-self-xl-stretch { align-self: stretch !important; } } .float-left { float: left !important; } .float-right { float: right !important; } .float-none { float: none !important; } @media (min-width: 576px) { .float-sm-left { float: left !important; } .float-sm-right { float: right !important; } .float-sm-none { float: none !important; } } @media (min-width: 768px) { .float-md-left { float: left !important; } .float-md-right { float: right !important; } .float-md-none { float: none !important; } } @media (min-width: 992px) { .float-lg-left { float: left !important; } .float-lg-right { float: right !important; } .float-lg-none { float: none !important; } } @media (min-width: 1200px) { .float-xl-left { float: left !important; } .float-xl-right { float: right !important; } .float-xl-none { float: none !important; } } .position-static { position: static !important; } .position-relative { position: relative !important; } .position-absolute { position: absolute !important; } .position-fixed { position: fixed !important; } .position-sticky { position: sticky !important; } .fixed-top { position: fixed; top: 0; right: 0; left: 0; z-index: 1030; } .fixed-bottom { position: fixed; right: 0; bottom: 0; left: 0; z-index: 1030; } @supports (position: sticky) { .sticky-top { position: sticky; top: 0; z-index: 1020; } } .sr-only { position: absolute; width: 1px; height: 1px; padding: 0; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; } .sr-only-focusable:active, .sr-only-focusable:focus { position: static; width: auto; height: auto; overflow: visible; clip: auto; white-space: normal; } .shadow-sm { box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075) !important; } .shadow { box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15) !important; } .shadow-lg { box-shadow: 0 1rem 3rem rgba(0, 0, 0, 0.175) !important; } .shadow-none { box-shadow: none !important; } .w-25 { width: 25% !important; } .w-50 { width: 50% !important; } .w-75 { width: 75% !important; } .w-100 { width: 100% !important; } .w-auto { width: auto !important; } .h-25 { height: 25% !important; } .h-50 { height: 50% !important; } .h-75 { height: 75% !important; } .h-100 { height: 100% !important; } .h-auto { height: auto !important; } .mw-100 { max-width: 100% !important; } .mh-100 { max-height: 100% !important; } .m-0 { margin: 0 !important; } .mt-0, .my-0 { margin-top: 0 !important; } .mr-0, .mx-0 { margin-right: 0 !important; } .mb-0, .my-0 { margin-bottom: 0 !important; } .ml-0, .mx-0 { margin-left: 0 !important; } .m-1 { margin: 0.25rem !important; } .mt-1, .my-1 { margin-top: 0.25rem !important; } .mr-1, .mx-1 { margin-right: 0.25rem !important; } .mb-1, .my-1 { margin-bottom: 0.25rem !important; } .ml-1, .mx-1 { margin-left: 0.25rem !important; } .m-2 { margin: 0.5rem !important; } .mt-2, .my-2 { margin-top: 0.5rem !important; } .mr-2, .mx-2 { margin-right: 0.5rem !important; } .mb-2, .progress-group, .my-2 { margin-bottom: 0.5rem !important; } .ml-2, .mx-2 { margin-left: 0.5rem !important; } .m-3 { margin: 1rem !important; } .mt-3, .my-3 { margin-top: 1rem !important; } .mr-3, .mx-3 { margin-right: 1rem !important; } .mb-3, .small-box, .card, .info-box, .callout, .my-3 { margin-bottom: 1rem !important; } .ml-3, .mx-3 { margin-left: 1rem !important; } .m-4 { margin: 1.5rem !important; } .mt-4, .my-4 { margin-top: 1.5rem !important; } .mr-4, .mx-4 { margin-right: 1.5rem !important; } .mb-4, .my-4 { margin-bottom: 1.5rem !important; } .ml-4, .mx-4 { margin-left: 1.5rem !important; } .m-5 { margin: 3rem !important; } .mt-5, .my-5 { margin-top: 3rem !important; } .mr-5, .mx-5 { margin-right: 3rem !important; } .mb-5, .my-5 { margin-bottom: 3rem !important; } .ml-5, .mx-5 { margin-left: 3rem !important; } .p-0 { padding: 0 !important; } .pt-0, .py-0 { padding-top: 0 !important; } .pr-0, .px-0 { padding-right: 0 !important; } .pb-0, .py-0 { padding-bottom: 0 !important; } .pl-0, .px-0 { padding-left: 0 !important; } .p-1 { padding: 0.25rem !important; } .pt-1, .py-1 { padding-top: 0.25rem !important; } .pr-1, .px-1 { padding-right: 0.25rem !important; } .pb-1, .py-1 { padding-bottom: 0.25rem !important; } .pl-1, .px-1 { padding-left: 0.25rem !important; } .p-2 { padding: 0.5rem !important; } .pt-2, .py-2 { padding-top: 0.5rem !important; } .pr-2, .px-2 { padding-right: 0.5rem !important; } .pb-2, .py-2 { padding-bottom: 0.5rem !important; } .pl-2, .px-2 { padding-left: 0.5rem !important; } .p-3 { padding: 1rem !important; } .pt-3, .py-3 { padding-top: 1rem !important; } .pr-3, .px-3 { padding-right: 1rem !important; } .pb-3, .py-3 { padding-bottom: 1rem !important; } .pl-3, .px-3 { padding-left: 1rem !important; } .p-4 { padding: 1.5rem !important; } .pt-4, .py-4 { padding-top: 1.5rem !important; } .pr-4, .card-body.p-0 .table thead > tr > th:last-of-type, .card-body.p-0 .table thead > tr > td:last-of-type, .card-body.p-0 .table tbody > tr > th:last-of-type, .card-body.p-0 .table tbody > tr > td:last-of-type, .px-4 { padding-right: 1.5rem !important; } .pb-4, .py-4 { padding-bottom: 1.5rem !important; } .pl-4, .card-body.p-0 .table thead > tr > th:first-of-type, .card-body.p-0 .table thead > tr > td:first-of-type, .card-body.p-0 .table tbody > tr > th:first-of-type, .card-body.p-0 .table tbody > tr > td:first-of-type, .px-4 { padding-left: 1.5rem !important; } .p-5 { padding: 3rem !important; } .pt-5, .py-5 { padding-top: 3rem !important; } .pr-5, .px-5 { padding-right: 3rem !important; } .pb-5, .py-5 { padding-bottom: 3rem !important; } .pl-5, .px-5 { padding-left: 3rem !important; } .m-auto { margin: auto !important; } .mt-auto, .my-auto { margin-top: auto !important; } .mr-auto, .mx-auto { margin-right: auto !important; } .mb-auto, .my-auto { margin-bottom: auto !important; } .ml-auto, .mx-auto { margin-left: auto !important; } @media (min-width: 576px) { .m-sm-0 { margin: 0 !important; } .mt-sm-0, .my-sm-0 { margin-top: 0 !important; } .mr-sm-0, .mx-sm-0 { margin-right: 0 !important; } .mb-sm-0, .my-sm-0 { margin-bottom: 0 !important; } .ml-sm-0, .mx-sm-0 { margin-left: 0 !important; } .m-sm-1 { margin: 0.25rem !important; } .mt-sm-1, .my-sm-1 { margin-top: 0.25rem !important; } .mr-sm-1, .mx-sm-1 { margin-right: 0.25rem !important; } .mb-sm-1, .my-sm-1 { margin-bottom: 0.25rem !important; } .ml-sm-1, .mx-sm-1 { margin-left: 0.25rem !important; } .m-sm-2 { margin: 0.5rem !important; } .mt-sm-2, .my-sm-2 { margin-top: 0.5rem !important; } .mr-sm-2, .mx-sm-2 { margin-right: 0.5rem !important; } .mb-sm-2, .my-sm-2 { margin-bottom: 0.5rem !important; } .ml-sm-2, .mx-sm-2 { margin-left: 0.5rem !important; } .m-sm-3 { margin: 1rem !important; } .mt-sm-3, .my-sm-3 { margin-top: 1rem !important; } .mr-sm-3, .mx-sm-3 { margin-right: 1rem !important; } .mb-sm-3, .my-sm-3 { margin-bottom: 1rem !important; } .ml-sm-3, .mx-sm-3 { margin-left: 1rem !important; } .m-sm-4 { margin: 1.5rem !important; } .mt-sm-4, .my-sm-4 { margin-top: 1.5rem !important; } .mr-sm-4, .mx-sm-4 { margin-right: 1.5rem !important; } .mb-sm-4, .my-sm-4 { margin-bottom: 1.5rem !important; } .ml-sm-4, .mx-sm-4 { margin-left: 1.5rem !important; } .m-sm-5 { margin: 3rem !important; } .mt-sm-5, .my-sm-5 { margin-top: 3rem !important; } .mr-sm-5, .mx-sm-5 { margin-right: 3rem !important; } .mb-sm-5, .my-sm-5 { margin-bottom: 3rem !important; } .ml-sm-5, .mx-sm-5 { margin-left: 3rem !important; } .p-sm-0 { padding: 0 !important; } .pt-sm-0, .py-sm-0 { padding-top: 0 !important; } .pr-sm-0, .px-sm-0 { padding-right: 0 !important; } .pb-sm-0, .py-sm-0 { padding-bottom: 0 !important; } .pl-sm-0, .px-sm-0 { padding-left: 0 !important; } .p-sm-1 { padding: 0.25rem !important; } .pt-sm-1, .py-sm-1 { padding-top: 0.25rem !important; } .pr-sm-1, .px-sm-1 { padding-right: 0.25rem !important; } .pb-sm-1, .py-sm-1 { padding-bottom: 0.25rem !important; } .pl-sm-1, .px-sm-1 { padding-left: 0.25rem !important; } .p-sm-2 { padding: 0.5rem !important; } .pt-sm-2, .py-sm-2 { padding-top: 0.5rem !important; } .pr-sm-2, .px-sm-2 { padding-right: 0.5rem !important; } .pb-sm-2, .py-sm-2 { padding-bottom: 0.5rem !important; } .pl-sm-2, .px-sm-2 { padding-left: 0.5rem !important; } .p-sm-3 { padding: 1rem !important; } .pt-sm-3, .py-sm-3 { padding-top: 1rem !important; } .pr-sm-3, .px-sm-3 { padding-right: 1rem !important; } .pb-sm-3, .py-sm-3 { padding-bottom: 1rem !important; } .pl-sm-3, .px-sm-3 { padding-left: 1rem !important; } .p-sm-4 { padding: 1.5rem !important; } .pt-sm-4, .py-sm-4 { padding-top: 1.5rem !important; } .pr-sm-4, .px-sm-4 { padding-right: 1.5rem !important; } .pb-sm-4, .py-sm-4 { padding-bottom: 1.5rem !important; } .pl-sm-4, .px-sm-4 { padding-left: 1.5rem !important; } .p-sm-5 { padding: 3rem !important; } .pt-sm-5, .py-sm-5 { padding-top: 3rem !important; } .pr-sm-5, .px-sm-5 { padding-right: 3rem !important; } .pb-sm-5, .py-sm-5 { padding-bottom: 3rem !important; } .pl-sm-5, .px-sm-5 { padding-left: 3rem !important; } .m-sm-auto { margin: auto !important; } .mt-sm-auto, .my-sm-auto { margin-top: auto !important; } .mr-sm-auto, .mx-sm-auto { margin-right: auto !important; } .mb-sm-auto, .my-sm-auto { margin-bottom: auto !important; } .ml-sm-auto, .mx-sm-auto { margin-left: auto !important; } } @media (min-width: 768px) { .m-md-0 { margin: 0 !important; } .mt-md-0, .my-md-0 { margin-top: 0 !important; } .mr-md-0, .mx-md-0 { margin-right: 0 !important; } .mb-md-0, .my-md-0 { margin-bottom: 0 !important; } .ml-md-0, .mx-md-0 { margin-left: 0 !important; } .m-md-1 { margin: 0.25rem !important; } .mt-md-1, .my-md-1 { margin-top: 0.25rem !important; } .mr-md-1, .mx-md-1 { margin-right: 0.25rem !important; } .mb-md-1, .my-md-1 { margin-bottom: 0.25rem !important; } .ml-md-1, .mx-md-1 { margin-left: 0.25rem !important; } .m-md-2 { margin: 0.5rem !important; } .mt-md-2, .my-md-2 { margin-top: 0.5rem !important; } .mr-md-2, .mx-md-2 { margin-right: 0.5rem !important; } .mb-md-2, .my-md-2 { margin-bottom: 0.5rem !important; } .ml-md-2, .mx-md-2 { margin-left: 0.5rem !important; } .m-md-3 { margin: 1rem !important; } .mt-md-3, .my-md-3 { margin-top: 1rem !important; } .mr-md-3, .mx-md-3 { margin-right: 1rem !important; } .mb-md-3, .my-md-3 { margin-bottom: 1rem !important; } .ml-md-3, .mx-md-3 { margin-left: 1rem !important; } .m-md-4 { margin: 1.5rem !important; } .mt-md-4, .my-md-4 { margin-top: 1.5rem !important; } .mr-md-4, .mx-md-4 { margin-right: 1.5rem !important; } .mb-md-4, .my-md-4 { margin-bottom: 1.5rem !important; } .ml-md-4, .mx-md-4 { margin-left: 1.5rem !important; } .m-md-5 { margin: 3rem !important; } .mt-md-5, .my-md-5 { margin-top: 3rem !important; } .mr-md-5, .mx-md-5 { margin-right: 3rem !important; } .mb-md-5, .my-md-5 { margin-bottom: 3rem !important; } .ml-md-5, .mx-md-5 { margin-left: 3rem !important; } .p-md-0 { padding: 0 !important; } .pt-md-0, .py-md-0 { padding-top: 0 !important; } .pr-md-0, .px-md-0 { padding-right: 0 !important; } .pb-md-0, .py-md-0 { padding-bottom: 0 !important; } .pl-md-0, .px-md-0 { padding-left: 0 !important; } .p-md-1 { padding: 0.25rem !important; } .pt-md-1, .py-md-1 { padding-top: 0.25rem !important; } .pr-md-1, .px-md-1 { padding-right: 0.25rem !important; } .pb-md-1, .py-md-1 { padding-bottom: 0.25rem !important; } .pl-md-1, .px-md-1 { padding-left: 0.25rem !important; } .p-md-2 { padding: 0.5rem !important; } .pt-md-2, .py-md-2 { padding-top: 0.5rem !important; } .pr-md-2, .px-md-2 { padding-right: 0.5rem !important; } .pb-md-2, .py-md-2 { padding-bottom: 0.5rem !important; } .pl-md-2, .px-md-2 { padding-left: 0.5rem !important; } .p-md-3 { padding: 1rem !important; } .pt-md-3, .py-md-3 { padding-top: 1rem !important; } .pr-md-3, .px-md-3 { padding-right: 1rem !important; } .pb-md-3, .py-md-3 { padding-bottom: 1rem !important; } .pl-md-3, .px-md-3 { padding-left: 1rem !important; } .p-md-4 { padding: 1.5rem !important; } .pt-md-4, .py-md-4 { padding-top: 1.5rem !important; } .pr-md-4, .px-md-4 { padding-right: 1.5rem !important; } .pb-md-4, .py-md-4 { padding-bottom: 1.5rem !important; } .pl-md-4, .px-md-4 { padding-left: 1.5rem !important; } .p-md-5 { padding: 3rem !important; } .pt-md-5, .py-md-5 { padding-top: 3rem !important; } .pr-md-5, .px-md-5 { padding-right: 3rem !important; } .pb-md-5, .py-md-5 { padding-bottom: 3rem !important; } .pl-md-5, .px-md-5 { padding-left: 3rem !important; } .m-md-auto { margin: auto !important; } .mt-md-auto, .my-md-auto { margin-top: auto !important; } .mr-md-auto, .mx-md-auto { margin-right: auto !important; } .mb-md-auto, .my-md-auto { margin-bottom: auto !important; } .ml-md-auto, .mx-md-auto { margin-left: auto !important; } } @media (min-width: 992px) { .m-lg-0 { margin: 0 !important; } .mt-lg-0, .my-lg-0 { margin-top: 0 !important; } .mr-lg-0, .mx-lg-0 { margin-right: 0 !important; } .mb-lg-0, .my-lg-0 { margin-bottom: 0 !important; } .ml-lg-0, .mx-lg-0 { margin-left: 0 !important; } .m-lg-1 { margin: 0.25rem !important; } .mt-lg-1, .my-lg-1 { margin-top: 0.25rem !important; } .mr-lg-1, .mx-lg-1 { margin-right: 0.25rem !important; } .mb-lg-1, .my-lg-1 { margin-bottom: 0.25rem !important; } .ml-lg-1, .mx-lg-1 { margin-left: 0.25rem !important; } .m-lg-2 { margin: 0.5rem !important; } .mt-lg-2, .my-lg-2 { margin-top: 0.5rem !important; } .mr-lg-2, .mx-lg-2 { margin-right: 0.5rem !important; } .mb-lg-2, .my-lg-2 { margin-bottom: 0.5rem !important; } .ml-lg-2, .mx-lg-2 { margin-left: 0.5rem !important; } .m-lg-3 { margin: 1rem !important; } .mt-lg-3, .my-lg-3 { margin-top: 1rem !important; } .mr-lg-3, .mx-lg-3 { margin-right: 1rem !important; } .mb-lg-3, .my-lg-3 { margin-bottom: 1rem !important; } .ml-lg-3, .mx-lg-3 { margin-left: 1rem !important; } .m-lg-4 { margin: 1.5rem !important; } .mt-lg-4, .my-lg-4 { margin-top: 1.5rem !important; } .mr-lg-4, .mx-lg-4 { margin-right: 1.5rem !important; } .mb-lg-4, .my-lg-4 { margin-bottom: 1.5rem !important; } .ml-lg-4, .mx-lg-4 { margin-left: 1.5rem !important; } .m-lg-5 { margin: 3rem !important; } .mt-lg-5, .my-lg-5 { margin-top: 3rem !important; } .mr-lg-5, .mx-lg-5 { margin-right: 3rem !important; } .mb-lg-5, .my-lg-5 { margin-bottom: 3rem !important; } .ml-lg-5, .mx-lg-5 { margin-left: 3rem !important; } .p-lg-0 { padding: 0 !important; } .pt-lg-0, .py-lg-0 { padding-top: 0 !important; } .pr-lg-0, .px-lg-0 { padding-right: 0 !important; } .pb-lg-0, .py-lg-0 { padding-bottom: 0 !important; } .pl-lg-0, .px-lg-0 { padding-left: 0 !important; } .p-lg-1 { padding: 0.25rem !important; } .pt-lg-1, .py-lg-1 { padding-top: 0.25rem !important; } .pr-lg-1, .px-lg-1 { padding-right: 0.25rem !important; } .pb-lg-1, .py-lg-1 { padding-bottom: 0.25rem !important; } .pl-lg-1, .px-lg-1 { padding-left: 0.25rem !important; } .p-lg-2 { padding: 0.5rem !important; } .pt-lg-2, .py-lg-2 { padding-top: 0.5rem !important; } .pr-lg-2, .px-lg-2 { padding-right: 0.5rem !important; } .pb-lg-2, .py-lg-2 { padding-bottom: 0.5rem !important; } .pl-lg-2, .px-lg-2 { padding-left: 0.5rem !important; } .p-lg-3 { padding: 1rem !important; } .pt-lg-3, .py-lg-3 { padding-top: 1rem !important; } .pr-lg-3, .px-lg-3 { padding-right: 1rem !important; } .pb-lg-3, .py-lg-3 { padding-bottom: 1rem !important; } .pl-lg-3, .px-lg-3 { padding-left: 1rem !important; } .p-lg-4 { padding: 1.5rem !important; } .pt-lg-4, .py-lg-4 { padding-top: 1.5rem !important; } .pr-lg-4, .px-lg-4 { padding-right: 1.5rem !important; } .pb-lg-4, .py-lg-4 { padding-bottom: 1.5rem !important; } .pl-lg-4, .px-lg-4 { padding-left: 1.5rem !important; } .p-lg-5 { padding: 3rem !important; } .pt-lg-5, .py-lg-5 { padding-top: 3rem !important; } .pr-lg-5, .px-lg-5 { padding-right: 3rem !important; } .pb-lg-5, .py-lg-5 { padding-bottom: 3rem !important; } .pl-lg-5, .px-lg-5 { padding-left: 3rem !important; } .m-lg-auto { margin: auto !important; } .mt-lg-auto, .my-lg-auto { margin-top: auto !important; } .mr-lg-auto, .mx-lg-auto { margin-right: auto !important; } .mb-lg-auto, .my-lg-auto { margin-bottom: auto !important; } .ml-lg-auto, .mx-lg-auto { margin-left: auto !important; } } @media (min-width: 1200px) { .m-xl-0 { margin: 0 !important; } .mt-xl-0, .my-xl-0 { margin-top: 0 !important; } .mr-xl-0, .mx-xl-0 { margin-right: 0 !important; } .mb-xl-0, .my-xl-0 { margin-bottom: 0 !important; } .ml-xl-0, .mx-xl-0 { margin-left: 0 !important; } .m-xl-1 { margin: 0.25rem !important; } .mt-xl-1, .my-xl-1 { margin-top: 0.25rem !important; } .mr-xl-1, .mx-xl-1 { margin-right: 0.25rem !important; } .mb-xl-1, .my-xl-1 { margin-bottom: 0.25rem !important; } .ml-xl-1, .mx-xl-1 { margin-left: 0.25rem !important; } .m-xl-2 { margin: 0.5rem !important; } .mt-xl-2, .my-xl-2 { margin-top: 0.5rem !important; } .mr-xl-2, .mx-xl-2 { margin-right: 0.5rem !important; } .mb-xl-2, .my-xl-2 { margin-bottom: 0.5rem !important; } .ml-xl-2, .mx-xl-2 { margin-left: 0.5rem !important; } .m-xl-3 { margin: 1rem !important; } .mt-xl-3, .my-xl-3 { margin-top: 1rem !important; } .mr-xl-3, .mx-xl-3 { margin-right: 1rem !important; } .mb-xl-3, .my-xl-3 { margin-bottom: 1rem !important; } .ml-xl-3, .mx-xl-3 { margin-left: 1rem !important; } .m-xl-4 { margin: 1.5rem !important; } .mt-xl-4, .my-xl-4 { margin-top: 1.5rem !important; } .mr-xl-4, .mx-xl-4 { margin-right: 1.5rem !important; } .mb-xl-4, .my-xl-4 { margin-bottom: 1.5rem !important; } .ml-xl-4, .mx-xl-4 { margin-left: 1.5rem !important; } .m-xl-5 { margin: 3rem !important; } .mt-xl-5, .my-xl-5 { margin-top: 3rem !important; } .mr-xl-5, .mx-xl-5 { margin-right: 3rem !important; } .mb-xl-5, .my-xl-5 { margin-bottom: 3rem !important; } .ml-xl-5, .mx-xl-5 { margin-left: 3rem !important; } .p-xl-0 { padding: 0 !important; } .pt-xl-0, .py-xl-0 { padding-top: 0 !important; } .pr-xl-0, .px-xl-0 { padding-right: 0 !important; } .pb-xl-0, .py-xl-0 { padding-bottom: 0 !important; } .pl-xl-0, .px-xl-0 { padding-left: 0 !important; } .p-xl-1 { padding: 0.25rem !important; } .pt-xl-1, .py-xl-1 { padding-top: 0.25rem !important; } .pr-xl-1, .px-xl-1 { padding-right: 0.25rem !important; } .pb-xl-1, .py-xl-1 { padding-bottom: 0.25rem !important; } .pl-xl-1, .px-xl-1 { padding-left: 0.25rem !important; } .p-xl-2 { padding: 0.5rem !important; } .pt-xl-2, .py-xl-2 { padding-top: 0.5rem !important; } .pr-xl-2, .px-xl-2 { padding-right: 0.5rem !important; } .pb-xl-2, .py-xl-2 { padding-bottom: 0.5rem !important; } .pl-xl-2, .px-xl-2 { padding-left: 0.5rem !important; } .p-xl-3 { padding: 1rem !important; } .pt-xl-3, .py-xl-3 { padding-top: 1rem !important; } .pr-xl-3, .px-xl-3 { padding-right: 1rem !important; } .pb-xl-3, .py-xl-3 { padding-bottom: 1rem !important; } .pl-xl-3, .px-xl-3 { padding-left: 1rem !important; } .p-xl-4 { padding: 1.5rem !important; } .pt-xl-4, .py-xl-4 { padding-top: 1.5rem !important; } .pr-xl-4, .px-xl-4 { padding-right: 1.5rem !important; } .pb-xl-4, .py-xl-4 { padding-bottom: 1.5rem !important; } .pl-xl-4, .px-xl-4 { padding-left: 1.5rem !important; } .p-xl-5 { padding: 3rem !important; } .pt-xl-5, .py-xl-5 { padding-top: 3rem !important; } .pr-xl-5, .px-xl-5 { padding-right: 3rem !important; } .pb-xl-5, .py-xl-5 { padding-bottom: 3rem !important; } .pl-xl-5, .px-xl-5 { padding-left: 3rem !important; } .m-xl-auto { margin: auto !important; } .mt-xl-auto, .my-xl-auto { margin-top: auto !important; } .mr-xl-auto, .mx-xl-auto { margin-right: auto !important; } .mb-xl-auto, .my-xl-auto { margin-bottom: auto !important; } .ml-xl-auto, .mx-xl-auto { margin-left: auto !important; } } .text-monospace { font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; } .text-justify { text-align: justify !important; } .text-nowrap { white-space: nowrap !important; } .text-truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .text-left { text-align: left !important; } .text-right { text-align: right !important; } .text-center { text-align: center !important; } @media (min-width: 576px) { .text-sm-left { text-align: left !important; } .text-sm-right { text-align: right !important; } .text-sm-center { text-align: center !important; } } @media (min-width: 768px) { .text-md-left { text-align: left !important; } .text-md-right { text-align: right !important; } .text-md-center { text-align: center !important; } } @media (min-width: 992px) { .text-lg-left { text-align: left !important; } .text-lg-right { text-align: right !important; } .text-lg-center { text-align: center !important; } } @media (min-width: 1200px) { .text-xl-left { text-align: left !important; } .text-xl-right { text-align: right !important; } .text-xl-center { text-align: center !important; } } .text-lowercase { text-transform: lowercase !important; } .text-uppercase { text-transform: uppercase !important; } .text-capitalize { text-transform: capitalize !important; } .font-weight-light { font-weight: 300 !important; } .font-weight-normal { font-weight: 400 !important; } .font-weight-bold { font-weight: 700 !important; } .font-italic { font-style: italic !important; } .text-white { color: #ffffff !important; } .text-primary { color: #007bff !important; } a.text-primary:hover, a.text-primary:focus { color: #0062cc !important; } .text-secondary { color: #6c757d !important; } a.text-secondary:hover, a.text-secondary:focus { color: #545b62 !important; } .text-success { color: #28a745 !important; } a.text-success:hover, a.text-success:focus { color: #1e7e34 !important; } .text-info { color: #17a2b8 !important; } a.text-info:hover, a.text-info:focus { color: #117a8b !important; } .text-warning { color: #ffc107 !important; } a.text-warning:hover, a.text-warning:focus { color: #d39e00 !important; } .text-danger { color: #dc3545 !important; } a.text-danger:hover, a.text-danger:focus { color: #bd2130 !important; } .text-light { color: #f8f9fa !important; } a.text-light:hover, a.text-light:focus { color: #dae0e5 !important; } .text-dark { color: #343a40 !important; } a.text-dark:hover, a.text-dark:focus { color: #1d2124 !important; } .text-body { color: #212529 !important; } .text-muted { color: #6c757d !important; } .text-black-50 { color: rgba(0, 0, 0, 0.5) !important; } .text-white-50 { color: rgba(255, 255, 255, 0.5) !important; } .text-hide { font: 0/0 a; color: transparent; text-shadow: none; background-color: transparent; border: 0; } .visible { visibility: visible !important; } .invisible { visibility: hidden !important; } @media print { *, *::before, *::after { text-shadow: none !important; box-shadow: none !important; } a:not(.btn) { text-decoration: underline; } abbr[title]::after { content: " (" attr(title) ")"; } pre { white-space: pre-wrap !important; } pre, blockquote { border: 1px solid #adb5bd; page-break-inside: avoid; } thead { display: table-header-group; } tr, img { page-break-inside: avoid; } p, h2, h3 { orphans: 3; widows: 3; } h2, h3 { page-break-after: avoid; } @page { size: a3; } body { min-width: 992px !important; } .container { min-width: 992px !important; } .navbar { display: none; } .badge { border: 1px solid #000; } .table { border-collapse: collapse !important; } .table td, .table th { background-color: #ffffff !important; } .table-bordered th, .table-bordered td { border: 1px solid #dee2e6 !important; } } /* * Core: General Layout Style * ------------------------- */ html, body, .wrapper { min-height: 100%; overflow-x: hidden; } .wrapper { position: relative; } .layout-boxed .wrapper { box-shadow: 0 0 10px rgba(0, 0, 0, 0.3); } .layout-boxed .wrapper, .layout-boxed .wrapper:before { margin: 0 auto; max-width: 1250px; } @media (min-width: 768px) { .content-wrapper, .main-footer, .main-header { transition: margin-left 0.3s ease-in-out; margin-left: 250px; z-index: 3000; } } @media screen and (min-width: 768px) and (prefers-reduced-motion: reduce) { .content-wrapper, .main-footer, .main-header { transition: none; } } @media (min-width: 768px) { .sidebar-collapse .content-wrapper, .sidebar-collapse .main-footer, .sidebar-collapse .main-header { margin-left: 0; } } @media (max-width: 991.98px) { .content-wrapper, .content-wrapper:before, .main-footer, .main-footer:before, .main-header, .main-header:before { margin-left: 0; } } .content-wrapper { background: #f4f6f9; } .content-wrapper > .content { padding: 0 0.5rem; } .main-sidebar { position: fixed; top: 0; left: 0; bottom: 0; } .main-sidebar, .main-sidebar:before { transition: margin-left 0.3s ease-in-out, width 0.3s ease-in-out; width: 250px; } @media screen and (prefers-reduced-motion: reduce) { .main-sidebar, .main-sidebar:before { transition: none; } } .sidebar-collapse .main-sidebar, .sidebar-collapse .main-sidebar:before { margin-left: -250px; } @media (max-width: 991.98px) { .main-sidebar, .main-sidebar:before { box-shadow: none !important; margin-left: -250px; } .sidebar-open .main-sidebar, .sidebar-open .main-sidebar:before { margin-left: 0; } } .main-footer { padding: 15px; color: #555; border-top: 1px solid #dee2e6; background: #ffffff; } .content-header { padding: 15px 0.5rem; } .content-header h1 { font-size: 1.8rem; margin: 0; } .content-header .breadcrumb { margin-bottom: 0; padding: 0; background: transparent; line-height: 1.8rem; } .hold-transition .content-wrapper, .hold-transition .main-header, .hold-transition .main-footer { transition: none !important; } /* * Component: Main Header * ---------------------- */ .main-header { z-index: 1000; } .main-header .navbar-nav .nav-item { margin: 0; } .main-header .nav-link { position: relative; height: 2.5rem; } .main-header .navbar-nav[class*="-right"] .dropdown-menu { margin-top: -3px; right: 0; left: auto; } @media (max-width: 575.98px) { .main-header .navbar-nav[class*="-right"] .dropdown-menu { left: 0; right: auto; } } .navbar-img { height: 1.25rem; width: auto; } .navbar-badge { position: absolute; top: 9px; right: 5px; font-size: .6rem; font-weight: 300; padding: 2px 4px; } .btn-navbar { border-left-width: 0; background-color: transparent; } .form-control-navbar { border-right-width: 0; } .form-control-navbar + .input-group-append { margin-left: 0; } .form-control-navbar, .btn-navbar { transition: none; } .navbar-dark .form-control-navbar, .navbar-dark .btn-navbar { background-color: rgba(255, 255, 255, 0.2); border: 0; } .navbar-dark .form-control-navbar::placeholder, .navbar-dark .form-control-navbar + .input-group-append > .btn-navbar { color: rgba(255, 255, 255, 0.6); } .navbar-dark .form-control-navbar :-moz-placeholder { color: rgba(255, 255, 255, 0.6); } .navbar-dark .form-control-navbar ::-moz-placeholder { color: rgba(255, 255, 255, 0.6); } .navbar-dark .form-control-navbar :-ms-input-placeholder { color: rgba(255, 255, 255, 0.6); } .navbar-dark .form-control-navbar:focus, .navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar { border: 0 !important; background-color: rgba(255, 255, 255, 0.6); color: #343a40; } .navbar-light .form-control-navbar, .navbar-light .btn-navbar { background-color: #f2f4f6; border: 0; } .navbar-light .form-control-navbar::placeholder, .navbar-light .form-control-navbar + .input-group-append > .btn-navbar { color: rgba(0, 0, 0, 0.6); } .navbar-light .form-control-navbar :-moz-placeholder { color: rgba(0, 0, 0, 0.6); } .navbar-light .form-control-navbar ::-moz-placeholder { color: rgba(0, 0, 0, 0.6); } .navbar-light .form-control-navbar :-ms-input-placeholder { color: rgba(0, 0, 0, 0.6); } .navbar-light .form-control-navbar:focus, .navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar { border: 0 !important; background-color: #e9ecef; color: #343a40; } .brand-link { padding: 0.8125rem 0.5rem; font-size: 1.25rem; display: block; line-height: 1.5; white-space: nowrap; } .brand-link:hover { color: #ffffff; text-decoration: none; } [class*="sidebar-dark"] .brand-link { color: rgba(255, 255, 255, 0.8); border-bottom: 1px solid #4b545c; } [class*="sidebar-light"] .brand-link { color: rgba(0, 0, 0, 0.8); border-bottom: 1px solid #dee2e6; } .brand-image { float: left; line-height: .8; max-height: 34px; width: auto; margin-left: .8rem; margin-right: .5rem; margin-top: -3px; } /** * Component: Sidebar * ------------------ */ .main-sidebar { z-index: 1100; height: 100vh; overflow-y: hidden; } .sidebar { padding-bottom: 0; padding-top: 0; padding-left: 0.5rem; padding-right: 0.5rem; overflow-y: auto; height: calc(100% - 4rem); } .user-panel { position: relative; } [class*="sidebar-dark"] .user-panel { border-bottom: 1px solid #4f5962; } [class*="sidebar-light"] .user-panel { border-bottom: 1px solid #dee2e6; } .user-panel, .user-panel .info { overflow: hidden; white-space: nowrap; } .user-panel .image { padding-left: 0.8rem; display: inline-block; } .user-panel img { width: 2.1rem; height: auto; } .user-panel .info { display: inline-block; padding: 5px 5px 5px 10px; } .user-panel .status, .user-panel .dropdown-menu { font-size: 0.875rem; } .nav-sidebar .nav-item > .nav-link { margin-bottom: 0.2rem; } .nav-sidebar .nav-item > .nav-link .right { transition: transform ease-in-out 0.3s; } @media screen and (prefers-reduced-motion: reduce) { .nav-sidebar .nav-item > .nav-link .right { transition: none; } } .nav-sidebar .nav-link > p > .right { position: absolute; right: 1rem; top: 12px; } .nav-sidebar .menu-open > .nav-treeview { display: block; } .nav-sidebar .menu-open > .nav-link .right { -ms-transform: rotate(-90deg); transform: rotate(-90deg); } .nav-sidebar > .nav-item { margin-bottom: 0; } .nav-sidebar > .nav-item .nav-icon { text-align: center; width: 1.6rem; font-size: 1.2rem; margin-right: .2rem; } .nav-sidebar > .nav-item .float-right { margin-top: 3px; } .nav-sidebar .nav-treeview { display: none; list-style: none; padding: 0; } .nav-sidebar .nav-treeview > .nav-item > .nav-link > .nav-icon { width: 1.6rem; } .nav-sidebar .nav-header { font-size: .9rem; padding: 0.5rem; } .nav-sidebar .nav-header:not(:first-of-type) { padding: 1.7rem 1rem .5rem 1rem; } .nav-sidebar .nav-link p { display: inline-block; margin: 0; } #sidebar-overlay { position: fixed; top: 0; left: 0; right: 0; bottom: 0; display: none; background-color: rgba(0, 0, 0, 0.1); z-index: 1099; } @media (max-width: 991.98px) { .sidebar-open #sidebar-overlay { display: block; } } .sidebar-dark-primary { background-color: #343a40; } .sidebar-dark-primary .user-panel a:hover { color: #ffffff; } .sidebar-dark-primary .user-panel .status { color: #C2C7D0; background: rgba(255, 255, 255, 0.1); } .sidebar-dark-primary .user-panel .status:hover, .sidebar-dark-primary .user-panel .status:focus, .sidebar-dark-primary .user-panel .status:active { color: #ffffff; background: rgba(247, 247, 247, 0.1); } .sidebar-dark-primary .user-panel .dropdown-menu { border-color: rgba(242, 242, 242, 0.1); box-shadow: 0 2px 4px rgba(0, 0, 0, 0.4); } .sidebar-dark-primary .user-panel .dropdown-item { color: #212529; } .sidebar-dark-primary .nav-sidebar > .nav-item > .nav-link:active, .sidebar-dark-primary .nav-sidebar > .nav-item > .nav-link:focus { color: #C2C7D0; } .sidebar-dark-primary .nav-sidebar > .nav-item.menu-open > .nav-link, .sidebar-dark-primary .nav-sidebar > .nav-item:hover > .nav-link { color: #ffffff; background-color: rgba(255, 255, 255, 0.1); } .sidebar-dark-primary .nav-sidebar > .nav-item > .nav-link.active { color: #ffffff; background-color: #007bff; } .sidebar-dark-primary .nav-sidebar > .nav-item > .nav-treeview { background: transparent; } .sidebar-dark-primary .nav-header { color: #d0d4db; background: inherit; } .sidebar-dark-primary .sidebar a { color: #C2C7D0; } .sidebar-dark-primary .sidebar a:hover { text-decoration: none; } .sidebar-dark-primary .nav-treeview > .nav-item > .nav-link { color: #C2C7D0; } .sidebar-dark-primary .nav-treeview > .nav-item > .nav-link:hover { color: #ffffff; background-color: rgba(255, 255, 255, 0.1); } .sidebar-dark-primary .nav-treeview > .nav-item > .nav-link.active, .sidebar-dark-primary .nav-treeview > .nav-item > .nav-link.active:hover { color: #343a40; background-color: rgba(255, 255, 255, 0.9); } .sidebar-light-primary { background-color: #ffffff; } .sidebar-light-primary .user-panel a:hover { color: #212529; } .sidebar-light-primary .user-panel .status { color: #343a40; background: #f4f4f5; } .sidebar-light-primary .user-panel .status:hover, .sidebar-light-primary .user-panel .status:focus, .sidebar-light-primary .user-panel .status:active { color: #212529; background: #ececed; } .sidebar-light-primary .user-panel .dropdown-menu { border-color: #e7e7e8; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.4); } .sidebar-light-primary .user-panel .dropdown-item { color: #212529; } .sidebar-light-primary .nav-sidebar > .nav-item > .nav-link:active, .sidebar-light-primary .nav-sidebar > .nav-item > .nav-link:focus { color: #343a40; } .sidebar-light-primary .nav-sidebar > .nav-item.menu-open > .nav-link, .sidebar-light-primary .nav-sidebar > .nav-item:hover > .nav-link { color: #212529; background-color: #f4f4f5; } .sidebar-light-primary .nav-sidebar > .nav-item > .nav-link.active { color: #ffffff; background-color: #007bff; } .sidebar-light-primary .nav-sidebar > .nav-item > .nav-treeview { background: transparent; } .sidebar-light-primary .nav-header { color: #292d32; background: inherit; } .sidebar-light-primary .sidebar a { color: #343a40; } .sidebar-light-primary .sidebar a:hover { text-decoration: none; } .sidebar-light-primary .nav-treeview > .nav-item > .nav-link { color: #777; } .sidebar-light-primary .nav-treeview > .nav-item > .nav-link.active, .sidebar-light-primary .nav-treeview > .nav-item > .nav-link.active:hover { color: #212529; background-color: #f4f4f5; } .sidebar-light-primary .nav-treeview > .nav-item > .nav-link:hover { background-color: #f4f4f5; } .sidebar-dark-secondary { background-color: #343a40; } .sidebar-dark-secondary .user-panel a:hover { color: #ffffff; } .sidebar-dark-secondary .user-panel .status { color: #C2C7D0; background: rgba(255, 255, 255, 0.1); } .sidebar-dark-secondary .user-panel .status:hover, .sidebar-dark-secondary .user-panel .status:focus, .sidebar-dark-secondary .user-panel .status:active { color: #ffffff; background: rgba(247, 247, 247, 0.1); } .sidebar-dark-secondary .user-panel .dropdown-menu { border-color: rgba(242, 242, 242, 0.1); box-shadow: 0 2px 4px rgba(0, 0, 0, 0.4); } .sidebar-dark-secondary .user-panel .dropdown-item { color: #212529; } .sidebar-dark-secondary .nav-sidebar > .nav-item > .nav-link:active, .sidebar-dark-secondary .nav-sidebar > .nav-item > .nav-link:focus { color: #C2C7D0; } .sidebar-dark-secondary .nav-sidebar > .nav-item.menu-open > .nav-link, .sidebar-dark-secondary .nav-sidebar > .nav-item:hover > .nav-link { color: #ffffff; background-color: rgba(255, 255, 255, 0.1); } .sidebar-dark-secondary .nav-sidebar > .nav-item > .nav-link.active { color: #ffffff; background-color: #6c757d; } .sidebar-dark-secondary .nav-sidebar > .nav-item > .nav-treeview { background: transparent; } .sidebar-dark-secondary .nav-header { color: #d0d4db; background: inherit; } .sidebar-dark-secondary .sidebar a { color: #C2C7D0; } .sidebar-dark-secondary .sidebar a:hover { text-decoration: none; } .sidebar-dark-secondary .nav-treeview > .nav-item > .nav-link { color: #C2C7D0; } .sidebar-dark-secondary .nav-treeview > .nav-item > .nav-link:hover { color: #ffffff; background-color: rgba(255, 255, 255, 0.1); } .sidebar-dark-secondary .nav-treeview > .nav-item > .nav-link.active, .sidebar-dark-secondary .nav-treeview > .nav-item > .nav-link.active:hover { color: #343a40; background-color: rgba(255, 255, 255, 0.9); } .sidebar-light-secondary { background-color: #ffffff; } .sidebar-light-secondary .user-panel a:hover { color: #212529; } .sidebar-light-secondary .user-panel .status { color: #343a40; background: #f4f4f5; } .sidebar-light-secondary .user-panel .status:hover, .sidebar-light-secondary .user-panel .status:focus, .sidebar-light-secondary .user-panel .status:active { color: #212529; background: #ececed; } .sidebar-light-secondary .user-panel .dropdown-menu { border-color: #e7e7e8; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.4); } .sidebar-light-secondary .user-panel .dropdown-item { color: #212529; } .sidebar-light-secondary .nav-sidebar > .nav-item > .nav-link:active, .sidebar-light-secondary .nav-sidebar > .nav-item > .nav-link:focus { color: #343a40; } .sidebar-light-secondary .nav-sidebar > .nav-item.menu-open > .nav-link, .sidebar-light-secondary .nav-sidebar > .nav-item:hover > .nav-link { color: #212529; background-color: #f4f4f5; } .sidebar-light-secondary .nav-sidebar > .nav-item > .nav-link.active { color: #ffffff; background-color: #6c757d; } .sidebar-light-secondary .nav-sidebar > .nav-item > .nav-treeview { background: transparent; } .sidebar-light-secondary .nav-header { color: #292d32; background: inherit; } .sidebar-light-secondary .sidebar a { color: #343a40; } .sidebar-light-secondary .sidebar a:hover { text-decoration: none; } .sidebar-light-secondary .nav-treeview > .nav-item > .nav-link { color: #777; } .sidebar-light-secondary .nav-treeview > .nav-item > .nav-link.active, .sidebar-light-secondary .nav-treeview > .nav-item > .nav-link.active:hover { color: #212529; background-color: #f4f4f5; } .sidebar-light-secondary .nav-treeview > .nav-item > .nav-link:hover { background-color: #f4f4f5; } .sidebar-dark-success { background-color: #343a40; } .sidebar-dark-success .user-panel a:hover { color: #ffffff; } .sidebar-dark-success .user-panel .status { color: #C2C7D0; background: rgba(255, 255, 255, 0.1); } .sidebar-dark-success .user-panel .status:hover, .sidebar-dark-success .user-panel .status:focus, .sidebar-dark-success .user-panel .status:active { color: #ffffff; background: rgba(247, 247, 247, 0.1); } .sidebar-dark-success .user-panel .dropdown-menu { border-color: rgba(242, 242, 242, 0.1); box-shadow: 0 2px 4px rgba(0, 0, 0, 0.4); } .sidebar-dark-success .user-panel .dropdown-item { color: #212529; } .sidebar-dark-success .nav-sidebar > .nav-item > .nav-link:active, .sidebar-dark-success .nav-sidebar > .nav-item > .nav-link:focus { color: #C2C7D0; } .sidebar-dark-success .nav-sidebar > .nav-item.menu-open > .nav-link, .sidebar-dark-success .nav-sidebar > .nav-item:hover > .nav-link { color: #ffffff; background-color: rgba(255, 255, 255, 0.1); } .sidebar-dark-success .nav-sidebar > .nav-item > .nav-link.active { color: #ffffff; background-color: #28a745; } .sidebar-dark-success .nav-sidebar > .nav-item > .nav-treeview { background: transparent; } .sidebar-dark-success .nav-header { color: #d0d4db; background: inherit; } .sidebar-dark-success .sidebar a { color: #C2C7D0; } .sidebar-dark-success .sidebar a:hover { text-decoration: none; } .sidebar-dark-success .nav-treeview > .nav-item > .nav-link { color: #C2C7D0; } .sidebar-dark-success .nav-treeview > .nav-item > .nav-link:hover { color: #ffffff; background-color: rgba(255, 255, 255, 0.1); } .sidebar-dark-success .nav-treeview > .nav-item > .nav-link.active, .sidebar-dark-success .nav-treeview > .nav-item > .nav-link.active:hover { color: #343a40; background-color: rgba(255, 255, 255, 0.9); } .sidebar-light-success { background-color: #ffffff; } .sidebar-light-success .user-panel a:hover { color: #212529; } .sidebar-light-success .user-panel .status { color: #343a40; background: #f4f4f5; } .sidebar-light-success .user-panel .status:hover, .sidebar-light-success .user-panel .status:focus, .sidebar-light-success .user-panel .status:active { color: #212529; background: #ececed; } .sidebar-light-success .user-panel .dropdown-menu { border-color: #e7e7e8; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.4); } .sidebar-light-success .user-panel .dropdown-item { color: #212529; } .sidebar-light-success .nav-sidebar > .nav-item > .nav-link:active, .sidebar-light-success .nav-sidebar > .nav-item > .nav-link:focus { color: #343a40; } .sidebar-light-success .nav-sidebar > .nav-item.menu-open > .nav-link, .sidebar-light-success .nav-sidebar > .nav-item:hover > .nav-link { color: #212529; background-color: #f4f4f5; } .sidebar-light-success .nav-sidebar > .nav-item > .nav-link.active { color: #ffffff; background-color: #28a745; } .sidebar-light-success .nav-sidebar > .nav-item > .nav-treeview { background: transparent; } .sidebar-light-success .nav-header { color: #292d32; background: inherit; } .sidebar-light-success .sidebar a { color: #343a40; } .sidebar-light-success .sidebar a:hover { text-decoration: none; } .sidebar-light-success .nav-treeview > .nav-item > .nav-link { color: #777; } .sidebar-light-success .nav-treeview > .nav-item > .nav-link.active, .sidebar-light-success .nav-treeview > .nav-item > .nav-link.active:hover { color: #212529; background-color: #f4f4f5; } .sidebar-light-success .nav-treeview > .nav-item > .nav-link:hover { background-color: #f4f4f5; } .sidebar-dark-info { background-color: #343a40; } .sidebar-dark-info .user-panel a:hover { color: #ffffff; } .sidebar-dark-info .user-panel .status { color: #C2C7D0; background: rgba(255, 255, 255, 0.1); } .sidebar-dark-info .user-panel .status:hover, .sidebar-dark-info .user-panel .status:focus, .sidebar-dark-info .user-panel .status:active { color: #ffffff; background: rgba(247, 247, 247, 0.1); } .sidebar-dark-info .user-panel .dropdown-menu { border-color: rgba(242, 242, 242, 0.1); box-shadow: 0 2px 4px rgba(0, 0, 0, 0.4); } .sidebar-dark-info .user-panel .dropdown-item { color: #212529; } .sidebar-dark-info .nav-sidebar > .nav-item > .nav-link:active, .sidebar-dark-info .nav-sidebar > .nav-item > .nav-link:focus { color: #C2C7D0; } .sidebar-dark-info .nav-sidebar > .nav-item.menu-open > .nav-link, .sidebar-dark-info .nav-sidebar > .nav-item:hover > .nav-link { color: #ffffff; background-color: rgba(255, 255, 255, 0.1); } .sidebar-dark-info .nav-sidebar > .nav-item > .nav-link.active { color: #ffffff; background-color: #17a2b8; } .sidebar-dark-info .nav-sidebar > .nav-item > .nav-treeview { background: transparent; } .sidebar-dark-info .nav-header { color: #d0d4db; background: inherit; } .sidebar-dark-info .sidebar a { color: #C2C7D0; } .sidebar-dark-info .sidebar a:hover { text-decoration: none; } .sidebar-dark-info .nav-treeview > .nav-item > .nav-link { color: #C2C7D0; } .sidebar-dark-info .nav-treeview > .nav-item > .nav-link:hover { color: #ffffff; background-color: rgba(255, 255, 255, 0.1); } .sidebar-dark-info .nav-treeview > .nav-item > .nav-link.active, .sidebar-dark-info .nav-treeview > .nav-item > .nav-link.active:hover { color: #343a40; background-color: rgba(255, 255, 255, 0.9); } .sidebar-light-info { background-color: #ffffff; } .sidebar-light-info .user-panel a:hover { color: #212529; } .sidebar-light-info .user-panel .status { color: #343a40; background: #f4f4f5; } .sidebar-light-info .user-panel .status:hover, .sidebar-light-info .user-panel .status:focus, .sidebar-light-info .user-panel .status:active { color: #212529; background: #ececed; } .sidebar-light-info .user-panel .dropdown-menu { border-color: #e7e7e8; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.4); } .sidebar-light-info .user-panel .dropdown-item { color: #212529; } .sidebar-light-info .nav-sidebar > .nav-item > .nav-link:active, .sidebar-light-info .nav-sidebar > .nav-item > .nav-link:focus { color: #343a40; } .sidebar-light-info .nav-sidebar > .nav-item.menu-open > .nav-link, .sidebar-light-info .nav-sidebar > .nav-item:hover > .nav-link { color: #212529; background-color: #f4f4f5; } .sidebar-light-info .nav-sidebar > .nav-item > .nav-link.active { color: #ffffff; background-color: #17a2b8; } .sidebar-light-info .nav-sidebar > .nav-item > .nav-treeview { background: transparent; } .sidebar-light-info .nav-header { color: #292d32; background: inherit; } .sidebar-light-info .sidebar a { color: #343a40; } .sidebar-light-info .sidebar a:hover { text-decoration: none; } .sidebar-light-info .nav-treeview > .nav-item > .nav-link { color: #777; } .sidebar-light-info .nav-treeview > .nav-item > .nav-link.active, .sidebar-light-info .nav-treeview > .nav-item > .nav-link.active:hover { color: #212529; background-color: #f4f4f5; } .sidebar-light-info .nav-treeview > .nav-item > .nav-link:hover { background-color: #f4f4f5; } .sidebar-dark-warning { background-color: #343a40; } .sidebar-dark-warning .user-panel a:hover { color: #ffffff; } .sidebar-dark-warning .user-panel .status { color: #C2C7D0; background: rgba(255, 255, 255, 0.1); } .sidebar-dark-warning .user-panel .status:hover, .sidebar-dark-warning .user-panel .status:focus, .sidebar-dark-warning .user-panel .status:active { color: #ffffff; background: rgba(247, 247, 247, 0.1); } .sidebar-dark-warning .user-panel .dropdown-menu { border-color: rgba(242, 242, 242, 0.1); box-shadow: 0 2px 4px rgba(0, 0, 0, 0.4); } .sidebar-dark-warning .user-panel .dropdown-item { color: #212529; } .sidebar-dark-warning .nav-sidebar > .nav-item > .nav-link:active, .sidebar-dark-warning .nav-sidebar > .nav-item > .nav-link:focus { color: #C2C7D0; } .sidebar-dark-warning .nav-sidebar > .nav-item.menu-open > .nav-link, .sidebar-dark-warning .nav-sidebar > .nav-item:hover > .nav-link { color: #ffffff; background-color: rgba(255, 255, 255, 0.1); } .sidebar-dark-warning .nav-sidebar > .nav-item > .nav-link.active { color: #ffffff; background-color: #ffc107; } .sidebar-dark-warning .nav-sidebar > .nav-item > .nav-treeview { background: transparent; } .sidebar-dark-warning .nav-header { color: #d0d4db; background: inherit; } .sidebar-dark-warning .sidebar a { color: #C2C7D0; } .sidebar-dark-warning .sidebar a:hover { text-decoration: none; } .sidebar-dark-warning .nav-treeview > .nav-item > .nav-link { color: #C2C7D0; } .sidebar-dark-warning .nav-treeview > .nav-item > .nav-link:hover { color: #ffffff; background-color: rgba(255, 255, 255, 0.1); } .sidebar-dark-warning .nav-treeview > .nav-item > .nav-link.active, .sidebar-dark-warning .nav-treeview > .nav-item > .nav-link.active:hover { color: #343a40; background-color: rgba(255, 255, 255, 0.9); } .sidebar-light-warning { background-color: #ffffff; } .sidebar-light-warning .user-panel a:hover { color: #212529; } .sidebar-light-warning .user-panel .status { color: #343a40; background: #f4f4f5; } .sidebar-light-warning .user-panel .status:hover, .sidebar-light-warning .user-panel .status:focus, .sidebar-light-warning .user-panel .status:active { color: #212529; background: #ececed; } .sidebar-light-warning .user-panel .dropdown-menu { border-color: #e7e7e8; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.4); } .sidebar-light-warning .user-panel .dropdown-item { color: #212529; } .sidebar-light-warning .nav-sidebar > .nav-item > .nav-link:active, .sidebar-light-warning .nav-sidebar > .nav-item > .nav-link:focus { color: #343a40; } .sidebar-light-warning .nav-sidebar > .nav-item.menu-open > .nav-link, .sidebar-light-warning .nav-sidebar > .nav-item:hover > .nav-link { color: #212529; background-color: #f4f4f5; } .sidebar-light-warning .nav-sidebar > .nav-item > .nav-link.active { color: #ffffff; background-color: #ffc107; } .sidebar-light-warning .nav-sidebar > .nav-item > .nav-treeview { background: transparent; } .sidebar-light-warning .nav-header { color: #292d32; background: inherit; } .sidebar-light-warning .sidebar a { color: #343a40; } .sidebar-light-warning .sidebar a:hover { text-decoration: none; } .sidebar-light-warning .nav-treeview > .nav-item > .nav-link { color: #777; } .sidebar-light-warning .nav-treeview > .nav-item > .nav-link.active, .sidebar-light-warning .nav-treeview > .nav-item > .nav-link.active:hover { color: #212529; background-color: #f4f4f5; } .sidebar-light-warning .nav-treeview > .nav-item > .nav-link:hover { background-color: #f4f4f5; } .sidebar-dark-danger { background-color: #343a40; } .sidebar-dark-danger .user-panel a:hover { color: #ffffff; } .sidebar-dark-danger .user-panel .status { color: #C2C7D0; background: rgba(255, 255, 255, 0.1); } .sidebar-dark-danger .user-panel .status:hover, .sidebar-dark-danger .user-panel .status:focus, .sidebar-dark-danger .user-panel .status:active { color: #ffffff; background: rgba(247, 247, 247, 0.1); } .sidebar-dark-danger .user-panel .dropdown-menu { border-color: rgba(242, 242, 242, 0.1); box-shadow: 0 2px 4px rgba(0, 0, 0, 0.4); } .sidebar-dark-danger .user-panel .dropdown-item { color: #212529; } .sidebar-dark-danger .nav-sidebar > .nav-item > .nav-link:active, .sidebar-dark-danger .nav-sidebar > .nav-item > .nav-link:focus { color: #C2C7D0; } .sidebar-dark-danger .nav-sidebar > .nav-item.menu-open > .nav-link, .sidebar-dark-danger .nav-sidebar > .nav-item:hover > .nav-link { color: #ffffff; background-color: rgba(255, 255, 255, 0.1); } .sidebar-dark-danger .nav-sidebar > .nav-item > .nav-link.active { color: #ffffff; background-color: #dc3545; } .sidebar-dark-danger .nav-sidebar > .nav-item > .nav-treeview { background: transparent; } .sidebar-dark-danger .nav-header { color: #d0d4db; background: inherit; } .sidebar-dark-danger .sidebar a { color: #C2C7D0; } .sidebar-dark-danger .sidebar a:hover { text-decoration: none; } .sidebar-dark-danger .nav-treeview > .nav-item > .nav-link { color: #C2C7D0; } .sidebar-dark-danger .nav-treeview > .nav-item > .nav-link:hover { color: #ffffff; background-color: rgba(255, 255, 255, 0.1); } .sidebar-dark-danger .nav-treeview > .nav-item > .nav-link.active, .sidebar-dark-danger .nav-treeview > .nav-item > .nav-link.active:hover { color: #343a40; background-color: rgba(255, 255, 255, 0.9); } .sidebar-light-danger { background-color: #ffffff; } .sidebar-light-danger .user-panel a:hover { color: #212529; } .sidebar-light-danger .user-panel .status { color: #343a40; background: #f4f4f5; } .sidebar-light-danger .user-panel .status:hover, .sidebar-light-danger .user-panel .status:focus, .sidebar-light-danger .user-panel .status:active { color: #212529; background: #ececed; } .sidebar-light-danger .user-panel .dropdown-menu { border-color: #e7e7e8; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.4); } .sidebar-light-danger .user-panel .dropdown-item { color: #212529; } .sidebar-light-danger .nav-sidebar > .nav-item > .nav-link:active, .sidebar-light-danger .nav-sidebar > .nav-item > .nav-link:focus { color: #343a40; } .sidebar-light-danger .nav-sidebar > .nav-item.menu-open > .nav-link, .sidebar-light-danger .nav-sidebar > .nav-item:hover > .nav-link { color: #212529; background-color: #f4f4f5; } .sidebar-light-danger .nav-sidebar > .nav-item > .nav-link.active { color: #ffffff; background-color: #dc3545; } .sidebar-light-danger .nav-sidebar > .nav-item > .nav-treeview { background: transparent; } .sidebar-light-danger .nav-header { color: #292d32; background: inherit; } .sidebar-light-danger .sidebar a { color: #343a40; } .sidebar-light-danger .sidebar a:hover { text-decoration: none; } .sidebar-light-danger .nav-treeview > .nav-item > .nav-link { color: #777; } .sidebar-light-danger .nav-treeview > .nav-item > .nav-link.active, .sidebar-light-danger .nav-treeview > .nav-item > .nav-link.active:hover { color: #212529; background-color: #f4f4f5; } .sidebar-light-danger .nav-treeview > .nav-item > .nav-link:hover { background-color: #f4f4f5; } .sidebar-dark-light { background-color: #343a40; } .sidebar-dark-light .user-panel a:hover { color: #ffffff; } .sidebar-dark-light .user-panel .status { color: #C2C7D0; background: rgba(255, 255, 255, 0.1); } .sidebar-dark-light .user-panel .status:hover, .sidebar-dark-light .user-panel .status:focus, .sidebar-dark-light .user-panel .status:active { color: #ffffff; background: rgba(247, 247, 247, 0.1); } .sidebar-dark-light .user-panel .dropdown-menu { border-color: rgba(242, 242, 242, 0.1); box-shadow: 0 2px 4px rgba(0, 0, 0, 0.4); } .sidebar-dark-light .user-panel .dropdown-item { color: #212529; } .sidebar-dark-light .nav-sidebar > .nav-item > .nav-link:active, .sidebar-dark-light .nav-sidebar > .nav-item > .nav-link:focus { color: #C2C7D0; } .sidebar-dark-light .nav-sidebar > .nav-item.menu-open > .nav-link, .sidebar-dark-light .nav-sidebar > .nav-item:hover > .nav-link { color: #ffffff; background-color: rgba(255, 255, 255, 0.1); } .sidebar-dark-light .nav-sidebar > .nav-item > .nav-link.active { color: #ffffff; background-color: #f8f9fa; } .sidebar-dark-light .nav-sidebar > .nav-item > .nav-treeview { background: transparent; } .sidebar-dark-light .nav-header { color: #d0d4db; background: inherit; } .sidebar-dark-light .sidebar a { color: #C2C7D0; } .sidebar-dark-light .sidebar a:hover { text-decoration: none; } .sidebar-dark-light .nav-treeview > .nav-item > .nav-link { color: #C2C7D0; } .sidebar-dark-light .nav-treeview > .nav-item > .nav-link:hover { color: #ffffff; background-color: rgba(255, 255, 255, 0.1); } .sidebar-dark-light .nav-treeview > .nav-item > .nav-link.active, .sidebar-dark-light .nav-treeview > .nav-item > .nav-link.active:hover { color: #343a40; background-color: rgba(255, 255, 255, 0.9); } .sidebar-light-light { background-color: #ffffff; } .sidebar-light-light .user-panel a:hover { color: #212529; } .sidebar-light-light .user-panel .status { color: #343a40; background: #f4f4f5; } .sidebar-light-light .user-panel .status:hover, .sidebar-light-light .user-panel .status:focus, .sidebar-light-light .user-panel .status:active { color: #212529; background: #ececed; } .sidebar-light-light .user-panel .dropdown-menu { border-color: #e7e7e8; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.4); } .sidebar-light-light .user-panel .dropdown-item { color: #212529; } .sidebar-light-light .nav-sidebar > .nav-item > .nav-link:active, .sidebar-light-light .nav-sidebar > .nav-item > .nav-link:focus { color: #343a40; } .sidebar-light-light .nav-sidebar > .nav-item.menu-open > .nav-link, .sidebar-light-light .nav-sidebar > .nav-item:hover > .nav-link { color: #212529; background-color: #f4f4f5; } .sidebar-light-light .nav-sidebar > .nav-item > .nav-link.active { color: #ffffff; background-color: #f8f9fa; } .sidebar-light-light .nav-sidebar > .nav-item > .nav-treeview { background: transparent; } .sidebar-light-light .nav-header { color: #292d32; background: inherit; } .sidebar-light-light .sidebar a { color: #343a40; } .sidebar-light-light .sidebar a:hover { text-decoration: none; } .sidebar-light-light .nav-treeview > .nav-item > .nav-link { color: #777; } .sidebar-light-light .nav-treeview > .nav-item > .nav-link.active, .sidebar-light-light .nav-treeview > .nav-item > .nav-link.active:hover { color: #212529; background-color: #f4f4f5; } .sidebar-light-light .nav-treeview > .nav-item > .nav-link:hover { background-color: #f4f4f5; } .sidebar-dark-dark { background-color: #343a40; } .sidebar-dark-dark .user-panel a:hover { color: #ffffff; } .sidebar-dark-dark .user-panel .status { color: #C2C7D0; background: rgba(255, 255, 255, 0.1); } .sidebar-dark-dark .user-panel .status:hover, .sidebar-dark-dark .user-panel .status:focus, .sidebar-dark-dark .user-panel .status:active { color: #ffffff; background: rgba(247, 247, 247, 0.1); } .sidebar-dark-dark .user-panel .dropdown-menu { border-color: rgba(242, 242, 242, 0.1); box-shadow: 0 2px 4px rgba(0, 0, 0, 0.4); } .sidebar-dark-dark .user-panel .dropdown-item { color: #212529; } .sidebar-dark-dark .nav-sidebar > .nav-item > .nav-link:active, .sidebar-dark-dark .nav-sidebar > .nav-item > .nav-link:focus { color: #C2C7D0; } .sidebar-dark-dark .nav-sidebar > .nav-item.menu-open > .nav-link, .sidebar-dark-dark .nav-sidebar > .nav-item:hover > .nav-link { color: #ffffff; background-color: rgba(255, 255, 255, 0.1); } .sidebar-dark-dark .nav-sidebar > .nav-item > .nav-link.active { color: #ffffff; background-color: #343a40; } .sidebar-dark-dark .nav-sidebar > .nav-item > .nav-treeview { background: transparent; } .sidebar-dark-dark .nav-header { color: #d0d4db; background: inherit; } .sidebar-dark-dark .sidebar a { color: #C2C7D0; } .sidebar-dark-dark .sidebar a:hover { text-decoration: none; } .sidebar-dark-dark .nav-treeview > .nav-item > .nav-link { color: #C2C7D0; } .sidebar-dark-dark .nav-treeview > .nav-item > .nav-link:hover { color: #ffffff; background-color: rgba(255, 255, 255, 0.1); } .sidebar-dark-dark .nav-treeview > .nav-item > .nav-link.active, .sidebar-dark-dark .nav-treeview > .nav-item > .nav-link.active:hover { color: #343a40; background-color: rgba(255, 255, 255, 0.9); } .sidebar-light-dark { background-color: #ffffff; } .sidebar-light-dark .user-panel a:hover { color: #212529; } .sidebar-light-dark .user-panel .status { color: #343a40; background: #f4f4f5; } .sidebar-light-dark .user-panel .status:hover, .sidebar-light-dark .user-panel .status:focus, .sidebar-light-dark .user-panel .status:active { color: #212529; background: #ececed; } .sidebar-light-dark .user-panel .dropdown-menu { border-color: #e7e7e8; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.4); } .sidebar-light-dark .user-panel .dropdown-item { color: #212529; } .sidebar-light-dark .nav-sidebar > .nav-item > .nav-link:active, .sidebar-light-dark .nav-sidebar > .nav-item > .nav-link:focus { color: #343a40; } .sidebar-light-dark .nav-sidebar > .nav-item.menu-open > .nav-link, .sidebar-light-dark .nav-sidebar > .nav-item:hover > .nav-link { color: #212529; background-color: #f4f4f5; } .sidebar-light-dark .nav-sidebar > .nav-item > .nav-link.active { color: #ffffff; background-color: #343a40; } .sidebar-light-dark .nav-sidebar > .nav-item > .nav-treeview { background: transparent; } .sidebar-light-dark .nav-header { color: #292d32; background: inherit; } .sidebar-light-dark .sidebar a { color: #343a40; } .sidebar-light-dark .sidebar a:hover { text-decoration: none; } .sidebar-light-dark .nav-treeview > .nav-item > .nav-link { color: #777; } .sidebar-light-dark .nav-treeview > .nav-item > .nav-link.active, .sidebar-light-dark .nav-treeview > .nav-item > .nav-link.active:hover { color: #212529; background-color: #f4f4f5; } .sidebar-light-dark .nav-treeview > .nav-item > .nav-link:hover { background-color: #f4f4f5; } /* * Component: Sidebar Mini */ @media (min-width: 992px) { .sidebar-mini .nav-sidebar, .sidebar-mini .nav-sidebar > .nav-header, .sidebar-mini .nav-sidebar .nav-link { white-space: nowrap; overflow: hidden; } .sidebar-mini.sidebar-collapse .d-hidden-mini { display: none; } .sidebar-mini.sidebar-collapse .content-wrapper, .sidebar-mini.sidebar-collapse .main-footer, .sidebar-mini.sidebar-collapse .main-header { margin-left: 4.6rem !important; } .sidebar-mini.sidebar-collapse .nav-sidebar .nav-header { display: none; } .sidebar-mini.sidebar-collapse .sidebar .user-panel > .info, .sidebar-mini.sidebar-collapse .nav-sidebar .nav-link p, .sidebar-mini.sidebar-collapse .brand-text { opacity: 0; margin-left: -10px; } .sidebar-mini.sidebar-collapse .main-sidebar, .sidebar-mini.sidebar-collapse .main-sidebar:before { margin-left: 0; width: 4.6rem; } .sidebar-mini.sidebar-collapse .main-sidebar .user-panel .image { float: none; } .sidebar-mini.sidebar-collapse .main-sidebar:hover { width: 250px; } .sidebar-mini.sidebar-collapse .main-sidebar:hover .user-panel { text-align: left; } .sidebar-mini.sidebar-collapse .main-sidebar:hover .user-panel .image { float: left; } .sidebar-mini.sidebar-collapse .main-sidebar:hover .user-panel > .info, .sidebar-mini.sidebar-collapse .main-sidebar:hover .nav-sidebar .nav-link p, .sidebar-mini.sidebar-collapse .main-sidebar:hover .brand-text { opacity: 1; margin-left: 0; display: inline-block; } .sidebar-mini.sidebar-collapse .main-sidebar:hover .brand-image { margin-right: .5rem; } .sidebar-mini.sidebar-collapse .main-sidebar:hover .sidebar-form, .sidebar-mini.sidebar-collapse .main-sidebar:hover .user-panel > .info { display: block !important; -webkit-transform: translateZ(0); } .sidebar-mini.sidebar-collapse .main-sidebar:hover .nav-sidebar > .nav-item > .nav-link > span { display: inline-block !important; } .sidebar-mini.sidebar-collapse .visible-sidebar-mini { display: block !important; } } .nav-sidebar { position: relative; } .nav-sidebar:hover { overflow: visible; } .sidebar-form, .nav-sidebar > .nav-header { overflow: hidden; text-overflow: clip; } .nav-sidebar .nav-item > .nav-link { position: relative; } .nav-sidebar .nav-item > .nav-link > .float-right { position: absolute; right: 10px; top: 50%; margin-top: -7px; } .sidebar .nav-link p, .main-sidebar .brand-text, .sidebar .user-panel .info { transition: margin-left 0.3s linear, opacity 0.5s ease; } @media screen and (prefers-reduced-motion: reduce) { .sidebar .nav-link p, .main-sidebar .brand-text, .sidebar .user-panel .info { transition: none; } } /* * Component: Control sidebar. By default, this is the right sidebar. */ .control-sidebar { position: absolute; top: 2.5rem; z-index: 830; } .control-sidebar, .control-sidebar:before { width: 250px; right: -250px; bottom: 0; transition: right 0.3s ease-in-out; } @media screen and (prefers-reduced-motion: reduce) { .control-sidebar, .control-sidebar:before { transition: none; } } .control-sidebar:before { top: 0; display: block; position: fixed; content: " "; z-index: -1; } @media (min-width: 768px) { .control-sidebar-open .control-sidebar, .control-sidebar-open .control-sidebar:before { right: 0; } .control-sidebar-open .content-wrapper, .control-sidebar-open .main-footer { margin-right: 250px; } } @media (max-width: 991.98px) { .control-sidebar-open .control-sidebar, .control-sidebar-open .control-sidebar:before { right: 0; } } .control-sidebar-slide-open .control-sidebar, .control-sidebar-slide-open .control-sidebar:before { right: 0; } .control-sidebar-dark, .control-sidebar-dark a, .control-sidebar-dark .nav-link { color: #C2C7D0; } .control-sidebar-dark, .control-sidebar-dark:before { background: #343a40; } .control-sidebar-dark a:hover { color: #ffffff; } .control-sidebar-dark h1, .control-sidebar-dark h2, .control-sidebar-dark h3, .control-sidebar-dark h4, .control-sidebar-dark h5, .control-sidebar-dark h6, .control-sidebar-dark label { color: #ffffff; } .control-sidebar-dark .nav-tabs { border-bottom: 0; background-color: rgba(255, 255, 255, 0.1); margin-bottom: 5px; } .control-sidebar-dark .nav-tabs .nav-item { margin: 0; } .control-sidebar-dark .nav-tabs .nav-link { position: relative; border-radius: 0; text-align: center; padding: 10px 20px; } .control-sidebar-dark .nav-tabs .nav-link, .control-sidebar-dark .nav-tabs .nav-link:hover, .control-sidebar-dark .nav-tabs .nav-link:active, .control-sidebar-dark .nav-tabs .nav-link:focus, .control-sidebar-dark .nav-tabs .nav-link.active { border: 0; } .control-sidebar-dark .nav-tabs .nav-link:hover, .control-sidebar-dark .nav-tabs .nav-link:active, .control-sidebar-dark .nav-tabs .nav-link:focus, .control-sidebar-dark .nav-tabs .nav-link.active { border-left-color: transparent; border-bottom-color: transparent; border-top-color: transparent; color: #ffffff; } .control-sidebar-dark .nav-tabs .nav-link.active { background-color: #343a40; } .control-sidebar-dark .tab-pane { padding: 10px 15px; } .control-sidebar-light { color: #4b545c; } .control-sidebar-light, .control-sidebar-light:before { background: #ffffff; border-left: 1px solid #adb5bd; } /* * Component: Dropdown menus * ------------------------- */ .dropdown-item-title { font-size: 1rem; margin: 0; } .dropdown-menu-lg { min-width: 280px; max-width: 300px; padding: 0; } .dropdown-menu-lg .dropdown-divider { margin: 0; } .dropdown-menu-lg .dropdown-item { padding: 0.5rem 1rem; } .dropdown-menu-lg p { white-space: normal; margin: 0; } .dropdown-footer, .dropdown-header { text-align: center; display: block; padding: 0.5rem 1rem; font-size: 0.875rem; } /* Add fade animation to dropdown menus by appending the class .animated-dropdown-menu to the .dropdown-menu ul (or ol)*/ .open:not(.dropup) > .animated-dropdown-menu { backface-visibility: visible !important; animation: flipInX 0.7s both; } @keyframes flipInX { 0% { transform: perspective(400px) rotate3d(1, 0, 0, 90deg); transition-timing-function: ease-in; opacity: 0; } 40% { transform: perspective(400px) rotate3d(1, 0, 0, -20deg); transition-timing-function: ease-in; } 60% { transform: perspective(400px) rotate3d(1, 0, 0, 10deg); opacity: 1; } 80% { transform: perspective(400px) rotate3d(1, 0, 0, -5deg); } 100% { transform: perspective(400px); } } @-webkit-keyframes flipInX { 0% { -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 90deg); -webkit-transition-timing-function: ease-in; opacity: 0; } 40% { -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -20deg); -webkit-transition-timing-function: ease-in; } 60% { -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 10deg); opacity: 1; } 80% { -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -5deg); } 100% { -webkit-transform: perspective(400px); } } /* Fix dropdown menu in navbars */ .navbar-custom-menu > .navbar-nav > li { position: relative; } .navbar-custom-menu > .navbar-nav > li > .dropdown-menu { position: absolute; right: 0; left: auto; } @media (max-width: 767.98px) { .navbar-custom-menu > .navbar-nav { float: right; } .navbar-custom-menu > .navbar-nav > li { position: static; } .navbar-custom-menu > .navbar-nav > li > .dropdown-menu { position: absolute; right: 5%; left: auto; border: 1px solid #ddd; background: #ffffff; } } /* * Component: Form * --------------- */ .form-group.has-icon { position: relative; } .form-group.has-icon .form-control { padding-right: 35px; } .form-group.has-icon .form-icon { cursor: pointer; position: absolute; right: 3px; top: 0; padding: 0.375rem 0.75rem; border: 0; background-color: transparent; font-size: 1rem; } /* button groups */ .btn-group-vertical .btn.btn-flat:first-of-type, .btn-group-vertical .btn.btn-flat:last-of-type { border-radius: 0; } /* Support Font Awesome icons in form-control */ .form-control-feedback.fa { line-height: calc(2.25rem + 2px); } .input-lg + .form-control-feedback.fa, .input-group-lg + .form-control-feedback.fa, .form-group-lg .form-control + .form-control-feedback.fa { line-height: calc(2.875rem + 2px); } .input-sm + .form-control-feedback.fa, .input-group-sm + .form-control-feedback.fa, .form-group-sm .form-control + .form-control-feedback.fa { line-height: calc(1.8125rem + 2px); } label:not(.form-check-label):not(.custom-file-label) { font-weight: 700; } /* * Component: Progress Bar * ----------------------- */ .progress { box-shadow: none; border-radius: 1px; } .progress-sm { height: 10px; } .progress-xs { height: 7px; } .progress-xxs { height: 3px; } .progress.vertical { position: relative; width: 30px; height: 200px; display: inline-block; margin-right: 10px; } .progress.vertical > .progress-bar { width: 100%; position: absolute; bottom: 0; } .progress.vertical.sm, .progress.vertical.progress-sm { width: 20px; } .progress.vertical.xs, .progress.vertical.progress-xs { width: 10px; } .progress.vertical.xxs, .progress.vertical.progress-xxs { width: 3px; } .table tr > td .progress { margin: 0; } /* * Component: Small Box * -------------------- */ .small-box { border-radius: 0.25rem; box-shadow: 0 0 1px rgba(0, 0, 0, 0.125), 0 1px 3px rgba(0, 0, 0, 0.2); position: relative; display: block; margin-bottom: 20px; } .small-box > .inner { padding: 10px; } .small-box > .small-box-footer { position: relative; text-align: center; padding: 3px 0; color: #ffffff; color: rgba(255, 255, 255, 0.8); display: block; z-index: 10; background: rgba(0, 0, 0, 0.1); text-decoration: none; } .small-box > .small-box-footer:hover { color: #ffffff; background: rgba(0, 0, 0, 0.15); } .small-box h3 { font-size: 38px; font-weight: bold; margin: 0 0 10px 0; white-space: nowrap; padding: 0; } .small-box p { font-size: 15px; } .small-box p > small { display: block; color: #f9f9f9; font-size: 13px; margin-top: 5px; } .small-box h3, .small-box p { z-index: 5; } .small-box .icon { transition: all 0.3s linear; position: absolute; top: -10px; right: 10px; z-index: 0; font-size: 90px; color: rgba(0, 0, 0, 0.15); } .small-box:hover { text-decoration: none; } .small-box:hover .icon { font-size: 95px; } @media (max-width: 767.98px) { .small-box { text-align: center; } .small-box .icon { display: none; } .small-box p { font-size: 12px; } } /* * Component: Box * -------------- */ .card { box-shadow: 0 0 1px rgba(0, 0, 0, 0.125), 0 1px 3px rgba(0, 0, 0, 0.2); } .card.bg-dark, .card.bg-dark .card-body { color: #ffffff; } .card.collapsed-card .card-body, .card.collapsed-card .card-footer { display: none; } .card .nav.flex-column > li { border-bottom: 1px solid rgba(0, 0, 0, 0.125); margin: 0; } .card .nav.flex-column > li:last-of-type { border-bottom: none; } .card.height-control .card-body { max-height: 300px; overflow: auto; } .card .border-right { border-right: 1px solid rgba(0, 0, 0, 0.125); } .card .border-left { border-left: 1px solid rgba(0, 0, 0, 0.125); } .card > .overlay, .card > .loading-img, .overlay-wrapper > .overlay, .overlay-wrapper > .loading-img { position: absolute; top: 0; left: 0; width: 100%; height: 100%; } .card .overlay, .overlay-wrapper .overlay { z-index: 50; background: rgba(255, 255, 255, 0.7); border-radius: 0.25rem; } .card .overlay > .fa, .overlay-wrapper .overlay > .fa { position: absolute; top: 50%; left: 50%; margin-left: -15px; margin-top: -15px; color: #000; font-size: 30px; } .card .overlay.dark, .overlay-wrapper .overlay.dark { background: rgba(0, 0, 0, 0.5); } .card-header::after, .card-body::after, .card-footer::after { display: block; clear: both; content: ""; } .card-header { position: relative; background-color: transparent; border-bottom: 1px solid rgba(0, 0, 0, 0.125); border-top-left-radius: 0.25rem; border-top-right-radius: 0.25rem; } .collapsed-card .card-header { border-bottom: none; } .card-header > .card-tools { position: absolute; right: 1rem; top: .5rem; } .card-header > .card-tools [data-toggle="tooltip"] { position: relative; } .card-title { font-size: 1.25rem; font-weight: 400; margin: 0; } .btn-tool { padding: .25rem .5rem; font-size: 0.875rem; background: transparent; color: #adb5bd; } .btn-group.show .btn-tool, .btn-tool:hover { color: #495057; } .show .btn-tool, .btn-tool:focus { box-shadow: none !important; } .card-body > .table { margin-bottom: 0; } .card-body > .table > thead > tr > th, .card-body > .table > thead > tr > td { border-top-width: 0; } .card-body .fc { margin-top: 5px; } .card-body .full-width-chart { margin: -19px; } .card-body.p-0 .full-width-chart { margin: -9px; } .chart-legend { margin: 10px 0; } @media (max-width: 576px) { .chart-legend > li { float: left; margin-right: 10px; } } .card-comments { background: #f7f7f7; } .card-comments .card-comment { padding: 8px 0; border-bottom: 1px solid #eee; } .card-comments .card-comment::after { display: block; clear: both; content: ""; } .card-comments .card-comment:last-of-type { border-bottom: 0; } .card-comments .card-comment:first-of-type { padding-top: 0; } .card-comments .card-comment img { float: left; } .card-comments .comment-text { margin-left: 40px; color: #555; } .card-comments .username { color: #444; display: block; font-weight: 600; } .card-comments .text-muted { font-weight: 400; font-size: 12px; } .todo-list { margin: 0; padding: 0; list-style: none; overflow: auto; } .todo-list > li { border-radius: 2px; padding: 10px; background: #f4f4f4; margin-bottom: 2px; border-left: 2px solid #e6e7e8; color: #444; } .todo-list > li:last-of-type { margin-bottom: 0; } .todo-list > li > input[type='checkbox'] { margin: 0 10px 0 5px; } .todo-list > li .text { display: inline-block; margin-left: 5px; font-weight: 600; } .todo-list > li .label { margin-left: 10px; font-size: 9px; } .todo-list > li .tools { display: none; float: right; color: #dc3545; } .todo-list > li .tools > .fa, .todo-list > li .tools > .glyphicon, .todo-list > li .tools > .ion { margin-right: 5px; cursor: pointer; } .todo-list > li:hover .tools { display: inline-block; } .todo-list > li.done { color: #999; } .todo-list > li.done .text { text-decoration: line-through; font-weight: 500; } .todo-list > li.done .label { background: #adb5bd !important; } .todo-list .danger { border-left-color: #dc3545; } .todo-list .warning { border-left-color: #ffc107; } .todo-list .info { border-left-color: #17a2b8; } .todo-list .success { border-left-color: #28a745; } .todo-list .primary { border-left-color: #007bff; } .todo-list .handle { display: inline-block; cursor: move; margin: 0 5px; } .card-input { max-width: 200px; } .card-primary:not(.card-outline) .card-header { background-color: #007bff; border-bottom: 0; } .card-primary:not(.card-outline) .card-header, .card-primary:not(.card-outline) .card-header a { color: #ffffff; } .card-primary.card-outline { border-top: 3px solid #007bff; } .bg-primary .btn-tool, .label-primary .btn-tool, .bg-primary-gradient .btn-tool, .card-primary:not(.card-outline) .btn-tool { color: rgba(255, 255, 255, 0.8); } .bg-primary .btn-tool:hover, .label-primary .btn-tool:hover, .bg-primary-gradient .btn-tool:hover, .card-primary:not(.card-outline) .btn-tool:hover { color: #ffffff; } .card-secondary:not(.card-outline) .card-header { background-color: #6c757d; border-bottom: 0; } .card-secondary:not(.card-outline) .card-header, .card-secondary:not(.card-outline) .card-header a { color: #ffffff; } .card-secondary.card-outline { border-top: 3px solid #6c757d; } .bg-secondary .btn-tool, .bg-secondary-gradient .btn-tool, .card-secondary:not(.card-outline) .btn-tool { color: rgba(255, 255, 255, 0.8); } .bg-secondary .btn-tool:hover, .bg-secondary-gradient .btn-tool:hover, .card-secondary:not(.card-outline) .btn-tool:hover { color: #ffffff; } .card-success:not(.card-outline) .card-header { background-color: #28a745; border-bottom: 0; } .card-success:not(.card-outline) .card-header, .card-success:not(.card-outline) .card-header a { color: #ffffff; } .card-success.card-outline { border-top: 3px solid #28a745; } .bg-success .btn-tool, .alert-success .btn-tool, .label-success .btn-tool, .bg-success-gradient .btn-tool, .card-success:not(.card-outline) .btn-tool { color: rgba(255, 255, 255, 0.8); } .bg-success .btn-tool:hover, .alert-success .btn-tool:hover, .label-success .btn-tool:hover, .bg-success-gradient .btn-tool:hover, .card-success:not(.card-outline) .btn-tool:hover { color: #ffffff; } .card-info:not(.card-outline) .card-header { background-color: #17a2b8; border-bottom: 0; } .card-info:not(.card-outline) .card-header, .card-info:not(.card-outline) .card-header a { color: #ffffff; } .card-info.card-outline { border-top: 3px solid #17a2b8; } .bg-info .btn-tool, .alert-info .btn-tool, .label-info .btn-tool, .bg-info-gradient .btn-tool, .card-info:not(.card-outline) .btn-tool { color: rgba(255, 255, 255, 0.8); } .bg-info .btn-tool:hover, .alert-info .btn-tool:hover, .label-info .btn-tool:hover, .bg-info-gradient .btn-tool:hover, .card-info:not(.card-outline) .btn-tool:hover { color: #ffffff; } .card-warning:not(.card-outline) .card-header { background-color: #ffc107; border-bottom: 0; } .card-warning:not(.card-outline) .card-header, .card-warning:not(.card-outline) .card-header a { color: #1F2D3D; } .card-warning.card-outline { border-top: 3px solid #ffc107; } .bg-warning .btn-tool, .alert-warning .btn-tool, .label-warning .btn-tool, .bg-warning-gradient .btn-tool, .card-warning:not(.card-outline) .btn-tool { color: rgba(31, 45, 61, 0.8); } .bg-warning .btn-tool:hover, .alert-warning .btn-tool:hover, .label-warning .btn-tool:hover, .bg-warning-gradient .btn-tool:hover, .card-warning:not(.card-outline) .btn-tool:hover { color: #1F2D3D; } .card-danger:not(.card-outline) .card-header { background-color: #dc3545; border-bottom: 0; } .card-danger:not(.card-outline) .card-header, .card-danger:not(.card-outline) .card-header a { color: #ffffff; } .card-danger.card-outline { border-top: 3px solid #dc3545; } .bg-danger .btn-tool, .alert-danger .btn-tool, .alert-error .btn-tool, .label-danger .btn-tool, .bg-danger-gradient .btn-tool, .card-danger:not(.card-outline) .btn-tool { color: rgba(255, 255, 255, 0.8); } .bg-danger .btn-tool:hover, .alert-danger .btn-tool:hover, .alert-error .btn-tool:hover, .label-danger .btn-tool:hover, .bg-danger-gradient .btn-tool:hover, .card-danger:not(.card-outline) .btn-tool:hover { color: #ffffff; } .card-light:not(.card-outline) .card-header { background-color: #f8f9fa; border-bottom: 0; } .card-light:not(.card-outline) .card-header, .card-light:not(.card-outline) .card-header a { color: #1F2D3D; } .card-light.card-outline { border-top: 3px solid #f8f9fa; } .bg-light .btn-tool, .bg-light-gradient .btn-tool, .card-light:not(.card-outline) .btn-tool { color: rgba(31, 45, 61, 0.8); } .bg-light .btn-tool:hover, .bg-light-gradient .btn-tool:hover, .card-light:not(.card-outline) .btn-tool:hover { color: #1F2D3D; } .card-dark:not(.card-outline) .card-header { background-color: #343a40; border-bottom: 0; } .card-dark:not(.card-outline) .card-header, .card-dark:not(.card-outline) .card-header a { color: #ffffff; } .card-dark.card-outline { border-top: 3px solid #343a40; } .bg-dark .btn-tool, .bg-dark-gradient .btn-tool, .card-dark:not(.card-outline) .btn-tool { color: rgba(255, 255, 255, 0.8); } .bg-dark .btn-tool:hover, .bg-dark-gradient .btn-tool:hover, .card-dark:not(.card-outline) .btn-tool:hover { color: #ffffff; } /* * Component: Info Box * ------------------- */ .info-box { box-shadow: 0 0 1px rgba(0, 0, 0, 0.125), 0 1px 3px rgba(0, 0, 0, 0.2); border-radius: 0.25rem; padding: .5rem; min-height: 80px; background: #ffffff; } .info-box .progress { background-color: rgba(0, 0, 0, 0.125); margin: 5px 0; height: 2px; } .info-box .progress .progress-bar { background-color: #ffffff; } .info-box-icon { border-radius: 0.25rem; display: block; width: 70px; text-align: center; font-size: 30px; } .info-box-icon > img { max-width: 100%; } .info-box-content { padding: 5px 10px; flex: 1; } .info-box-number { display: block; font-weight: 700; } .progress-description, .info-box-text { display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .info-box.bg-primary, .info-box.label-primary, .info-box.bg-primary-gradient { color: #ffffff; } .info-box.bg-primary .progress-bar, .info-box.label-primary .progress-bar, .info-box.bg-primary-gradient .progress-bar { background-color: #ffffff; } .info-box.bg-secondary, .info-box.bg-secondary-gradient { color: #ffffff; } .info-box.bg-secondary .progress-bar, .info-box.bg-secondary-gradient .progress-bar { background-color: #ffffff; } .info-box.bg-success, .info-box.alert-success, .info-box.label-success, .info-box.bg-success-gradient { color: #ffffff; } .info-box.bg-success .progress-bar, .info-box.alert-success .progress-bar, .info-box.label-success .progress-bar, .info-box.bg-success-gradient .progress-bar { background-color: #ffffff; } .info-box.bg-info, .info-box.alert-info, .info-box.label-info, .info-box.bg-info-gradient { color: #ffffff; } .info-box.bg-info .progress-bar, .info-box.alert-info .progress-bar, .info-box.label-info .progress-bar, .info-box.bg-info-gradient .progress-bar { background-color: #ffffff; } .info-box.bg-warning, .info-box.alert-warning, .info-box.label-warning, .info-box.bg-warning-gradient { color: #1F2D3D; } .info-box.bg-warning .progress-bar, .info-box.alert-warning .progress-bar, .info-box.label-warning .progress-bar, .info-box.bg-warning-gradient .progress-bar { background-color: #1F2D3D; } .info-box.bg-danger, .info-box.alert-danger, .info-box.alert-error, .info-box.label-danger, .info-box.bg-danger-gradient { color: #ffffff; } .info-box.bg-danger .progress-bar, .info-box.alert-danger .progress-bar, .info-box.alert-error .progress-bar, .info-box.label-danger .progress-bar, .info-box.bg-danger-gradient .progress-bar { background-color: #ffffff; } .info-box.bg-light, .info-box.bg-light-gradient { color: #1F2D3D; } .info-box.bg-light .progress-bar, .info-box.bg-light-gradient .progress-bar { background-color: #1F2D3D; } .info-box.bg-dark, .info-box.bg-dark-gradient { color: #ffffff; } .info-box.bg-dark .progress-bar, .info-box.bg-dark-gradient .progress-bar { background-color: #ffffff; } .info-box-more { display: block; } .progress-description { margin: 0; } /* * Component: Timeline * ------------------- */ .timeline { position: relative; margin: 0 0 30px 0; padding: 0; list-style: none; } .timeline:before { content: ''; position: absolute; top: 0; bottom: 0; width: 4px; background: #ddd; left: 31px; margin: 0; border-radius: 0.25rem; } .timeline > li { position: relative; margin-right: 10px; margin-bottom: 15px; } .timeline > li::after { display: block; clear: both; content: ""; } .timeline > li > .timeline-item { box-shadow: 0 0 1px rgba(0, 0, 0, 0.125), 0 1px 3px rgba(0, 0, 0, 0.2); border-radius: 0.25rem; margin-top: 0; background: #ffffff; color: #444; margin-left: 60px; margin-right: 15px; padding: 0; position: relative; } .timeline > li > .timeline-item > .time { color: #999; float: right; padding: 10px; font-size: 12px; } .timeline > li > .timeline-item > .timeline-header { margin: 0; color: #555; border-bottom: 1px solid rgba(0, 0, 0, 0.125); padding: 10px; font-size: 16px; line-height: 1.1; } .timeline > li > .timeline-item > .timeline-header > a { font-weight: 600; } .timeline > li > .timeline-item > .timeline-body, .timeline > li > .timeline-item > .timeline-footer { padding: 10px; } .timeline > li > .fa, .timeline > li > .glyphicon, .timeline > li > .ion { width: 30px; height: 30px; font-size: 15px; line-height: 30px; position: absolute; background: #adb5bd; border-radius: 50%; text-align: center; left: 18px; top: 0; } .timeline > .time-label > span { font-weight: 600; padding: 5px; display: inline-block; background-color: #ffffff; border-radius: 4px; } .timeline-inverse > li > .timeline-item { background: #f8f9fa; border: 1px solid #ddd; box-shadow: none; } .timeline-inverse > li > .timeline-item > .timeline-header { border-bottom-color: #ddd; } /* * Component: Button * ----------------- */ .btn.btn-flat { border-radius: 0; box-shadow: none; border-width: 1px; } .btn.btn-file { position: relative; overflow: hidden; } .btn.btn-file > input[type='file'] { position: absolute; top: 0; right: 0; min-width: 100%; min-height: 100%; font-size: 100px; text-align: right; opacity: 0; outline: none; background: white; cursor: inherit; display: block; } .btn-default { background-color: #f4f4f4; color: #444; border-color: #ddd; } .btn-default:hover, .btn-default:active, .btn-default.hover { background-color: #e7e7e7; } .btn-app { border-radius: 3px; position: relative; padding: 15px 5px; margin: 0 0 10px 10px; min-width: 80px; height: 60px; text-align: center; color: #666; border: 1px solid #ddd; background-color: #f4f4f4; font-size: 12px; } .btn-app > .fa, .btn-app > .glyphicon, .btn-app > .ion { font-size: 20px; display: block; } .btn-app:hover { background: #f4f4f4; color: #444; border-color: #aaa; } .btn-app:active, .btn-app:focus { box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); } .btn-app > .badge { position: absolute; top: -3px; right: -10px; font-size: 10px; font-weight: 400; } /* * Component: Callout * ------------------ */ .callout { border-radius: 0.25rem; background-color: #ffffff; padding: .5rem 1rem .5rem .5rem; border-left: 5px solid #eee; } .callout a { color: #ffffff; text-decoration: underline; } .callout a:hover { color: #eee; } .callout p:last-child { margin-bottom: 0; } .callout.callout-danger { border-left-color: #bd2130; } .callout.callout-warning { border-left-color: #d39e00; } .callout.callout-info { border-left-color: #117a8b; } .callout.callout-success { border-left-color: #1e7e34; } /* * Component: alert * ---------------- */ .alert .icon { margin-right: 10px; } .alert .close, .alert .mailbox-attachment-close { color: #000; opacity: .2; } .alert .close:hover, .alert .mailbox-attachment-close:hover { opacity: .5; } .alert a { color: #ffffff; text-decoration: underline; } .alert-success { border-color: #23923d; } .alert-danger, .alert-error { border-color: #d32535; } .alert-warning { border-color: #edb100; } .alert-info { border-color: #148ea1; } /* * Component: Nav * -------------- */ .nav-pills .nav-link { color: #6c757d; } .nav-pills .nav-link:not(.active):hover { color: #007bff; } /* * Component: Products List * ------------------------ */ .products-list { list-style: none; margin: 0; padding: 0; } .products-list > .item { border-radius: 0.25rem; padding: 10px 0; background: #ffffff; } .products-list > .item::after { display: block; clear: both; content: ""; } .products-list .product-img { float: left; } .products-list .product-img img { width: 50px; height: 50px; } .products-list .product-info { margin-left: 60px; } .products-list .product-title { font-weight: 600; } .products-list .product-description { display: block; color: #6c757d; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; } .product-list-in-card > .item { border-radius: 0; border-bottom: 1px solid rgba(0, 0, 0, 0.125); } .product-list-in-card > .item:last-of-type { border-bottom-width: 0; } /* * Component: Table * ---------------- */ .table.no-border, .table.no-border td, .table.no-border th { border: 0; } .table.text-center, .table.text-center td, .table.text-center th { text-align: center; } .table-valign-middle thead > tr > th, .table-valign-middle thead > tr > td, .table-valign-middle tbody > tr > th, .table-valign-middle tbody > tr > td { vertical-align: middle; } /* * Component: Label * ---------------- */ .label-default { background-color: #adb5bd; color: #444; } /* * Component: Direct Chat * ---------------------- */ .direct-chat .card-body { position: relative; overflow-x: hidden; padding: 0; } .direct-chat.chat-pane-open .direct-chat-contacts { -webkit-transform: translate(0, 0); -ms-transform: translate(0, 0); transform: translate(0, 0); } .direct-chat-messages { -webkit-transform: translate(0, 0); -ms-transform: translate(0, 0); transform: translate(0, 0); padding: 10px; height: 250px; overflow: auto; } .direct-chat-msg, .direct-chat-text { display: block; } .direct-chat-msg { margin-bottom: 10px; } .direct-chat-msg::after { display: block; clear: both; content: ""; } .direct-chat-messages, .direct-chat-contacts { transition: transform .5s ease-in-out; } .direct-chat-text { border-radius: 0.3rem; position: relative; padding: 5px 10px; background: #d2d6de; border: 1px solid #d2d6de; margin: 5px 0 0 50px; color: #444; } .direct-chat-text:after, .direct-chat-text:before { position: absolute; right: 100%; top: 15px; border: solid transparent; border-right-color: #d2d6de; content: ' '; height: 0; width: 0; pointer-events: none; } .direct-chat-text:after { border-width: 5px; margin-top: -5px; } .direct-chat-text:before { border-width: 6px; margin-top: -6px; } .right .direct-chat-text { margin-right: 50px; margin-left: 0; } .right .direct-chat-text:after, .right .direct-chat-text:before { right: auto; left: 100%; border-right-color: transparent; border-left-color: #d2d6de; } .direct-chat-img { border-radius: 50%; float: left; width: 40px; height: 40px; } .right .direct-chat-img { float: right; } .direct-chat-info { display: block; margin-bottom: 2px; font-size: 0.875rem; } .direct-chat-name { font-weight: 600; } .direct-chat-timestamp { color: #999; } .direct-chat-contacts-open .direct-chat-contacts { -webkit-transform: translate(0, 0); -ms-transform: translate(0, 0); transform: translate(0, 0); } .direct-chat-contacts { -webkit-transform: translate(101%, 0); -ms-transform: translate(101%, 0); transform: translate(101%, 0); position: absolute; top: 0; bottom: 0; height: 250px; width: 100%; background: #222d32; color: #ffffff; overflow: auto; } .contacts-list > li { border-bottom: 1px solid rgba(0, 0, 0, 0.2); padding: 10px; margin: 0; } .contacts-list > li::after { display: block; clear: both; content: ""; } .contacts-list > li:last-of-type { border-bottom: none; } .contacts-list-img { border-radius: 50%; width: 40px; float: left; } .contacts-list-info { margin-left: 45px; color: #ffffff; } .contacts-list-name, .contacts-list-status { display: block; } .contacts-list-name { font-weight: 600; } .contacts-list-status { font-size: 0.875rem; } .contacts-list-date { color: #aaa; font-weight: normal; } .contacts-list-msg { color: #999; } .direct-chat-danger .right > .direct-chat-text { background: #dc3545; border-color: #dc3545; color: #ffffff; } .direct-chat-danger .right > .direct-chat-text:after, .direct-chat-danger .right > .direct-chat-text:before { border-left-color: #dc3545; } .direct-chat-primary .right > .direct-chat-text { background: #007bff; border-color: #007bff; color: #ffffff; } .direct-chat-primary .right > .direct-chat-text:after, .direct-chat-primary .right > .direct-chat-text:before { border-left-color: #007bff; } .direct-chat-warning .right > .direct-chat-text { background: #ffc107; border-color: #ffc107; color: #1F2D3D; } .direct-chat-warning .right > .direct-chat-text:after, .direct-chat-warning .right > .direct-chat-text:before { border-left-color: #ffc107; } .direct-chat-info .right > .direct-chat-text { background: #17a2b8; border-color: #17a2b8; color: #ffffff; } .direct-chat-info .right > .direct-chat-text:after, .direct-chat-info .right > .direct-chat-text:before { border-left-color: #17a2b8; } .direct-chat-success .right > .direct-chat-text { background: #28a745; border-color: #28a745; color: #ffffff; } .direct-chat-success .right > .direct-chat-text:after, .direct-chat-success .right > .direct-chat-text:before { border-left-color: #28a745; } /* * Component: Users List * --------------------- */ .users-list > li { width: 25%; float: left; padding: 10px; text-align: center; } .users-list > li img { border-radius: 50%; max-width: 100%; height: auto; } .users-list > li > a:hover, .users-list > li > a:hover .users-list-name { color: #999; } .users-list-name, .users-list-date { display: block; } .users-list-name { font-size: 0.875rem; color: #444; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; } .users-list-date { color: #999; font-size: 12px; } /* * Component: Carousel * ------------------- */ .carousel-control.left, .carousel-control.right { background-image: none; } .carousel-control > .fa { font-size: 40px; position: absolute; top: 50%; z-index: 5; display: inline-block; margin-top: -20px; } /* * Component: Social Widgets * ------------------------- */ .card-widget { border: none; position: relative; } .widget-user .widget-user-header { padding: 1rem; height: 120px; border-top-left-radius: 0.25rem; border-top-right-radius: 0.25rem; } .widget-user .widget-user-username { margin-top: 0; margin-bottom: 5px; font-size: 25px; font-weight: 300; text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2); } .widget-user .widget-user-desc { margin-top: 0; } .widget-user .widget-user-image { position: absolute; top: 65px; left: 50%; margin-left: -45px; } .widget-user .widget-user-image > img { width: 90px; height: auto; border: 3px solid #ffffff; } .widget-user .card-footer { padding-top: 40px; } .widget-user-2 .widget-user-header { padding: 1rem; border-top-left-radius: 0.25rem; border-top-right-radius: 0.25rem; } .widget-user-2 .widget-user-username { margin-top: 5px; margin-bottom: 5px; font-size: 25px; font-weight: 300; } .widget-user-2 .widget-user-desc { margin-top: 0; } .widget-user-2 .widget-user-username, .widget-user-2 .widget-user-desc { margin-left: 75px; } .widget-user-2 .widget-user-image > img { width: 65px; height: auto; float: left; } /* * Page: Mailbox * ------------- */ .mailbox-messages > .table { margin: 0; } .mailbox-controls { padding: 5px; } .mailbox-controls.with-border { border-bottom: 1px solid rgba(0, 0, 0, 0.125); } .mailbox-read-info { border-bottom: 1px solid rgba(0, 0, 0, 0.125); padding: 10px; } .mailbox-read-info h3 { font-size: 20px; margin: 0; } .mailbox-read-info h5 { margin: 0; padding: 5px 0 0 0; } .mailbox-read-time { color: #999; font-size: 13px; } .mailbox-read-message { padding: 10px; } .mailbox-attachments li { float: left; width: 200px; border: 1px solid #eee; margin-bottom: 10px; margin-right: 10px; } .mailbox-attachment-name { font-weight: bold; color: #666; } .mailbox-attachment-icon, .mailbox-attachment-info, .mailbox-attachment-size { display: block; } .mailbox-attachment-info { padding: 10px; background: #f4f4f4; } .mailbox-attachment-size { color: #999; font-size: 12px; } .mailbox-attachment-icon { text-align: center; font-size: 65px; color: #666; padding: 20px 10px; } .mailbox-attachment-icon.has-img { padding: 0; } .mailbox-attachment-icon.has-img > img { max-width: 100%; height: auto; } /* * Page: Lock Screen * ----------------- */ /* ADD THIS CLASS TO THE TAG */ .lockscreen { background: #e9ecef; } .lockscreen-logo { font-size: 35px; text-align: center; margin-bottom: 25px; font-weight: 300; } .lockscreen-logo a { color: #444; } .lockscreen-wrapper { max-width: 400px; margin: 0 auto; margin-top: 10%; } /* User name [optional] */ .lockscreen .lockscreen-name { text-align: center; font-weight: 600; } /* Will contain the image and the sign in form */ .lockscreen-item { border-radius: 4px; padding: 0; background: #ffffff; position: relative; margin: 10px auto 30px auto; width: 290px; } /* User image */ .lockscreen-image { border-radius: 50%; position: absolute; left: -10px; top: -25px; background: #ffffff; padding: 5px; z-index: 10; } .lockscreen-image > img { border-radius: 50%; width: 70px; height: 70px; } /* Contains the password input and the login button */ .lockscreen-credentials { margin-left: 70px; } .lockscreen-credentials .form-control { border: 0; } .lockscreen-credentials .btn { background-color: #ffffff; border: 0; padding: 0 10px; } .lockscreen-footer { margin-top: 10px; } /* * Page: Login & Register * ---------------------- */ .login-logo, .register-logo { font-size: 35px; text-align: center; margin-bottom: 25px; font-weight: 300; } .login-logo a, .register-logo a { color: #444; } .login-page, .register-page { background: #e9ecef; } .login-box, .register-box { width: 360px; margin: 7% auto; } @media (max-width: 576px) { .login-box, .register-box { width: 90%; margin-top: 20px; } } .login-box-body, .register-box-body { background: #ffffff; padding: 20px; border-top: 0; color: #666; } .login-box-body .form-control-feedback, .register-box-body .form-control-feedback { color: #777; } .login-box-msg, .register-box-msg { margin: 0; text-align: center; padding: 0 20px 20px 20px; } .social-auth-links { margin: 10px 0; } /* * Page: 400 and 500 error pages * ------------------------------ */ .error-page { width: 600px; margin: 20px auto 0 auto; } @media (max-width: 767.98px) { .error-page { width: 100%; } } .error-page > .headline { float: left; font-size: 100px; font-weight: 300; } @media (max-width: 767.98px) { .error-page > .headline { float: none; text-align: center; } } .error-page > .error-content { margin-left: 190px; display: block; } @media (max-width: 767.98px) { .error-page > .error-content { margin-left: 0; } } .error-page > .error-content > h3 { font-weight: 300; font-size: 25px; } @media (max-width: 767.98px) { .error-page > .error-content > h3 { text-align: center; } } /* * Page: Invoice * ------------- */ .invoice { position: relative; background: #ffffff; border: 1px solid rgba(0, 0, 0, 0.125); } .invoice-title { margin-top: 0; } /* * Page: Profile * ------------- */ .profile-user-img { margin: 0 auto; width: 100px; padding: 3px; border: 3px solid #adb5bd; } .profile-username { font-size: 21px; margin-top: 5px; } .post { border-bottom: 1px solid #adb5bd; margin-bottom: 15px; padding-bottom: 15px; color: #666; } .post:last-of-type { border-bottom: 0; margin-bottom: 0; padding-bottom: 0; } .post .user-block { margin-bottom: 15px; } /* * Plugin: Full Calendar * --------------------- */ .fc-button { background: #f4f4f4; background-image: none; color: #444; border-color: #ddd; border-bottom-color: #ddd; } .fc-button:hover, .fc-button:active, .fc-button.hover { background-color: #e9e9e9; } .fc-header-title h2 { font-size: 15px; line-height: 1.6em; color: #666; margin-left: 10px; } .fc-header-right { padding-right: 10px; } .fc-header-left { padding-left: 10px; } .fc-widget-header { background: #fafafa; } .fc-grid { width: 100%; border: 0; } .fc-widget-header:first-of-type, .fc-widget-content:first-of-type { border-left: 0; border-right: 0; } .fc-widget-header:last-of-type, .fc-widget-content:last-of-type { border-right: 0; } .fc-toolbar { padding: 1rem; margin: 0; } .fc-day-number { font-size: 20px; font-weight: 300; padding-right: 10px; } .fc-color-picker { list-style: none; margin: 0; padding: 0; } .fc-color-picker > li { float: left; font-size: 30px; margin-right: 5px; line-height: 30px; } .fc-color-picker > li .fa { transition: transform linear .3s; } .fc-color-picker > li .fa:hover { -ms-transform: rotate(30deg); transform: rotate(30deg); } #add-new-event { transition: all linear .3s; } .external-event { box-shadow: 0 0 1px rgba(0, 0, 0, 0.125), 0 1px 3px rgba(0, 0, 0, 0.2); padding: 5px 10px; font-weight: bold; margin-bottom: 4px; border-radius: 0.25rem; cursor: move; } .external-event:hover { box-shadow: inset 0 0 90px rgba(0, 0, 0, 0.2); } /* * Plugin: Select2 * --------------- */ .select2-container--default.select2-container--focus, .select2-container--default:focus, .select2-container--default:active, .select2-selection.select2-container--focus, .select2-selection:focus, .select2-selection:active { outline: none; } .select2-container--default .select2-selection--single, .select2-selection .select2-selection--single { border: 1px solid #d2d6de; padding: 6px 12px; height: 34px; } .select2-container--default.select2-container--open { border-color: #007bff; } .select2-dropdown { border: 1px solid #d2d6de; } .select2-container--default .select2-results__option--highlighted[aria-selected] { background-color: #007bff; color: white; } .select2-results__option { padding: 6px 12px; user-select: none; -webkit-user-select: none; } .select2-container .select2-selection--single .select2-selection__rendered { padding-left: 0; padding-right: 0; height: auto; margin-top: -4px; } .select2-container[dir="rtl"] .select2-selection--single .select2-selection__rendered { padding-right: 6px; padding-left: 20px; } .select2-container--default .select2-selection--single .select2-selection__arrow { height: 28px; right: 3px; } .select2-container--default .select2-selection--single .select2-selection__arrow b { margin-top: 0; } .select2-dropdown .select2-search__field, .select2-search--inline .select2-search__field { border: 1px solid #d2d6de; } .select2-dropdown .select2-search__field:focus, .select2-search--inline .select2-search__field:focus { outline: none; border: 1px solid #007bff; } .select2-container--default .select2-results__option[aria-disabled=true] { color: #999; } .select2-container--default .select2-results__option[aria-selected=true] { background-color: #ddd; } .select2-container--default .select2-results__option[aria-selected=true], .select2-container--default .select2-results__option[aria-selected=true]:hover { color: #444; } .select2-container--default .select2-selection--multiple { border: 1px solid #d2d6de; } .select2-container--default .select2-selection--multiple:focus { border-color: #007bff; } .select2-container--default.select2-container--focus .select2-selection--multiple { border-color: #d2d6de; } .select2-container--default .select2-selection--multiple .select2-selection__choice { background-color: #007bff; border-color: #006fe6; padding: 1px 10px; color: #ffffff; } .select2-container--default .select2-selection--multiple .select2-selection__choice__remove { margin-right: 5px; color: rgba(255, 255, 255, 0.7); } .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover { color: #ffffff; } .select2-container .select2-selection--single .select2-selection__rendered { padding-right: 10px; } /* * General: Miscellaneous * ---------------------- */ a.text-muted:hover { color: #007bff !important; } .border-transparent { border-color: transparent !important; } .description-block { display: block; margin: 10px 0; text-align: center; } .description-block.margin-bottom { margin-bottom: 25px; } .description-block > .description-header { margin: 0; padding: 0; font-weight: 600; font-size: 16px; } .description-block > .description-text { text-transform: uppercase; } .bg-primary, .label-primary, .bg-primary a, .label-primary a { color: #ffffff !important; } .bg-secondary, .bg-secondary a { color: #ffffff !important; } .bg-success, .alert-success, .label-success, .bg-success a, .alert-success a, .label-success a { color: #ffffff !important; } .bg-info, .alert-info, .label-info, .bg-info a, .alert-info a, .label-info a { color: #ffffff !important; } .bg-warning, .alert-warning, .label-warning, .bg-warning a, .alert-warning a, .label-warning a { color: #1F2D3D !important; } .bg-danger, .alert-danger, .alert-error, .label-danger, .bg-danger a, .alert-danger a, .alert-error a, .label-danger a { color: #ffffff !important; } .bg-light, .bg-light a { color: #1F2D3D !important; } .bg-dark, .bg-dark a { color: #ffffff !important; } .bg-gray { color: #000; background-color: #adb5bd; } .bg-gray-light { background-color: #f2f4f5; color: #1F2D3D !important; } .bg-black { background-color: #000; color: #ffffff !important; } .bg-white { background-color: #ffffff; color: #1F2D3D !important; } [class^="bg-"].disabled { opacity: .65; } .link-muted { color: #5d6974; } .link-muted:hover, .link-muted:focus { color: #464f58; } .link-black { color: #666; } .link-black:hover, .link-black:focus { color: #999; } .hide { display: none !important; } .no-border { border: 0 !important; } .no-shadow { box-shadow: none !important; } .list-unstyled, .chart-legend, .contacts-list, .users-list, .mailbox-attachments { list-style: none; margin: 0; padding: 0; } .list-group-unbordered > .list-group-item { border-left: 0; border-right: 0; border-radius: 0; padding-left: 0; padding-right: 0; } .flat { border-radius: 0 !important; } .jqstooltip { padding: 5px !important; width: auto !important; height: auto !important; } .bg-primary-gradient { background: #007bff; background: -webkit-gradient(linear, left bottom, left top, color-stop(0, #007bff), color-stop(1, #3395ff)); background: -ms-linear-gradient(bottom, #007bff, #3395ff); background: -moz-linear-gradient(center bottom, #007bff 0%, #3395ff 100%); background: -o-linear-gradient(#3395ff, #007bff); color: #ffffff; } .bg-secondary-gradient { background: #6c757d; background: -webkit-gradient(linear, left bottom, left top, color-stop(0, #6c757d), color-stop(1, #868e96)); background: -ms-linear-gradient(bottom, #6c757d, #868e96); background: -moz-linear-gradient(center bottom, #6c757d 0%, #868e96 100%); background: -o-linear-gradient(#868e96, #6c757d); color: #ffffff; } .bg-success-gradient { background: #28a745; background: -webkit-gradient(linear, left bottom, left top, color-stop(0, #28a745), color-stop(1, #34ce57)); background: -ms-linear-gradient(bottom, #28a745, #34ce57); background: -moz-linear-gradient(center bottom, #28a745 0%, #34ce57 100%); background: -o-linear-gradient(#34ce57, #28a745); color: #ffffff; } .bg-info-gradient { background: #17a2b8; background: -webkit-gradient(linear, left bottom, left top, color-stop(0, #17a2b8), color-stop(1, #1fc8e3)); background: -ms-linear-gradient(bottom, #17a2b8, #1fc8e3); background: -moz-linear-gradient(center bottom, #17a2b8 0%, #1fc8e3 100%); background: -o-linear-gradient(#1fc8e3, #17a2b8); color: #ffffff; } .bg-warning-gradient { background: #ffc107; background: -webkit-gradient(linear, left bottom, left top, color-stop(0, #ffc107), color-stop(1, #ffce3a)); background: -ms-linear-gradient(bottom, #ffc107, #ffce3a); background: -moz-linear-gradient(center bottom, #ffc107 0%, #ffce3a 100%); background: -o-linear-gradient(#ffce3a, #ffc107); color: #1F2D3D; } .bg-danger-gradient { background: #dc3545; background: -webkit-gradient(linear, left bottom, left top, color-stop(0, #dc3545), color-stop(1, #e4606d)); background: -ms-linear-gradient(bottom, #dc3545, #e4606d); background: -moz-linear-gradient(center bottom, #dc3545 0%, #e4606d 100%); background: -o-linear-gradient(#e4606d, #dc3545); color: #ffffff; } .bg-light-gradient { background: #f8f9fa; background: -webkit-gradient(linear, left bottom, left top, color-stop(0, #f8f9fa), color-stop(1, white)); background: -ms-linear-gradient(bottom, #f8f9fa, white); background: -moz-linear-gradient(center bottom, #f8f9fa 0%, white 100%); background: -o-linear-gradient(white, #f8f9fa); color: #1F2D3D; } .bg-dark-gradient { background: #343a40; background: -webkit-gradient(linear, left bottom, left top, color-stop(0, #343a40), color-stop(1, #4b545c)); background: -ms-linear-gradient(bottom, #343a40, #4b545c); background: -moz-linear-gradient(center bottom, #343a40 0%, #4b545c 100%); background: -o-linear-gradient(#4b545c, #343a40); color: #ffffff; } .description-block .description-icon { font-size: 16px; } .list-header { font-size: 15px; padding: 10px 4px; font-weight: bold; color: #666; } .list-seperator { height: 1px; background: rgba(0, 0, 0, 0.125); margin: 15px 0 9px 0; } .list-link > a { padding: 4px; color: #777; } .list-link > a:hover { color: #222; } .user-block::after { display: block; clear: both; content: ""; } .user-block img { width: 40px; height: 40px; float: left; } .user-block .username, .user-block .description, .user-block .comment { display: block; margin-left: 50px; } .user-block .username { font-size: 16px; font-weight: 600; } .user-block .description { color: #999; font-size: 13px; } .user-block.user-block-sm .username, .user-block.user-block-sm .description, .user-block.user-block-sm .comment { margin-left: 40px; } .user-block.user-block-sm .username { font-size: 14px; } .img-sm, .card-comments .card-comment img, .user-block.user-block-sm img, .img-md, .img-lg { float: left; } .img-sm, .card-comments .card-comment img, .user-block.user-block-sm img { width: 30px !important; height: 30px !important; } .img-sm + .img-push, .card-comments .card-comment img + .img-push, .user-block.user-block-sm img + .img-push { margin-left: 40px; } .img-md { width: 60px; height: 60px; } .img-md + .img-push { margin-left: 70px; } .img-lg { width: 100px; height: 100px; } .img-lg + .img-push { margin-left: 110px; } .img-bordered { border: 3px solid #adb5bd; padding: 3px; } .img-bordered-sm { border: 2px solid #adb5bd; padding: 2px; } .img-rounded { border-radius: 0.25rem; } .img-circle { border-radius: 50%; } .img-size-64, .img-size-50, .img-size-32 { height: auto; } .img-size-64 { width: 64px; } .img-size-50 { width: 50px; } .img-size-32 { width: 32px; } .size-32, .size-40, .size-50 { display: block; text-align: center; } .size-32 { width: 32px; height: 32px; line-height: 32px; } .size-40 { width: 40px; height: 40px; line-height: 40px; } .size-50 { width: 50px; height: 50px; line-height: 50px; } .attachment-block { border: 1px solid rgba(0, 0, 0, 0.125); padding: 5px; margin-bottom: 10px; background: #f7f7f7; } .attachment-block .attachment-img { max-width: 100px; max-height: 100px; height: auto; float: left; } .attachment-block .attachment-pushed { margin-left: 110px; } .attachment-block .attachment-heading { margin: 0; } .attachment-block .attachment-text { color: #555; } .connectedSortable { min-height: 100px; } .ui-helper-hidden-accessible { border: 0; clip: rect(0 0 0 0); height: 1px; margin: -1px; overflow: hidden; padding: 0; position: absolute; width: 1px; } .sort-highlight { background: #f4f4f4; border: 1px dashed #ddd; margin-bottom: 10px; } .chart { position: relative; overflow: hidden; } .flex-1 { flex: 1; } /* * Misc: print * ----------- */ @media print { .no-print, .main-sidebar, .main-header, .content-header { display: none !important; } .content-wrapper, .main-footer { margin-left: 0 !important; min-height: 0 !important; -webkit-transform: translate(0, 0); -ms-transform: translate(0, 0); transform: translate(0, 0); } .layout-fixed .content-wrapper { padding-top: 0 !important; } .invoice { width: 100%; border: 0; margin: 0; padding: 0; } .invoice-col { float: left; width: 33.3333333%; } .table-responsive { overflow: auto; } .table-responsive > .table tr th, .table-responsive > .table tr td { white-space: normal !important; } } .text-bold, .text-bold.table td, .text-bold.table th { font-weight: 700; } .text-sm { font-size: 0.875rem; } .text-xs { font-size: 1rem; } .text-lg { font-size: 1.25rem; } .text-xl { font-size: 2rem; } .elevation-0 { box-shadow: none; } .elevation-1, .sidebar-dark-primary .nav-sidebar > .nav-item > .nav-link.active, .sidebar-light-primary .nav-sidebar > .nav-item > .nav-link.active, .sidebar-dark-secondary .nav-sidebar > .nav-item > .nav-link.active, .sidebar-light-secondary .nav-sidebar > .nav-item > .nav-link.active, .sidebar-dark-success .nav-sidebar > .nav-item > .nav-link.active, .sidebar-light-success .nav-sidebar > .nav-item > .nav-link.active, .sidebar-dark-info .nav-sidebar > .nav-item > .nav-link.active, .sidebar-light-info .nav-sidebar > .nav-item > .nav-link.active, .sidebar-dark-warning .nav-sidebar > .nav-item > .nav-link.active, .sidebar-light-warning .nav-sidebar > .nav-item > .nav-link.active, .sidebar-dark-danger .nav-sidebar > .nav-item > .nav-link.active, .sidebar-light-danger .nav-sidebar > .nav-item > .nav-link.active, .sidebar-dark-light .nav-sidebar > .nav-item > .nav-link.active, .sidebar-light-light .nav-sidebar > .nav-item > .nav-link.active, .sidebar-dark-dark .nav-sidebar > .nav-item > .nav-link.active, .sidebar-light-dark .nav-sidebar > .nav-item > .nav-link.active, .callout { box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24); } .elevation-2 { box-shadow: 0 3px 6px rgba(0, 0, 0, 0.16), 0 3px 6px rgba(0, 0, 0, 0.23); } .elevation-3 { box-shadow: 0 10px 20px rgba(0, 0, 0, 0.19), 0 6px 6px rgba(0, 0, 0, 0.23); } .elevation-4 { box-shadow: 0 14px 28px rgba(0, 0, 0, 0.25), 0 10px 10px rgba(0, 0, 0, 0.22); } .elevation-5 { box-shadow: 0 19px 38px rgba(0, 0, 0, 0.3), 0 15px 12px rgba(0, 0, 0, 0.22); } /*# sourceMappingURL=adminlte.css.map */ ================================================ FILE: src/main/resources/static/admin/dist/css/main.css ================================================ html { overflow-x:hidden; } .content-header { position: relative; padding: 0 0 3px 8px } .content-header>.breadcrumb { position: relative; top: 0; right: 0; float: none; margin-top: 0px; padding-left: 10px; background: #ecf0f5; } .main-footer { padding: 7px; color: #444; border-top: 1px solid #eee; } [v-cloak] { display: none; } .grid-btn{ margin-bottom:12px; display:flex; } .grid-btn .btn{ margin-right:10px; } .pointer{cursor: pointer;} .ml-10 { margin-left:0 !important; } @media (min-width: 768px) { .ml-10 { margin-left:10px !important; } .col-sm-10 {width: 70%;padding-left: 0px;} .col-sm-2 {width: 24%;} } tbody > tr > th {font-weight: normal; } .panel .table { margin:0 0; } .panel .pagination { margin:0; } .panel-default>.panel-heading {background-color: #f5f5f5;} .row{ border-top: 1px solid #ddd; margin:0; padding:20px 2px 0px 2px; } .col-xs-6{padding-left: 0px;padding-right: 0px;} .form-horizontal .form-group {margin-left:0px;margin-right:0px;} .form-horizontal{ width:550px;padding-top:20px; } .dropdown-donate-lg { min-width: 120px; max-width: 120px; padding: 0; } ul.thumbnails.image_picker_selector { overflow: auto; list-style-image: none; list-style-position: outside; list-style-type: none; padding: 0px; margin: 0px; } ul.thumbnails.image_picker_selector li { margin: 0px 12px 12px 0px; float: left; } ul.thumbnails.image_picker_selector li .thumbnail { padding: 6px; border: 1px solid #dddddd; } ul.thumbnails.image_picker_selector li .thumbnail.selected { background: #0088cc; } ================================================ FILE: src/main/resources/static/admin/dist/js/adminlte.js ================================================ /*! * AdminLTE v3.0.0-alpha (https://adminlte.io) * Copyright 2014-2018 Abdullah Almsaeed * Licensed under MIT (https://github.com/almasaeed2010/AdminLTE/blob/master/LICENSE) */ (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define(['exports'], factory) : (factory((global.adminlte = {}))); }(this, (function (exports) { 'use strict'; var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; var classCallCheck = function (instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }; /** * -------------------------------------------- * AdminLTE ControlSidebar.js * License MIT * -------------------------------------------- */ var ControlSidebar = function ($) { /** * Constants * ==================================================== */ var NAME = 'ControlSidebar'; var DATA_KEY = 'lte.control.sidebar'; var JQUERY_NO_CONFLICT = $.fn[NAME]; var Selector = { CONTROL_SIDEBAR: '.control-sidebar', DATA_TOGGLE: '[data-widget="control-sidebar"]', MAIN_HEADER: '.main-header' }; var ClassName = { CONTROL_SIDEBAR_OPEN: 'control-sidebar-open', CONTROL_SIDEBAR_SLIDE: 'control-sidebar-slide-open' }; var Default = { slide: true /** * Class Definition * ==================================================== */ }; var ControlSidebar = function () { function ControlSidebar(element, config) { classCallCheck(this, ControlSidebar); this._element = element; this._config = this._getConfig(config); } // Public ControlSidebar.prototype.show = function show() { // Show the control sidebar if (this._config.slide) { $('body').removeClass(ClassName.CONTROL_SIDEBAR_SLIDE); } else { $('body').removeClass(ClassName.CONTROL_SIDEBAR_OPEN); } }; ControlSidebar.prototype.collapse = function collapse() { // Collapse the control sidebar if (this._config.slide) { $('body').addClass(ClassName.CONTROL_SIDEBAR_SLIDE); } else { $('body').addClass(ClassName.CONTROL_SIDEBAR_OPEN); } }; ControlSidebar.prototype.toggle = function toggle() { this._setMargin(); var shouldOpen = $('body').hasClass(ClassName.CONTROL_SIDEBAR_OPEN) || $('body').hasClass(ClassName.CONTROL_SIDEBAR_SLIDE); if (shouldOpen) { // Open the control sidebar this.show(); } else { // Close the control sidebar this.collapse(); } }; // Private ControlSidebar.prototype._getConfig = function _getConfig(config) { return $.extend({}, Default, config); }; ControlSidebar.prototype._setMargin = function _setMargin() { $(Selector.CONTROL_SIDEBAR).css({ top: $(Selector.MAIN_HEADER).outerHeight() }); }; // Static ControlSidebar._jQueryInterface = function _jQueryInterface(operation) { return this.each(function () { var data = $(this).data(DATA_KEY); if (!data) { data = new ControlSidebar(this, $(this).data()); $(this).data(DATA_KEY, data); } if (data[operation] === 'undefined') { throw new Error(operation + ' is not a function'); } data[operation](); }); }; return ControlSidebar; }(); /** * * Data Api implementation * ==================================================== */ $(document).on('click', Selector.DATA_TOGGLE, function (event) { event.preventDefault(); ControlSidebar._jQueryInterface.call($(this), 'toggle'); }); /** * jQuery API * ==================================================== */ $.fn[NAME] = ControlSidebar._jQueryInterface; $.fn[NAME].Constructor = ControlSidebar; $.fn[NAME].noConflict = function () { $.fn[NAME] = JQUERY_NO_CONFLICT; return ControlSidebar._jQueryInterface; }; return ControlSidebar; }(jQuery); /** * -------------------------------------------- * AdminLTE Layout.js * License MIT * -------------------------------------------- */ var Layout = function ($) { /** * Constants * ==================================================== */ var NAME = 'Layout'; var DATA_KEY = 'lte.layout'; var JQUERY_NO_CONFLICT = $.fn[NAME]; var Selector = { SIDEBAR: '.main-sidebar', HEADER: '.main-header', CONTENT: '.content-wrapper', CONTENT_HEADER: '.content-header', WRAPPER: '.wrapper', CONTROL_SIDEBAR: '.control-sidebar', LAYOUT_FIXED: '.layout-fixed', FOOTER: '.main-footer' }; var ClassName = { HOLD: 'hold-transition', SIDEBAR: 'main-sidebar', LAYOUT_FIXED: 'layout-fixed' /** * Class Definition * ==================================================== */ }; var Layout = function () { function Layout(element) { classCallCheck(this, Layout); this._element = element; this._init(); } // Public Layout.prototype.fixLayoutHeight = function fixLayoutHeight() { var heights = { window: $(window).height(), header: $(Selector.HEADER).outerHeight(), footer: $(Selector.FOOTER).outerHeight(), sidebar: $(Selector.SIDEBAR).height() }; var max = this._max(heights); $(Selector.CONTENT).css('min-height', max - heights.header); $(Selector.SIDEBAR).css('min-height', max - heights.header); }; // Private Layout.prototype._init = function _init() { var _this = this; // Enable transitions $('body').removeClass(ClassName.HOLD); // Activate layout height watcher this.fixLayoutHeight(); $(Selector.SIDEBAR).on('collapsed.lte.treeview expanded.lte.treeview collapsed.lte.pushmenu expanded.lte.pushmenu', function () { _this.fixLayoutHeight(); }); $(window).resize(function () { _this.fixLayoutHeight(); }); $('body, html').css('height', 'auto'); }; Layout.prototype._max = function _max(numbers) { // Calculate the maximum number in a list var max = 0; Object.keys(numbers).forEach(function (key) { if (numbers[key] > max) { max = numbers[key]; } }); return max; }; // Static Layout._jQueryInterface = function _jQueryInterface(operation) { return this.each(function () { var data = $(this).data(DATA_KEY); if (!data) { data = new Layout(this); $(this).data(DATA_KEY, data); } if (operation) { data[operation](); } }); }; return Layout; }(); /** * Data API * ==================================================== */ $(window).on('load', function () { Layout._jQueryInterface.call($('body')); }); /** * jQuery API * ==================================================== */ $.fn[NAME] = Layout._jQueryInterface; $.fn[NAME].Constructor = Layout; $.fn[NAME].noConflict = function () { $.fn[NAME] = JQUERY_NO_CONFLICT; return Layout._jQueryInterface; }; return Layout; }(jQuery); /** * -------------------------------------------- * AdminLTE PushMenu.js * License MIT * -------------------------------------------- */ var PushMenu = function ($) { /** * Constants * ==================================================== */ var NAME = 'PushMenu'; var DATA_KEY = 'lte.pushmenu'; var EVENT_KEY = '.' + DATA_KEY; var JQUERY_NO_CONFLICT = $.fn[NAME]; var Event = { COLLAPSED: 'collapsed' + EVENT_KEY, SHOWN: 'shown' + EVENT_KEY }; var Default = { screenCollapseSize: 768 }; var Selector = { TOGGLE_BUTTON: '[data-widget="pushmenu"]', SIDEBAR_MINI: '.sidebar-mini', SIDEBAR_COLLAPSED: '.sidebar-collapse', BODY: 'body', OVERLAY: '#sidebar-overlay', WRAPPER: '.wrapper' }; var ClassName = { SIDEBAR_OPEN: 'sidebar-open', COLLAPSED: 'sidebar-collapse', OPEN: 'sidebar-open', SIDEBAR_MINI: 'sidebar-mini' /** * Class Definition * ==================================================== */ }; var PushMenu = function () { function PushMenu(element, options) { classCallCheck(this, PushMenu); this._element = element; this._options = $.extend({}, Default, options); if (!$(Selector.OVERLAY).length) { this._addOverlay(); } } // Public PushMenu.prototype.show = function show() { $(Selector.BODY).addClass(ClassName.OPEN).removeClass(ClassName.COLLAPSED); var shownEvent = $.Event(Event.SHOWN); $(this._element).trigger(shownEvent); }; PushMenu.prototype.collapse = function collapse() { $(Selector.BODY).removeClass(ClassName.OPEN).addClass(ClassName.COLLAPSED); var collapsedEvent = $.Event(Event.COLLAPSED); $(this._element).trigger(collapsedEvent); }; PushMenu.prototype.toggle = function toggle() { var isShown = void 0; if ($(window).width() >= this._options.screenCollapseSize) { isShown = !$(Selector.BODY).hasClass(ClassName.COLLAPSED); } else { isShown = $(Selector.BODY).hasClass(ClassName.OPEN); } if (isShown) { this.collapse(); } else { this.show(); } }; // Private PushMenu.prototype._addOverlay = function _addOverlay() { var _this = this; var overlay = $('
', { id: 'sidebar-overlay' }); overlay.on('click', function () { _this.collapse(); }); $(Selector.WRAPPER).append(overlay); }; // Static PushMenu._jQueryInterface = function _jQueryInterface(operation) { return this.each(function () { var data = $(this).data(DATA_KEY); if (!data) { data = new PushMenu(this); $(this).data(DATA_KEY, data); } if (operation) { data[operation](); } }); }; return PushMenu; }(); /** * Data API * ==================================================== */ $(document).on('click', Selector.TOGGLE_BUTTON, function (event) { event.preventDefault(); var button = event.currentTarget; if ($(button).data('widget') !== 'pushmenu') { button = $(button).closest(Selector.TOGGLE_BUTTON); } PushMenu._jQueryInterface.call($(button), 'toggle'); }); /** * jQuery API * ==================================================== */ $.fn[NAME] = PushMenu._jQueryInterface; $.fn[NAME].Constructor = PushMenu; $.fn[NAME].noConflict = function () { $.fn[NAME] = JQUERY_NO_CONFLICT; return PushMenu._jQueryInterface; }; return PushMenu; }(jQuery); /** * -------------------------------------------- * AdminLTE Treeview.js * License MIT * -------------------------------------------- */ var Treeview = function ($) { /** * Constants * ==================================================== */ var NAME = 'Treeview'; var DATA_KEY = 'lte.treeview'; var EVENT_KEY = '.' + DATA_KEY; var JQUERY_NO_CONFLICT = $.fn[NAME]; var Event = { SELECTED: 'selected' + EVENT_KEY, EXPANDED: 'expanded' + EVENT_KEY, COLLAPSED: 'collapsed' + EVENT_KEY, LOAD_DATA_API: 'load' + EVENT_KEY }; var Selector = { LI: '.nav-item', LINK: '.nav-link', TREEVIEW_MENU: '.nav-treeview', OPEN: '.menu-open', DATA_WIDGET: '[data-widget="treeview"]' }; var ClassName = { LI: 'nav-item', LINK: 'nav-link', TREEVIEW_MENU: 'nav-treeview', OPEN: 'menu-open' }; var Default = { trigger: Selector.DATA_WIDGET + ' ' + Selector.LINK, animationSpeed: 300, accordion: true /** * Class Definition * ==================================================== */ }; var Treeview = function () { function Treeview(element, config) { classCallCheck(this, Treeview); this._config = config; this._element = element; } // Public Treeview.prototype.init = function init() { this._setupListeners(); }; Treeview.prototype.expand = function expand(treeviewMenu, parentLi) { var _this = this; var expandedEvent = $.Event(Event.EXPANDED); if (this._config.accordion) { var openMenuLi = parentLi.siblings(Selector.OPEN).first(); var openTreeview = openMenuLi.find(Selector.TREEVIEW_MENU).first(); this.collapse(openTreeview, openMenuLi); } treeviewMenu.slideDown(this._config.animationSpeed, function () { parentLi.addClass(ClassName.OPEN); $(_this._element).trigger(expandedEvent); }); }; Treeview.prototype.collapse = function collapse(treeviewMenu, parentLi) { var _this2 = this; var collapsedEvent = $.Event(Event.COLLAPSED); treeviewMenu.slideUp(this._config.animationSpeed, function () { parentLi.removeClass(ClassName.OPEN); $(_this2._element).trigger(collapsedEvent); treeviewMenu.find(Selector.OPEN + ' > ' + Selector.TREEVIEW_MENU).slideUp(); treeviewMenu.find(Selector.OPEN).removeClass(ClassName.OPEN); }); }; Treeview.prototype.toggle = function toggle(event) { var $relativeTarget = $(event.currentTarget); var treeviewMenu = $relativeTarget.next(); if (!treeviewMenu.is(Selector.TREEVIEW_MENU)) { return; } event.preventDefault(); var parentLi = $relativeTarget.parents(Selector.LI).first(); var isOpen = parentLi.hasClass(ClassName.OPEN); if (isOpen) { this.collapse($(treeviewMenu), parentLi); } else { this.expand($(treeviewMenu), parentLi); } }; // Private Treeview.prototype._setupListeners = function _setupListeners() { var _this3 = this; $(document).on('click', this._config.trigger, function (event) { _this3.toggle(event); }); }; // Static Treeview._jQueryInterface = function _jQueryInterface(config) { return this.each(function () { var data = $(this).data(DATA_KEY); var _config = $.extend({}, Default, $(this).data()); if (!data) { data = new Treeview($(this), _config); $(this).data(DATA_KEY, data); } if (config === 'init') { data[config](); } }); }; return Treeview; }(); /** * Data API * ==================================================== */ $(window).on(Event.LOAD_DATA_API, function () { $(Selector.DATA_WIDGET).each(function () { Treeview._jQueryInterface.call($(this), 'init'); }); }); /** * jQuery API * ==================================================== */ $.fn[NAME] = Treeview._jQueryInterface; $.fn[NAME].Constructor = Treeview; $.fn[NAME].noConflict = function () { $.fn[NAME] = JQUERY_NO_CONFLICT; return Treeview._jQueryInterface; }; return Treeview; }(jQuery); /** * -------------------------------------------- * AdminLTE Widget.js * License MIT * -------------------------------------------- */ var Widget = function ($) { /** * Constants * ==================================================== */ var NAME = 'Widget'; var DATA_KEY = 'lte.widget'; var EVENT_KEY = '.' + DATA_KEY; var JQUERY_NO_CONFLICT = $.fn[NAME]; var Event = { EXPANDED: 'expanded' + EVENT_KEY, COLLAPSED: 'collapsed' + EVENT_KEY, REMOVED: 'removed' + EVENT_KEY }; var Selector = { DATA_REMOVE: '[data-widget="remove"]', DATA_COLLAPSE: '[data-widget="collapse"]', CARD: '.card', CARD_HEADER: '.card-header', CARD_BODY: '.card-body', CARD_FOOTER: '.card-footer', COLLAPSED: '.collapsed-card' }; var ClassName = { COLLAPSED: 'collapsed-card' }; var Default = { animationSpeed: 'normal', collapseTrigger: Selector.DATA_COLLAPSE, removeTrigger: Selector.DATA_REMOVE }; var Widget = function () { function Widget(element, settings) { classCallCheck(this, Widget); this._element = element; this._parent = element.parents(Selector.CARD).first(); this._settings = $.extend({}, Default, settings); } Widget.prototype.collapse = function collapse() { var _this = this; this._parent.children(Selector.CARD_BODY + ', ' + Selector.CARD_FOOTER).slideUp(this._settings.animationSpeed, function () { _this._parent.addClass(ClassName.COLLAPSED); }); var collapsed = $.Event(Event.COLLAPSED); this._element.trigger(collapsed, this._parent); }; Widget.prototype.expand = function expand() { var _this2 = this; this._parent.children(Selector.CARD_BODY + ', ' + Selector.CARD_FOOTER).slideDown(this._settings.animationSpeed, function () { _this2._parent.removeClass(ClassName.COLLAPSED); }); var expanded = $.Event(Event.EXPANDED); this._element.trigger(expanded, this._parent); }; Widget.prototype.remove = function remove() { this._parent.slideUp(); var removed = $.Event(Event.REMOVED); this._element.trigger(removed, this._parent); }; Widget.prototype.toggle = function toggle() { if (this._parent.hasClass(ClassName.COLLAPSED)) { this.expand(); return; } this.collapse(); }; // Private Widget.prototype._init = function _init(card) { var _this3 = this; this._parent = card; $(this).find(this._settings.collapseTrigger).click(function () { _this3.toggle(); }); $(this).find(this._settings.removeTrigger).click(function () { _this3.remove(); }); }; // Static Widget._jQueryInterface = function _jQueryInterface(config) { return this.each(function () { var data = $(this).data(DATA_KEY); if (!data) { data = new Widget($(this), data); $(this).data(DATA_KEY, typeof config === 'string' ? data : config); } if (typeof config === 'string' && config.match(/remove|toggle/)) { data[config](); } else if ((typeof config === 'undefined' ? 'undefined' : _typeof(config)) === 'object') { data._init($(this)); } }); }; return Widget; }(); /** * Data API * ==================================================== */ $(document).on('click', Selector.DATA_COLLAPSE, function (event) { if (event) { event.preventDefault(); } Widget._jQueryInterface.call($(this), 'toggle'); }); $(document).on('click', Selector.DATA_REMOVE, function (event) { if (event) { event.preventDefault(); } Widget._jQueryInterface.call($(this), 'remove'); }); /** * jQuery API * ==================================================== */ $.fn[NAME] = Widget._jQueryInterface; $.fn[NAME].Constructor = Widget; $.fn[NAME].noConflict = function () { $.fn[NAME] = JQUERY_NO_CONFLICT; return Widget._jQueryInterface; }; return Widget; }(jQuery); exports.ControlSidebar = ControlSidebar; exports.Layout = Layout; exports.PushMenu = PushMenu; exports.Treeview = Treeview; exports.Widget = Widget; Object.defineProperty(exports, '__esModule', { value: true }); }))); //# sourceMappingURL=adminlte.js.map ================================================ FILE: src/main/resources/static/admin/dist/js/demo.js ================================================ /** * AdminLTE Demo Menu * ------------------ * You should not use this file in production. * This file is for demo purposes only. */ (function ($) { 'use strict' var $sidebar = $('.control-sidebar') var $container = $('
', { class: 'p-3' }) $sidebar.append($container) var navbar_dark_skins = [ 'bg-primary', 'bg-info', 'bg-success', 'bg-danger' ] var navbar_light_skins = [ 'bg-warning', 'bg-white', 'bg-gray-light' ] $container.append( '
Customize AdminLTE

' + '
Navbar Variants
' ) var $navbar_variants = $('
', { 'class': 'd-flex' }) var navbar_all_colors = navbar_dark_skins.concat(navbar_light_skins) var $navbar_variants_colors = createSkinBlock(navbar_all_colors, function (e) { var color = $(this).data('color') console.log('Adding ' + color) var $main_header = $('.main-header') $main_header.removeClass('navbar-dark').removeClass('navbar-light') navbar_all_colors.map(function (color) { $main_header.removeClass(color) }) if (navbar_dark_skins.indexOf(color) > -1) { $main_header.addClass('navbar-dark') console.log('AND navbar-dark') } else { console.log('AND navbar-light') $main_header.addClass('navbar-light') } $main_header.addClass(color) }) $navbar_variants.append($navbar_variants_colors) $container.append($navbar_variants) var $checkbox_container = $('
', { 'class': 'mb-4' }) var $navbar_border = $('', { type : 'checkbox', value : 1, checked: $('.main-header').hasClass('border-bottom'), 'class': 'mr-1' }).on('click', function () { if ($(this).is(':checked')) { $('.main-header').addClass('border-bottom') } else { $('.main-header').removeClass('border-bottom') } console.log($(this).is(':checked'), 'hi') }) $checkbox_container.append($navbar_border) $checkbox_container.append('Navbar border') $container.append($checkbox_container) var sidebar_colors = [ 'bg-primary', 'bg-warning', 'bg-info', 'bg-danger', 'bg-success' ] var sidebar_skins = [ 'sidebar-dark-primary', 'sidebar-dark-warning', 'sidebar-dark-info', 'sidebar-dark-danger', 'sidebar-dark-success', 'sidebar-light-primary', 'sidebar-light-warning', 'sidebar-light-info', 'sidebar-light-danger', 'sidebar-light-success' ] $container.append('
Dark Sidebar Variants
') var $sidebar_variants = $('
', { 'class': 'd-flex' }) $container.append($sidebar_variants) $container.append(createSkinBlock(sidebar_colors, function () { var color = $(this).data('color') var sidebar_class = 'sidebar-dark-' + color.replace('bg-', '') var $sidebar = $('.main-sidebar') sidebar_skins.map(function (skin) { $sidebar.removeClass(skin) }) $sidebar.addClass(sidebar_class) console.log('added ' + sidebar_class) })) $container.append('
Light Sidebar Variants
') var $sidebar_variants = $('
', { 'class': 'd-flex' }) $container.append($sidebar_variants) $container.append(createSkinBlock(sidebar_colors, function () { var color = $(this).data('color') var sidebar_class = 'sidebar-light-' + color.replace('bg-', '') var $sidebar = $('.main-sidebar') sidebar_skins.map(function (skin) { $sidebar.removeClass(skin) }) $sidebar.addClass(sidebar_class) console.log('added ' + sidebar_class) })) var logo_skins = navbar_all_colors $container.append('
Brand Logo Variants
') var $logo_variants = $('
', { 'class': 'd-flex' }) $container.append($logo_variants) var $clear_btn = $('', { href: 'javascript:void(0)' }).text('clear').on('click', function () { var $logo = $('.brand-link') logo_skins.map(function (skin) { $logo.removeClass(skin) }) }) $container.append(createSkinBlock(logo_skins, function () { var color = $(this).data('color') var $logo = $('.brand-link') logo_skins.map(function (skin) { $logo.removeClass(skin) }) $logo.addClass(color) }).append($clear_btn)) function createSkinBlock(colors, callback) { var $block = $('
', { 'class': 'd-flex flex-wrap mb-3' }) colors.map(function (color) { var $color = $('
', { 'class': (typeof color === 'object' ? color.join(' ') : color) + ' elevation-2' }) $block.append($color) $color.data('color', color) $color.css({ width : '40px', height : '20px', borderRadius: '25px', marginRight : 10, marginBottom: 10, opacity : 0.8, cursor : 'pointer' }) $color.hover(function () { $(this).css({ opacity: 1 }).removeClass('elevation-2').addClass('elevation-4') }, function () { $(this).css({ opacity: 0.8 }).removeClass('elevation-4').addClass('elevation-2') }) if (callback) { $color.on('click', callback) } }) return $block } })(jQuery) ================================================ FILE: src/main/resources/static/admin/dist/js/newbee_mall_carousel.js ================================================ $(function () { $("#jqGrid").jqGrid({ url: '/admin/carousels/list', datatype: "json", colModel: [ {label: 'id', name: 'carouselId', index: 'carouselId', width: 50, key: true, hidden: true}, {label: '轮播图', name: 'carouselUrl', index: 'carouselUrl', width: 180, formatter: coverImageFormatter}, {label: '跳转链接', name: 'redirectUrl', index: 'redirectUrl', width: 120}, {label: '排序值', name: 'carouselRank', index: 'carouselRank', width: 120}, {label: '添加时间', name: 'createTime', index: 'createTime', width: 120} ], height: 560, rowNum: 10, rowList: [10, 20, 50], styleUI: 'Bootstrap', loadtext: '信息读取中...', rownumbers: false, rownumWidth: 20, autowidth: true, multiselect: true, pager: "#jqGridPager", jsonReader: { root: "data.list", page: "data.currPage", total: "data.totalPage", records: "data.totalCount" }, prmNames: { page: "page", rows: "limit", order: "order", }, gridComplete: function () { //隐藏grid底部滚动条 $("#jqGrid").closest(".ui-jqgrid-bdiv").css({"overflow-x": "hidden"}); } }); function coverImageFormatter(cellvalue) { return "coverImage"; } $(window).resize(function () { $("#jqGrid").setGridWidth($(".card-body").width()); }); new AjaxUpload('#uploadCarouselImage', { action: '/admin/upload/file', name: 'file', autoSubmit: true, responseType: "json", onSubmit: function (file, extension) { if (!(extension && /^(jpg|jpeg|png|gif)$/.test(extension.toLowerCase()))) { Swal.fire({ text: "只支持jpg、png、gif格式的文件!", icon: "error",iconColor:"#f05b72", }); return false; } }, onComplete: function (file, r) { if (r != null && r.resultCode == 200) { $("#carouselImg").attr("src", r.data); $("#carouselImg").attr("style", "width: 128px;height: 128px;display:block;"); return false; } else { Swal.fire({ text: r.message, icon: "error",iconColor:"#f05b72", }); } } }); }); /** * jqGrid重新加载 */ function reload() { var page = $("#jqGrid").jqGrid('getGridParam', 'page'); $("#jqGrid").jqGrid('setGridParam', { page: page }).trigger("reloadGrid"); } function carouselAdd() { reset(); $('.modal-title').html('轮播图添加'); $('#carouselModal').modal('show'); } //绑定modal上的保存按钮 $('#saveButton').click(function () { var redirectUrl = $("#redirectUrl").val(); var carouselRank = $("#carouselRank").val(); var carouselUrl = $('#carouselImg')[0].src; var data = { "carouselUrl": carouselUrl, "carouselRank": carouselRank, "redirectUrl": redirectUrl }; var url = '/admin/carousels/save'; var id = getSelectedRowWithoutAlert(); if (id != null) { url = '/admin/carousels/update'; data = { "carouselId": id, "carouselUrl": carouselUrl, "carouselRank": carouselRank, "redirectUrl": redirectUrl }; } $.ajax({ type: 'POST',//方法类型 url: url, contentType: 'application/json', data: JSON.stringify(data), success: function (result) { if (result.resultCode == 200) { $('#carouselModal').modal('hide'); Swal.fire({ text: "保存成功", icon: "success",iconColor:"#1d953f", }); reload(); } else { $('#carouselModal').modal('hide'); Swal.fire({ text: result.message, icon: "error",iconColor:"#f05b72", }); } ; }, error: function () { Swal.fire({ text: "操作失败", icon: "error",iconColor:"#f05b72", }); } }); }); function carouselEdit() { reset(); var id = getSelectedRow(); if (id == null) { return; } //请求数据 $.get("/admin/carousels/info/" + id, function (r) { if (r.resultCode == 200 && r.data != null) { //填充数据至modal $("#carouselImg").attr("src", r.data.carouselUrl); $("#carouselImg").attr("style", "height: 64px;width: 64px;display:block;"); $("#redirectUrl").val(r.data.redirectUrl); $("#carouselRank").val(r.data.carouselRank); } }); $('.modal-title').html('轮播图编辑'); $('#carouselModal').modal('show'); } function deleteCarousel() { var ids = getSelectedRows(); if (ids == null) { return; } Swal.fire({ title: "确认弹框", text: "确认要删除数据吗?", icon: "warning",iconColor:"#dea32c", showCancelButton: true, confirmButtonText: '确认', cancelButtonText: '取消' }).then((flag) => { if (flag.value) { $.ajax({ type: "POST", url: "/admin/carousels/delete", contentType: "application/json", data: JSON.stringify(ids), success: function (r) { if (r.resultCode == 200) { Swal.fire({ text: "删除成功", icon: "success",iconColor:"#1d953f", }); $("#jqGrid").trigger("reloadGrid"); } else { Swal.fire({ text: r.message, icon: "error",iconColor:"#f05b72", }); } } }); } } ) ; } function reset() { $("#redirectUrl").val('##'); $("#carouselRank").val(0); $("#carouselImg").attr("src", '/admin/dist/img/img-upload.png'); $("#carouselImg").attr("style", "height: 64px;width: 64px;display:block;"); $('#edit-error-msg').css("display", "none"); } ================================================ FILE: src/main/resources/static/admin/dist/js/newbee_mall_category.js ================================================ $(function () { var categoryLevel = $("#categoryLevel").val(); var parentId = $("#parentId").val(); $("#jqGrid").jqGrid({ url: '/admin/categories/list?categoryLevel=' + categoryLevel + '&parentId=' + parentId, datatype: "json", colModel: [ {label: 'id', name: 'categoryId', index: 'categoryId', width: 50, key: true, hidden: true}, {label: '分类名称', name: 'categoryName', index: 'categoryName', width: 240}, {label: '排序值', name: 'categoryRank', index: 'categoryRank', width: 120}, {label: '添加时间', name: 'createTime', index: 'createTime', width: 120} ], height: 560, rowNum: 10, rowList: [10, 20, 50], styleUI: 'Bootstrap', loadtext: '信息读取中...', rownumbers: false, rownumWidth: 20, autowidth: true, multiselect: true, pager: "#jqGridPager", jsonReader: { root: "data.list", page: "data.currPage", total: "data.totalPage", records: "data.totalCount" }, prmNames: { page: "page", rows: "limit", order: "order", }, gridComplete: function () { //隐藏grid底部滚动条 $("#jqGrid").closest(".ui-jqgrid-bdiv").css({"overflow-x": "hidden"}); } }); $(window).resize(function () { $("#jqGrid").setGridWidth($(".card-body").width()); }); }); /** * jqGrid重新加载 */ function reload() { var page = $("#jqGrid").jqGrid('getGridParam', 'page'); $("#jqGrid").jqGrid('setGridParam', { page: page }).trigger("reloadGrid"); } function categoryAdd() { reset(); $('.modal-title').html('分类添加'); $('#categoryModal').modal('show'); } /** * 管理下级分类 */ function categoryManage() { var categoryLevel = parseInt($("#categoryLevel").val()); var parentId = $("#parentId").val(); var id = getSelectedRow(); if (id == undefined || id < 0) { return false; } if (categoryLevel == 1 || categoryLevel == 2) { categoryLevel = categoryLevel + 1; window.location.href = '/admin/categories?categoryLevel=' + categoryLevel + '&parentId=' + id + '&backParentId=' + parentId; } else { Swal.fire({ text: "无下级分类", icon: "warning",iconColor:"#dea32c", }); } } /** * 返回上一层级 */ function categoryBack() { var categoryLevel = parseInt($("#categoryLevel").val()); var backParentId = $("#backParentId").val(); if (categoryLevel == 2 || categoryLevel == 3) { categoryLevel = categoryLevel - 1; window.location.href = '/admin/categories?categoryLevel=' + categoryLevel + '&parentId=' + backParentId + '&backParentId=0'; } else { Swal.fire({ text: "无上级分类", icon: "warning",iconColor:"#dea32c", }); } } //绑定modal上的保存按钮 $('#saveButton').click(function () { var categoryName = $("#categoryName").val(); var categoryLevel = $("#categoryLevel").val(); var parentId = $("#parentId").val(); var categoryRank = $("#categoryRank").val(); if (!validCN_ENString2_18(categoryName)) { $('#edit-error-msg').css("display", "block"); $('#edit-error-msg').html("请输入符合规范的分类名称!"); } else { var data = { "categoryName": categoryName, "categoryLevel": categoryLevel, "parentId": parentId, "categoryRank": categoryRank }; var url = '/admin/categories/save'; var id = getSelectedRowWithoutAlert(); if (id != null) { url = '/admin/categories/update'; data = { "categoryId": id, "categoryName": categoryName, "categoryLevel": categoryLevel, "parentId": parentId, "categoryRank": categoryRank }; } $.ajax({ type: 'POST',//方法类型 url: url, contentType: 'application/json', data: JSON.stringify(data), success: function (result) { if (result.resultCode == 200) { $('#categoryModal').modal('hide'); Swal.fire({ text: "保存成功", icon: "success",iconColor:"#1d953f", }); reload(); } else { $('#categoryModal').modal('hide'); Swal.fire({ text: result.message, icon: "error",iconColor:"#f05b72", }); } ; }, error: function () { Swal.fire({ text: "操作失败", icon: "error",iconColor:"#f05b72", }); } }); } }); function categoryEdit() { reset(); var id = getSelectedRow(); if (id == null) { return; } var rowData = $("#jqGrid").jqGrid("getRowData", id); $('.modal-title').html('分类编辑'); $('#categoryModal').modal('show'); $("#categoryId").val(id); $("#categoryName").val(rowData.categoryName); $("#categoryRank").val(rowData.categoryRank); } /** * 分类的删除会牵涉到多级分类的修改和商品数据的修改,请谨慎删除分类数据, * 如果在商城页面不想显示相关分类可以通过调整rank值来调整显示顺序, * 不过代码我也写了一部分,如果想保留删除功能的话可以在此代码的基础上进行修改。 */ function deleteCagegory() { var ids = getSelectedRows(); if (ids == null) { return; } Swal.fire({ title: "确认弹框", text: "确认要删除数据吗?", icon: "warning",iconColor:"#dea32c", showCancelButton: true, confirmButtonText: '确认', cancelButtonText: '取消' }).then((flag) => { if (flag.value) { $.ajax({ type: "POST", url: "/admin/categories/delete", contentType: "application/json", data: JSON.stringify(ids), success: function (r) { if (r.resultCode == 200) { Swal.fire({ text: "删除成功", icon: "success",iconColor:"#1d953f", }); $("#jqGrid").trigger("reloadGrid"); } else { Swal.fire({ text: r.message, icon: "error",iconColor:"#f05b72", }); } } }); } } ) ; } function reset() { $("#categoryName").val(''); $("#categoryRank").val(0); $('#edit-error-msg').css("display", "none"); } ================================================ FILE: src/main/resources/static/admin/dist/js/newbee_mall_goods.js ================================================ $(function () { $("#jqGrid").jqGrid({ url: '/admin/goods/list', datatype: "json", colModel: [ {label: '商品编号', name: 'goodsId', index: 'goodsId', width: 60, key: true}, {label: '商品名', name: 'goodsName', index: 'goodsName', width: 120}, {label: '商品简介', name: 'goodsIntro', index: 'goodsIntro', width: 120}, {label: '商品图片', name: 'goodsCoverImg', index: 'goodsCoverImg', width: 120, formatter: coverImageFormatter}, {label: '商品库存', name: 'stockNum', index: 'stockNum', width: 60}, {label: '商品售价', name: 'sellingPrice', index: 'sellingPrice', width: 60}, { label: '上架状态', name: 'goodsSellStatus', index: 'goodsSellStatus', width: 80, formatter: goodsSellStatusFormatter }, {label: '创建时间', name: 'createTime', index: 'createTime', width: 60} ], height: 760, rowNum: 20, rowList: [20, 50, 80], styleUI: 'Bootstrap', loadtext: '信息读取中...', rownumbers: false, rownumWidth: 20, autowidth: true, multiselect: true, pager: "#jqGridPager", jsonReader: { root: "data.list", page: "data.currPage", total: "data.totalPage", records: "data.totalCount" }, prmNames: { page: "page", rows: "limit", order: "order", }, gridComplete: function () { //隐藏grid底部滚动条 $("#jqGrid").closest(".ui-jqgrid-bdiv").css({"overflow-x": "hidden"}); } }); $(window).resize(function () { $("#jqGrid").setGridWidth($(".card-body").width()); }); function goodsSellStatusFormatter(cellvalue) { //商品上架状态 0-上架 1-下架 if (cellvalue == 0) { return ""; } if (cellvalue == 1) { return ""; } } function coverImageFormatter(cellvalue) { return "商品主图"; } }); /** * jqGrid重新加载 */ function reload() { initFlatPickr(); var page = $("#jqGrid").jqGrid('getGridParam', 'page'); $("#jqGrid").jqGrid('setGridParam', { page: page }).trigger("reloadGrid"); } /** * 添加商品 */ function addGoods() { window.location.href = "/admin/goods/edit"; } /** * 修改商品 */ function editGoods() { var id = getSelectedRow(); if (id == null) { return; } window.location.href = "/admin/goods/edit/" + id; } /** * 上架 */ function putUpGoods() { var ids = getSelectedRows(); if (ids == null) { return; } Swal.fire({ title: "确认弹框", text: "确认要执行上架操作吗?", icon: "warning",iconColor:"#dea32c", showCancelButton: true, confirmButtonText: '确认', cancelButtonText: '取消' }).then((flag) => { if (flag.value) { $.ajax({ type: "PUT", url: "/admin/goods/status/0", contentType: "application/json", data: JSON.stringify(ids), success: function (r) { if (r.resultCode == 200) { Swal.fire({ text: "上架成功", icon: "success",iconColor:"#1d953f", }); $("#jqGrid").trigger("reloadGrid"); } else { Swal.fire({ text: r.message, icon: "error",iconColor:"#f05b72", }); } } }); } } ) ; } /** * 下架 */ function putDownGoods() { var ids = getSelectedRows(); if (ids == null) { return; } Swal.fire({ title: "确认弹框", text: "确认要执行下架操作吗?", icon: "warning",iconColor:"#dea32c", showCancelButton: true, confirmButtonText: '确认', cancelButtonText: '取消' }).then((flag) => { if (flag.value) { $.ajax({ type: "PUT", url: "/admin/goods/status/1", contentType: "application/json", data: JSON.stringify(ids), success: function (r) { if (r.resultCode == 200) { Swal.fire({ text: "下架成功", icon: "success",iconColor:"#1d953f", }); $("#jqGrid").trigger("reloadGrid"); } else { Swal.fire({ text: r.message, icon: "error",iconColor:"#f05b72", }); } } }); } } ) ; } ================================================ FILE: src/main/resources/static/admin/dist/js/newbee_mall_goods_edit.js ================================================ var editor; $(function () { //富文本编辑器 用于商品详情编辑 const E = window.wangEditor; const editorConfig = { MENU_CONF: {} } editorConfig.MENU_CONF['uploadImage'] = { //配置服务端图片上传地址 server: '/admin/upload/files', // 超时时间5s timeout: 5 * 1000, fieldName: 'files', // 选择文件时的类型限制,默认为 ['image/*'] allowedFileTypes: ['image/*'], // 限制图片大小 4M maxFileSize: 4 * 1024 * 1024, base64LimitSize: 5 * 1024, onBeforeUpload(file) { console.log('onBeforeUpload', file) return file // will upload this file // return false // prevent upload }, onProgress(progress) { console.log('onProgress', progress) }, onSuccess(file, res) { console.log('onSuccess', file, res) }, onFailed(file, res) { alert(res.message) console.log('onFailed', file, res) }, onError(file, err, res) { alert(err.message) console.error('onError', file, err, res) }, customInsert: function (result,insertImgFn) { if (result != null && result.resultCode == 200) { // insertImgFn 可把图片插入到编辑器,传入图片 src ,执行函数即可 result.data.forEach(img => { insertImgFn(img) }); } else if (result != null && result.resultCode != 200) { Swal.fire({ text: result.message, icon: "error",iconColor:"#f05b72", }); } else { Swal.fire({ text: "error", icon: "error",iconColor:"#f05b72", }); } } } editor = E.createEditor({ selector: '#editor-text-area', html: $(".editor-text").val(), config: editorConfig }) const toolbar = E.createToolbar({ editor, selector: '#editor-toolbar', }) //图片上传插件初始化 用于商品预览图上传 new AjaxUpload('#uploadGoodsCoverImg', { action: '/admin/upload/file', name: 'file', autoSubmit: true, responseType: "json", onSubmit: function (file, extension) { if (!(extension && /^(jpg|jpeg|png|gif)$/.test(extension.toLowerCase()))) { Swal.fire({ text: "只支持jpg、png、gif格式的文件!", icon: "error",iconColor:"#f05b72", }); return false; } }, onComplete: function (file, r) { if (r != null && r.resultCode == 200) { $("#goodsCoverImg").attr("src", r.data); $("#goodsCoverImg").attr("style", "width: 128px;height: 128px;display:block;"); return false; } else if (r != null && r.resultCode != 200) { Swal.fire({ text: r.message, icon: "error",iconColor:"#f05b72", }); return false; } else { Swal.fire({ text: "error", icon: "error",iconColor:"#f05b72", }); } } }); }); $('#saveButton').click(function () { var goodsId = $('#goodsId').val(); var goodsCategoryId = $('#levelThree option:selected').val(); var goodsName = $('#goodsName').val(); var tag = $('#tag').val(); var originalPrice = $('#originalPrice').val(); var sellingPrice = $('#sellingPrice').val(); var goodsIntro = $('#goodsIntro').val(); var stockNum = $('#stockNum').val(); var goodsSellStatus = $("input[name='goodsSellStatus']:checked").val(); var goodsDetailContent = editor.getHtml(); var goodsCoverImg = $('#goodsCoverImg')[0].src; if (isNull(goodsCategoryId)) { Swal.fire({ text: "请选择分类", icon: "error",iconColor:"#f05b72", }); return; } if (isNull(goodsName)) { Swal.fire({ text: "请输入商品名称", icon: "error",iconColor:"#f05b72", }); return; } if (!validLength(goodsName, 100)) { Swal.fire({ text: "商品名称过长", icon: "error",iconColor:"#f05b72", }); return; } if (isNull(tag)) { Swal.fire({ text: "请输入商品小标签", icon: "error",iconColor:"#f05b72", }); return; } if (!validLength(tag, 100)) { Swal.fire({ text: "标签过长", icon: "error",iconColor:"#f05b72", }); return; } if (isNull(goodsIntro)) { Swal.fire({ text: "请输入商品简介", icon: "error",iconColor:"#f05b72", }); return; } if (!validLength(goodsIntro, 100)) { Swal.fire({ text: "简介过长", icon: "error",iconColor:"#f05b72", }); return; } if (isNull(originalPrice) || originalPrice < 1) { Swal.fire({ text: "请输入商品价格", icon: "error",iconColor:"#f05b72", }); return; } if (isNull(sellingPrice) || sellingPrice < 1) { Swal.fire({ text: "请输入商品售卖价", icon: "error",iconColor:"#f05b72", }); return; } if (isNull(stockNum) || sellingPrice < 0) { Swal.fire({ text: "请输入商品库存数", icon: "error",iconColor:"#f05b72", }); return; } if (isNull(goodsSellStatus)) { Swal.fire({ text: "请选择上架状态", icon: "error",iconColor:"#f05b72", }); return; } if (isNull(goodsDetailContent)) { Swal.fire({ text: "请输入商品介绍", icon: "error",iconColor:"#f05b72", }); return; } if (!validLength(goodsDetailContent, 50000)) { Swal.fire({ text: "商品介绍内容过长", icon: "error",iconColor:"#f05b72", }); return; } if (isNull(goodsCoverImg) || goodsCoverImg.indexOf('img-upload') != -1) { Swal.fire({ text: "封面图片不能为空", icon: "error",iconColor:"#f05b72", }); return; } var url = '/admin/goods/save'; var swlMessage = '保存成功'; var data = { "goodsName": goodsName, "goodsIntro": goodsIntro, "goodsCategoryId": goodsCategoryId, "tag": tag, "originalPrice": originalPrice, "sellingPrice": sellingPrice, "stockNum": stockNum, "goodsDetailContent": goodsDetailContent, "goodsCoverImg": goodsCoverImg, "goodsCarousel": goodsCoverImg, "goodsSellStatus": goodsSellStatus }; if (goodsId > 0) { url = '/admin/goods/update'; swlMessage = '修改成功'; data = { "goodsId": goodsId, "goodsName": goodsName, "goodsIntro": goodsIntro, "goodsCategoryId": goodsCategoryId, "tag": tag, "originalPrice": originalPrice, "sellingPrice": sellingPrice, "stockNum": stockNum, "goodsDetailContent": goodsDetailContent, "goodsCoverImg": goodsCoverImg, "goodsCarousel": goodsCoverImg, "goodsSellStatus": goodsSellStatus }; } console.log(data); $.ajax({ type: 'POST',//方法类型 url: url, contentType: 'application/json', data: JSON.stringify(data), success: function (result) { if (result.resultCode == 200) { Swal.fire({ text: swlMessage, icon: "success",iconColor:"#1d953f", showCancelButton: false, confirmButtonColor: '#1baeae', confirmButtonText: '返回商品列表', confirmButtonClass: 'btn btn-success', buttonsStyling: false }).then(function () { window.location.href = "/admin/goods"; }) } else { Swal.fire({ text: result.message, icon: "error",iconColor:"#f05b72", }); } ; }, error: function () { Swal.fire({ text: "操作失败", icon: "error",iconColor:"#f05b72", }); } }); }); $('#cancelButton').click(function () { window.location.href = "/admin/goods"; }); $('#levelOne').on('change', function () { $.ajax({ url: '/admin/categories/listForSelect?categoryId=' + $(this).val(), type: 'GET', success: function (result) { if (result.resultCode == 200) { var levelTwoSelect = ''; var secondLevelCategories = result.data.secondLevelCategories; var length2 = secondLevelCategories.length; for (var i = 0; i < length2; i++) { levelTwoSelect += ''; } $('#levelTwo').html(levelTwoSelect); var levelThreeSelect = ''; var thirdLevelCategories = result.data.thirdLevelCategories; var length3 = thirdLevelCategories.length; for (var i = 0; i < length3; i++) { levelThreeSelect += ''; } $('#levelThree').html(levelThreeSelect); } else { Swal.fire({ text: result.message, icon: "error",iconColor:"#f05b72", }); } ; }, error: function () { Swal.fire({ text: "操作失败", icon: "error",iconColor:"#f05b72", }); } }); }); $('#levelTwo').on('change', function () { $.ajax({ url: '/admin/categories/listForSelect?categoryId=' + $(this).val(), type: 'GET', success: function (result) { if (result.resultCode == 200) { var levelThreeSelect = ''; var thirdLevelCategories = result.data.thirdLevelCategories; var length = thirdLevelCategories.length; for (var i = 0; i < length; i++) { levelThreeSelect += ''; } $('#levelThree').html(levelThreeSelect); } else { Swal.fire({ text: result.message, icon: "error",iconColor:"#f05b72", }); } ; }, error: function () { Swal.fire({ text: "操作失败", icon: "error",iconColor:"#f05b72", }); } }); }); ================================================ FILE: src/main/resources/static/admin/dist/js/newbee_mall_index_config.js ================================================ $(function () { var configType = $("#configType").val(); $("#jqGrid").jqGrid({ url: '/admin/indexConfigs/list?configType=' + configType, datatype: "json", colModel: [ {label: 'id', name: 'configId', index: 'configId', width: 50, key: true, hidden: true}, {label: '配置项名称', name: 'configName', index: 'configName', width: 180}, {label: '跳转链接', name: 'redirectUrl', index: 'redirectUrl', width: 120}, {label: '排序值', name: 'configRank', index: 'configRank', width: 120}, {label: '商品编号', name: 'goodsId', index: 'goodsId', width: 120}, {label: '添加时间', name: 'createTime', index: 'createTime', width: 120} ], height: 560, rowNum: 10, rowList: [10, 20, 50], styleUI: 'Bootstrap', loadtext: '信息读取中...', rownumbers: false, rownumWidth: 20, autowidth: true, multiselect: true, pager: "#jqGridPager", jsonReader: { root: "data.list", page: "data.currPage", total: "data.totalPage", records: "data.totalCount" }, prmNames: { page: "page", rows: "limit", order: "order", }, gridComplete: function () { //隐藏grid底部滚动条 $("#jqGrid").closest(".ui-jqgrid-bdiv").css({"overflow-x": "hidden"}); } }); $(window).resize(function () { $("#jqGrid").setGridWidth($(".card-body").width()); }); }); /** * jqGrid重新加载 */ function reload() { var page = $("#jqGrid").jqGrid('getGridParam', 'page'); $("#jqGrid").jqGrid('setGridParam', { page: page }).trigger("reloadGrid"); } function configAdd() { reset(); $('.modal-title').html('首页配置项添加'); $('#indexConfigModal').modal('show'); } //绑定modal上的保存按钮 $('#saveButton').click(function () { var configName = $("#configName").val(); var configType = $("#configType").val(); var redirectUrl = $("#redirectUrl").val(); var goodsId = $("#goodsId").val(); var configRank = $("#configRank").val(); if (!validCN_ENString2_18(configName)) { $('#edit-error-msg').css("display", "block"); $('#edit-error-msg').html("请输入符合规范的配置项名称!"); } else { var data = { "configName": configName, "configType": configType, "redirectUrl": redirectUrl, "goodsId": goodsId, "configRank": configRank }; var url = '/admin/indexConfigs/save'; var id = getSelectedRowWithoutAlert(); if (id != null) { url = '/admin/indexConfigs/update'; data = { "configId": id, "configName": configName, "configType": configType, "redirectUrl": redirectUrl, "goodsId": goodsId, "configRank": configRank }; } $.ajax({ type: 'POST',//方法类型 url: url, contentType: 'application/json', data: JSON.stringify(data), success: function (result) { if (result.resultCode == 200) { $('#indexConfigModal').modal('hide'); Swal.fire({ text: "保存成功", icon: "success",iconColor:"#1d953f", }); reload(); } else { $('#indexConfigModal').modal('hide'); Swal.fire({ text: result.message, icon: "error",iconColor:"#f05b72", }); } ; }, error: function () { Swal.fire({ text: "操作失败", icon: "error",iconColor:"#f05b72", }); } }); } }); function configEdit() { reset(); var id = getSelectedRow(); if (id == null) { return; } var rowData = $("#jqGrid").jqGrid("getRowData", id); $('.modal-title').html('首页配置项编辑'); $('#indexConfigModal').modal('show'); $("#configId").val(id); $("#configName").val(rowData.configName); $("#redirectUrl").val(rowData.redirectUrl); $("#goodsId").val(rowData.goodsId); $("#configRank").val(rowData.configRank); } function deleteConfig () { var ids = getSelectedRows(); if (ids == null) { return; } Swal.fire({ title: "确认弹框", text: "确认要删除数据吗?", icon: "warning",iconColor:"#dea32c", showCancelButton: true, confirmButtonText: '确认', cancelButtonText: '取消' }).then((flag) => { if (flag.value) { $.ajax({ type: "POST", url: "/admin/indexConfigs/delete", contentType: "application/json", data: JSON.stringify(ids), success: function (r) { if (r.resultCode == 200) { Swal.fire({ text: "删除成功", icon: "success",iconColor:"#1d953f", }); $("#jqGrid").trigger("reloadGrid"); } else { Swal.fire({ text: r.message, icon: "error",iconColor:"#f05b72", }); } } }); } } ) ; } function reset() { $("#configName").val(''); $("#redirectUrl").val('##'); $("#configRank").val(0); $("#goodsId").val(0); $('#edit-error-msg').css("display", "none"); } ================================================ FILE: src/main/resources/static/admin/dist/js/newbee_mall_order.js ================================================ $(function () { $("#jqGrid").jqGrid({ url: '/admin/orders/list', datatype: "json", colModel: [ {label: 'id', name: 'orderId', index: 'orderId', width: 50, key: true, hidden: true}, {label: '订单号', name: 'orderNo', index: 'orderNo', width: 120}, {label: '订单总价', name: 'totalPrice', index: 'totalPrice', width: 60}, {label: '订单状态', name: 'orderStatus', index: 'orderStatus', width: 80, formatter: orderStatusFormatter}, {label: '支付方式', name: 'payType', index: 'payType', width: 80,formatter:payTypeFormatter}, {label: '收件人地址', name: 'userAddress', index: 'userAddress', width: 10, hidden: true}, {label: '创建时间', name: 'createTime', index: 'createTime', width: 120}, {label: '操作', name: 'createTime', index: 'createTime', width: 120, formatter: operateFormatter} ], height: 760, rowNum: 20, rowList: [20, 50, 80], styleUI: 'Bootstrap', loadtext: '信息读取中...', rownumbers: false, rownumWidth: 20, autowidth: true, multiselect: true, pager: "#jqGridPager", jsonReader: { root: "data.list", page: "data.currPage", total: "data.totalPage", records: "data.totalCount" }, prmNames: { page: "page", rows: "limit", order: "order", }, gridComplete: function () { //隐藏grid底部滚动条 $("#jqGrid").closest(".ui-jqgrid-bdiv").css({"overflow-x": "hidden"}); } }); $(window).resize(function () { $("#jqGrid").setGridWidth($(".card-body").width()); }); function operateFormatter(cellvalue, rowObject) { return "查看订单信息" + "
" + "查看收件人信息"; } function orderStatusFormatter(cellvalue) { //订单状态:0.待支付 1.已支付 2.配货完成 3:出库成功 4.交易成功 -1.手动关闭 -2.超时关闭 -3.商家关闭 if (cellvalue == 0) { return "待支付"; } if (cellvalue == 1) { return "已支付"; } if (cellvalue == 2) { return "配货完成"; } if (cellvalue == 3) { return "出库成功"; } if (cellvalue == 4) { return "交易成功"; } if (cellvalue == -1) { return "手动关闭"; } if (cellvalue == -2) { return "超时关闭"; } if (cellvalue == -3) { return "商家关闭"; } } function payTypeFormatter(cellvalue) { //支付类型:0.无 1.支付宝支付 2.微信支付 if (cellvalue == 0) { return "无"; } if (cellvalue == 1) { return "支付宝支付"; } if (cellvalue == 2) { return "微信支付"; } } $(window).resize(function () { $("#jqGrid").setGridWidth($(".card-body").width()); }); }); /** * jqGrid重新加载 */ function reload() { initFlatPickr(); var page = $("#jqGrid").jqGrid('getGridParam', 'page'); $("#jqGrid").jqGrid('setGridParam', { page: page }).trigger("reloadGrid"); } /** * 查看订单项信息 * @param orderId */ function openOrderItems(orderId) { $('.modal-title').html('订单详情'); $.ajax({ type: 'GET',//方法类型 url: '/admin/order-items/' + orderId, contentType: 'application/json', success: function (result) { if (result.resultCode == 200) { $('#orderItemModal').modal('show'); var itemString = ''; for (i = 0; i < result.data.length; i++) { itemString += result.data[i].goodsName + ' x ' + result.data[i].goodsCount + ' 商品编号 ' + result.data[i].goodsId + ";
"; } $("#orderItemString").html(itemString); } else { Swal.fire({ text: result.message, icon: "error",iconColor:"#f05b72", }); } ; }, error: function () { Swal.fire({ text: "操作失败", icon: "error",iconColor:"#f05b72", }); } }); } /** * 查看收件人信息 * @param orderId */ function openExpressInfo(orderId) { var rowData = $("#jqGrid").jqGrid("getRowData", orderId); $('.modal-title').html('收件信息'); $('#expressInfoModal').modal('show'); $("#userAddressInfo").html(rowData.userAddress); } /** * 订单编辑 */ function orderEdit() { reset(); var id = getSelectedRow(); if (id == null) { return; } var rowData = $("#jqGrid").jqGrid("getRowData", id); $('.modal-title').html('订单编辑'); $('#orderInfoModal').modal('show'); $("#orderId").val(id); $("#userAddress").val(rowData.userAddress); $("#totalPrice").val(rowData.totalPrice); } //绑定modal上的保存按钮 $('#saveButton').click(function () { var totalPrice = $("#totalPrice").val(); var userName = $("#userName").val(); var userPhone = $("#userPhone").val(); var userAddress = $("#userAddress").val(); var id = getSelectedRowWithoutAlert(); var url = '/admin/orders/update'; var data = { "orderId": id, "totalPrice": totalPrice, "userName": userName, "userPhone": userPhone, "userAddress": userAddress }; $.ajax({ type: 'POST',//方法类型 url: url, contentType: 'application/json', data: JSON.stringify(data), success: function (result) { if (result.resultCode == 200) { $('#orderInfoModal').modal('hide'); Swal.fire({ text: "保存成功", icon: "success",iconColor:"#1d953f", }); reload(); } else { $('#orderInfoModal').modal('hide'); Swal.fire({ text: result.message, icon: "error",iconColor:"#f05b72", }); } ; }, error: function () { Swal.fire({ text: "操作失败", icon: "error",iconColor:"#f05b72", }); } }); }); /** * 订单拣货完成 */ function orderCheckDone() { var ids = getSelectedRows(); if (ids == null) { return; } var orderNos = ''; for (i = 0; i < ids.length; i++) { var rowData = $("#jqGrid").jqGrid("getRowData", ids[i]); if (rowData.orderStatus != '已支付') { orderNos += rowData.orderNo + " "; } } if (orderNos.length > 0 & orderNos.length < 100) { Swal.fire({ text: orderNos + "订单的状态不是支付成功无法执行配货完成操作", icon: "error",iconColor:"#f05b72", }); return; } if (orderNos.length >= 100) { Swal.fire({ text: "你选择了太多状态不是支付成功的订单,无法执行配货完成操作", icon: "error",iconColor:"#f05b72", }); return; } Swal.fire({ title: "确认弹框", text: "确认要执行配货完成操作吗?", icon: "warning",iconColor:"#dea32c", showCancelButton: true, confirmButtonText: '确认', cancelButtonText: '取消' }).then((flag) => { if (flag.value) { $.ajax({ type: "POST", url: "/admin/orders/checkDone", contentType: "application/json", data: JSON.stringify(ids), success: function (r) { if (r.resultCode == 200) { Swal.fire({ text: "配货完成", icon: "success",iconColor:"#1d953f", }); $("#jqGrid").trigger("reloadGrid"); } else { Swal.fire({ text: r.message, icon: "error",iconColor:"#f05b72", }); } } }); } } ) ; } /** * 订单出库 */ function orderCheckOut() { var ids = getSelectedRows(); if (ids == null) { return; } var orderNos = ''; for (i = 0; i < ids.length; i++) { var rowData = $("#jqGrid").jqGrid("getRowData", ids[i]); if (rowData.orderStatus != '已支付' && rowData.orderStatus != '配货完成') { orderNos += rowData.orderNo + " "; } } if (orderNos.length > 0 & orderNos.length < 100) { Swal.fire({ text: orderNos + "订单的状态不是支付成功或配货完成无法执行出库操作", icon: "error",iconColor:"#f05b72", }); return; } if (orderNos.length >= 100) { Swal.fire({ text: "你选择了太多状态不是支付成功或配货完成的订单,无法执行出库操作", icon: "error",iconColor:"#f05b72", }); return; } Swal.fire({ title: "确认弹框", text: "确认要执行出库操作吗?", icon: "warning",iconColor:"#dea32c", showCancelButton: true, confirmButtonText: '确认', cancelButtonText: '取消' }).then((flag) => { if (flag.value) { $.ajax({ type: "POST", url: "/admin/orders/checkOut", contentType: "application/json", data: JSON.stringify(ids), success: function (r) { if (r.resultCode == 200) { Swal.fire({ text: "出库成功", icon: "success",iconColor:"#1d953f", }); $("#jqGrid").trigger("reloadGrid"); } else { Swal.fire({ text: r.message, icon: "error",iconColor:"#f05b72", }); } } }); } } ) ; } function closeOrder() { var ids = getSelectedRows(); if (ids == null) { return; } Swal.fire({ title: "确认弹框", text: "确认要关闭订单吗?", icon: "warning",iconColor:"#dea32c", showCancelButton: true, confirmButtonText: '确认', cancelButtonText: '取消' }).then((flag) => { if (flag.value) { $.ajax({ type: "POST", url: "/admin/orders/close", contentType: "application/json", data: JSON.stringify(ids), success: function (r) { if (r.resultCode == 200) { Swal.fire({ text: "关闭成功", icon: "success",iconColor:"#1d953f", }); $("#jqGrid").trigger("reloadGrid"); } else { Swal.fire({ text: r.message, icon: "error",iconColor:"#f05b72", }); } } }); } } ) ; } function reset() { $("#totalPrice").val(0); $("#userAddress").val(''); $('#edit-error-msg').css("display", "none"); } ================================================ FILE: src/main/resources/static/admin/dist/js/newbee_mall_user.js ================================================ $(function () { $("#jqGrid").jqGrid({ url: '/admin/users/list', datatype: "json", colModel: [ {label: 'id', name: 'userId', index: 'userId', width: 50, key: true, hidden: true}, {label: '昵称', name: 'nickName', index: 'nickName', width: 180}, {label: '登录名', name: 'loginName', index: 'loginName', width: 120}, {label: '身份状态', name: 'lockedFlag', index: 'lockedFlag', width: 60, formatter: lockedFormatter}, {label: '是否注销', name: 'isDeleted', index: 'isDeleted', width: 60, formatter: deletedFormatter}, {label: '注册时间', name: 'createTime', index: 'createTime', width: 120} ], height: 560, rowNum: 10, rowList: [10, 20, 50], styleUI: 'Bootstrap', loadtext: '信息读取中...', rownumbers: false, rownumWidth: 20, autowidth: true, multiselect: true, pager: "#jqGridPager", jsonReader: { root: "data.list", page: "data.currPage", total: "data.totalPage", records: "data.totalCount" }, prmNames: { page: "page", rows: "limit", order: "order", }, gridComplete: function () { //隐藏grid底部滚动条 $("#jqGrid").closest(".ui-jqgrid-bdiv").css({"overflow-x": "hidden"}); } }); $(window).resize(function () { $("#jqGrid").setGridWidth($(".card-body").width()); }); function lockedFormatter(cellvalue) { if (cellvalue == 1) { return ""; } else if (cellvalue == 0) { return ""; } } function deletedFormatter(cellvalue) { if (cellvalue == 1) { return ""; } else if (cellvalue == 0) { return ""; } } }); /** * jqGrid重新加载 */ function reload() { var page = $("#jqGrid").jqGrid('getGridParam', 'page'); $("#jqGrid").jqGrid('setGridParam', { page: page }).trigger("reloadGrid"); } function lockUser(lockStatus) { var ids = getSelectedRows(); if (ids == null) { return; } if (lockStatus != 0 && lockStatus != 1) { Swal.fire({ text: '非法操作', icon: "error",iconColor:"#f05b72", }); } Swal.fire({ title: "确认弹框", text: "确认要修改账号状态吗?", icon: "warning",iconColor:"#dea32c", showCancelButton: true, confirmButtonText: '确认', cancelButtonText: '取消' }).then((flag) => { if (flag.value) { $.ajax({ type: "POST", url: "/admin/users/lock/" + lockStatus, contentType: "application/json", data: JSON.stringify(ids), success: function (r) { if (r.resultCode == 200) { Swal.fire({ text: "操作成功", icon: "success",iconColor:"#1d953f", }); $("#jqGrid").trigger("reloadGrid"); } else { Swal.fire({ text: r.message, icon: "error",iconColor:"#f05b72", }); } } }); } } ) ; } ================================================ FILE: src/main/resources/static/admin/dist/js/plugins/bootstrap/css/bootstrap-grid.css ================================================ ================================================ FILE: src/main/resources/static/admin/dist/js/plugins/bootstrap/css/bootstrap-reboot.css ================================================ ================================================ FILE: src/main/resources/static/admin/dist/js/plugins/bootstrap/css/bootstrap.css ================================================ ================================================ FILE: src/main/resources/static/admin/dist/js/plugins/bootstrap/js/bootstrap.bundle.js ================================================ ================================================ FILE: src/main/resources/static/admin/dist/js/plugins/bootstrap/js/bootstrap.js ================================================ ================================================ FILE: src/main/resources/static/admin/dist/js/plugins/chart.js ================================================ /*! * Chart.js * http://chartjs.org/ * Version: 2.7.1 * * Copyright 2017 Nick Downie * Released under the MIT license * https://github.com/chartjs/Chart.js/blob/master/LICENSE.md */ !function(t){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{("undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this).Chart=t()}}(function(){return function t(e,n,i){function a(o,s){if(!n[o]){if(!e[o]){var l="function"==typeof require&&require;if(!s&&l)return l(o,!0);if(r)return r(o,!0);var u=new Error("Cannot find module '"+o+"'");throw u.code="MODULE_NOT_FOUND",u}var d=n[o]={exports:{}};e[o][0].call(d.exports,function(t){var n=e[o][1][t];return a(n||t)},d,d.exports,t,e,n,i)}return n[o].exports}for(var r="function"==typeof require&&require,o=0;on?(e+.05)/(n+.05):(n+.05)/(e+.05)},level:function(t){var e=this.contrast(t);return e>=7.1?"AAA":e>=4.5?"AA":""},dark:function(){var t=this.values.rgb;return(299*t[0]+587*t[1]+114*t[2])/1e3<128},light:function(){return!this.dark()},negate:function(){for(var t=[],e=0;e<3;e++)t[e]=255-this.values.rgb[e];return this.setValues("rgb",t),this},lighten:function(t){var e=this.values.hsl;return e[2]+=e[2]*t,this.setValues("hsl",e),this},darken:function(t){var e=this.values.hsl;return e[2]-=e[2]*t,this.setValues("hsl",e),this},saturate:function(t){var e=this.values.hsl;return e[1]+=e[1]*t,this.setValues("hsl",e),this},desaturate:function(t){var e=this.values.hsl;return e[1]-=e[1]*t,this.setValues("hsl",e),this},whiten:function(t){var e=this.values.hwb;return e[1]+=e[1]*t,this.setValues("hwb",e),this},blacken:function(t){var e=this.values.hwb;return e[2]+=e[2]*t,this.setValues("hwb",e),this},greyscale:function(){var t=this.values.rgb,e=.3*t[0]+.59*t[1]+.11*t[2];return this.setValues("rgb",[e,e,e]),this},clearer:function(t){var e=this.values.alpha;return this.setValues("alpha",e-e*t),this},opaquer:function(t){var e=this.values.alpha;return this.setValues("alpha",e+e*t),this},rotate:function(t){var e=this.values.hsl,n=(e[0]+t)%360;return e[0]=n<0?360+n:n,this.setValues("hsl",e),this},mix:function(t,e){var n=this,i=t,a=void 0===e?.5:e,r=2*a-1,o=n.alpha()-i.alpha(),s=((r*o==-1?r:(r+o)/(1+r*o))+1)/2,l=1-s;return this.rgb(s*n.red()+l*i.red(),s*n.green()+l*i.green(),s*n.blue()+l*i.blue()).alpha(n.alpha()*a+i.alpha()*(1-a))},toJSON:function(){return this.rgb()},clone:function(){var t,e,n=new r,i=this.values,a=n.values;for(var o in i)i.hasOwnProperty(o)&&(t=i[o],"[object Array]"===(e={}.toString.call(t))?a[o]=t.slice(0):"[object Number]"===e?a[o]=t:console.error("unexpected color value:",t));return n}},r.prototype.spaces={rgb:["red","green","blue"],hsl:["hue","saturation","lightness"],hsv:["hue","saturation","value"],hwb:["hue","whiteness","blackness"],cmyk:["cyan","magenta","yellow","black"]},r.prototype.maxes={rgb:[255,255,255],hsl:[360,100,100],hsv:[360,100,100],hwb:[360,100,100],cmyk:[100,100,100,100]},r.prototype.getValues=function(t){for(var e=this.values,n={},i=0;i.04045?Math.pow((e+.055)/1.055,2.4):e/12.92)+.3576*(n=n>.04045?Math.pow((n+.055)/1.055,2.4):n/12.92)+.1805*(i=i>.04045?Math.pow((i+.055)/1.055,2.4):i/12.92)),100*(.2126*e+.7152*n+.0722*i),100*(.0193*e+.1192*n+.9505*i)]}function d(t){var e,n,i,a=u(t),r=a[0],o=a[1],s=a[2];return r/=95.047,o/=100,s/=108.883,r=r>.008856?Math.pow(r,1/3):7.787*r+16/116,o=o>.008856?Math.pow(o,1/3):7.787*o+16/116,s=s>.008856?Math.pow(s,1/3):7.787*s+16/116,e=116*o-16,n=500*(r-o),i=200*(o-s),[e,n,i]}function c(t){var e,n,i,a,r,o=t[0]/360,s=t[1]/100,l=t[2]/100;if(0==s)return r=255*l,[r,r,r];e=2*l-(n=l<.5?l*(1+s):l+s-l*s),a=[0,0,0];for(var u=0;u<3;u++)(i=o+1/3*-(u-1))<0&&i++,i>1&&i--,r=6*i<1?e+6*(n-e)*i:2*i<1?n:3*i<2?e+(n-e)*(2/3-i)*6:e,a[u]=255*r;return a}function h(t){var e=t[0]/60,n=t[1]/100,i=t[2]/100,a=Math.floor(e)%6,r=e-Math.floor(e),o=255*i*(1-n),s=255*i*(1-n*r),l=255*i*(1-n*(1-r)),i=255*i;switch(a){case 0:return[i,l,o];case 1:return[s,i,o];case 2:return[o,i,l];case 3:return[o,s,i];case 4:return[l,o,i];case 5:return[i,o,s]}}function f(t){var e,n,i,a,o=t[0]/360,s=t[1]/100,l=t[2]/100,u=s+l;switch(u>1&&(s/=u,l/=u),e=Math.floor(6*o),n=1-l,i=6*o-e,0!=(1&e)&&(i=1-i),a=s+i*(n-s),e){default:case 6:case 0:r=n,g=a,b=s;break;case 1:r=a,g=n,b=s;break;case 2:r=s,g=n,b=a;break;case 3:r=s,g=a,b=n;break;case 4:r=a,g=s,b=n;break;case 5:r=n,g=s,b=a}return[255*r,255*g,255*b]}function m(t){var e,n,i,a=t[0]/100,r=t[1]/100,o=t[2]/100,s=t[3]/100;return e=1-Math.min(1,a*(1-s)+s),n=1-Math.min(1,r*(1-s)+s),i=1-Math.min(1,o*(1-s)+s),[255*e,255*n,255*i]}function p(t){var e,n,i,a=t[0]/100,r=t[1]/100,o=t[2]/100;return e=3.2406*a+-1.5372*r+-.4986*o,n=-.9689*a+1.8758*r+.0415*o,i=.0557*a+-.204*r+1.057*o,e=e>.0031308?1.055*Math.pow(e,1/2.4)-.055:e*=12.92,n=n>.0031308?1.055*Math.pow(n,1/2.4)-.055:n*=12.92,i=i>.0031308?1.055*Math.pow(i,1/2.4)-.055:i*=12.92,e=Math.min(Math.max(0,e),1),n=Math.min(Math.max(0,n),1),i=Math.min(Math.max(0,i),1),[255*e,255*n,255*i]}function v(t){var e,n,i,a=t[0],r=t[1],o=t[2];return a/=95.047,r/=100,o/=108.883,a=a>.008856?Math.pow(a,1/3):7.787*a+16/116,r=r>.008856?Math.pow(r,1/3):7.787*r+16/116,o=o>.008856?Math.pow(o,1/3):7.787*o+16/116,e=116*r-16,n=500*(a-r),i=200*(r-o),[e,n,i]}function y(t){var e,n,i,a,r=t[0],o=t[1],s=t[2];return r<=8?a=(n=100*r/903.3)/100*7.787+16/116:(n=100*Math.pow((r+16)/116,3),a=Math.pow(n/100,1/3)),e=e/95.047<=.008856?e=95.047*(o/500+a-16/116)/7.787:95.047*Math.pow(o/500+a,3),i=i/108.883<=.008859?i=108.883*(a-s/200-16/116)/7.787:108.883*Math.pow(a-s/200,3),[e,n,i]}function x(t){var e,n,i,a=t[0],r=t[1],o=t[2];return e=Math.atan2(o,r),(n=360*e/2/Math.PI)<0&&(n+=360),i=Math.sqrt(r*r+o*o),[a,i,n]}function _(t){return p(y(t))}function k(t){var e,n,i,a=t[0],r=t[1];return i=t[2]/360*2*Math.PI,e=r*Math.cos(i),n=r*Math.sin(i),[a,e,n]}function w(t){return M[t]}e.exports={rgb2hsl:i,rgb2hsv:a,rgb2hwb:o,rgb2cmyk:s,rgb2keyword:l,rgb2xyz:u,rgb2lab:d,rgb2lch:function(t){return x(d(t))},hsl2rgb:c,hsl2hsv:function(t){var e,n,i=t[0],a=t[1]/100,r=t[2]/100;return 0===r?[0,0,0]:(r*=2,a*=r<=1?r:2-r,n=(r+a)/2,e=2*a/(r+a),[i,100*e,100*n])},hsl2hwb:function(t){return o(c(t))},hsl2cmyk:function(t){return s(c(t))},hsl2keyword:function(t){return l(c(t))},hsv2rgb:h,hsv2hsl:function(t){var e,n,i=t[0],a=t[1]/100,r=t[2]/100;return n=(2-a)*r,e=a*r,e/=n<=1?n:2-n,e=e||0,n/=2,[i,100*e,100*n]},hsv2hwb:function(t){return o(h(t))},hsv2cmyk:function(t){return s(h(t))},hsv2keyword:function(t){return l(h(t))},hwb2rgb:f,hwb2hsl:function(t){return i(f(t))},hwb2hsv:function(t){return a(f(t))},hwb2cmyk:function(t){return s(f(t))},hwb2keyword:function(t){return l(f(t))},cmyk2rgb:m,cmyk2hsl:function(t){return i(m(t))},cmyk2hsv:function(t){return a(m(t))},cmyk2hwb:function(t){return o(m(t))},cmyk2keyword:function(t){return l(m(t))},keyword2rgb:w,keyword2hsl:function(t){return i(w(t))},keyword2hsv:function(t){return a(w(t))},keyword2hwb:function(t){return o(w(t))},keyword2cmyk:function(t){return s(w(t))},keyword2lab:function(t){return d(w(t))},keyword2xyz:function(t){return u(w(t))},xyz2rgb:p,xyz2lab:v,xyz2lch:function(t){return x(v(t))},lab2xyz:y,lab2rgb:_,lab2lch:x,lch2lab:k,lch2xyz:function(t){return y(k(t))},lch2rgb:function(t){return _(k(t))}};var M={aliceblue:[240,248,255],antiquewhite:[250,235,215],aqua:[0,255,255],aquamarine:[127,255,212],azure:[240,255,255],beige:[245,245,220],bisque:[255,228,196],black:[0,0,0],blanchedalmond:[255,235,205],blue:[0,0,255],blueviolet:[138,43,226],brown:[165,42,42],burlywood:[222,184,135],cadetblue:[95,158,160],chartreuse:[127,255,0],chocolate:[210,105,30],coral:[255,127,80],cornflowerblue:[100,149,237],cornsilk:[255,248,220],crimson:[220,20,60],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgoldenrod:[184,134,11],darkgray:[169,169,169],darkgreen:[0,100,0],darkgrey:[169,169,169],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkseagreen:[143,188,143],darkslateblue:[72,61,139],darkslategray:[47,79,79],darkslategrey:[47,79,79],darkturquoise:[0,206,209],darkviolet:[148,0,211],deeppink:[255,20,147],deepskyblue:[0,191,255],dimgray:[105,105,105],dimgrey:[105,105,105],dodgerblue:[30,144,255],firebrick:[178,34,34],floralwhite:[255,250,240],forestgreen:[34,139,34],fuchsia:[255,0,255],gainsboro:[220,220,220],ghostwhite:[248,248,255],gold:[255,215,0],goldenrod:[218,165,32],gray:[128,128,128],green:[0,128,0],greenyellow:[173,255,47],grey:[128,128,128],honeydew:[240,255,240],hotpink:[255,105,180],indianred:[205,92,92],indigo:[75,0,130],ivory:[255,255,240],khaki:[240,230,140],lavender:[230,230,250],lavenderblush:[255,240,245],lawngreen:[124,252,0],lemonchiffon:[255,250,205],lightblue:[173,216,230],lightcoral:[240,128,128],lightcyan:[224,255,255],lightgoldenrodyellow:[250,250,210],lightgray:[211,211,211],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightsalmon:[255,160,122],lightseagreen:[32,178,170],lightskyblue:[135,206,250],lightslategray:[119,136,153],lightslategrey:[119,136,153],lightsteelblue:[176,196,222],lightyellow:[255,255,224],lime:[0,255,0],limegreen:[50,205,50],linen:[250,240,230],magenta:[255,0,255],maroon:[128,0,0],mediumaquamarine:[102,205,170],mediumblue:[0,0,205],mediumorchid:[186,85,211],mediumpurple:[147,112,219],mediumseagreen:[60,179,113],mediumslateblue:[123,104,238],mediumspringgreen:[0,250,154],mediumturquoise:[72,209,204],mediumvioletred:[199,21,133],midnightblue:[25,25,112],mintcream:[245,255,250],mistyrose:[255,228,225],moccasin:[255,228,181],navajowhite:[255,222,173],navy:[0,0,128],oldlace:[253,245,230],olive:[128,128,0],olivedrab:[107,142,35],orange:[255,165,0],orangered:[255,69,0],orchid:[218,112,214],palegoldenrod:[238,232,170],palegreen:[152,251,152],paleturquoise:[175,238,238],palevioletred:[219,112,147],papayawhip:[255,239,213],peachpuff:[255,218,185],peru:[205,133,63],pink:[255,192,203],plum:[221,160,221],powderblue:[176,224,230],purple:[128,0,128],rebeccapurple:[102,51,153],red:[255,0,0],rosybrown:[188,143,143],royalblue:[65,105,225],saddlebrown:[139,69,19],salmon:[250,128,114],sandybrown:[244,164,96],seagreen:[46,139,87],seashell:[255,245,238],sienna:[160,82,45],silver:[192,192,192],skyblue:[135,206,235],slateblue:[106,90,205],slategray:[112,128,144],slategrey:[112,128,144],snow:[255,250,250],springgreen:[0,255,127],steelblue:[70,130,180],tan:[210,180,140],teal:[0,128,128],thistle:[216,191,216],tomato:[255,99,71],turquoise:[64,224,208],violet:[238,130,238],wheat:[245,222,179],white:[255,255,255],whitesmoke:[245,245,245],yellow:[255,255,0],yellowgreen:[154,205,50]},S={};for(var D in M)S[JSON.stringify(M[D])]=D},{}],4:[function(t,e,n){var i=t(3),a=function(){return new u};for(var r in i){a[r+"Raw"]=function(t){return function(e){return"number"==typeof e&&(e=Array.prototype.slice.call(arguments)),i[t](e)}}(r);var o=/(\w+)2(\w+)/.exec(r),s=o[1],l=o[2];(a[s]=a[s]||{})[l]=a[r]=function(t){return function(e){"number"==typeof e&&(e=Array.prototype.slice.call(arguments));var n=i[t](e);if("string"==typeof n||void 0===n)return n;for(var a=0;a0)for(n=0;n=0?n?"+":"":"-")+Math.pow(10,Math.max(0,a)).toString().substr(1)+i}function N(t,e,n,i){var a=i;"string"==typeof i&&(a=function(){return this[i]()}),t&&(Re[t]=a),e&&(Re[e[0]]=function(){return Y(a.apply(this,arguments),e[1],e[2])}),n&&(Re[n]=function(){return this.localeData().ordinal(a.apply(this,arguments),t)})}function z(t){return t.match(/\[[\s\S]/)?t.replace(/^\[|\]$/g,""):t.replace(/\\/g,"")}function B(t){var e,n,i=t.match(Ie);for(e=0,n=i.length;e=0&&Oe.test(t);)t=t.replace(Oe,function(t){return e.longDateFormat(t)||t}),Oe.lastIndex=0,n-=1;return t}function E(t,e,n){Ke[t]=D(e)?e:function(t,i){return t&&n?n:e}}function j(t,e){return d(Ke,t)?Ke[t](e._strict,e._locale):new RegExp(U(t))}function U(t){return q(t.replace("\\","").replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g,function(t,e,n,i,a){return e||n||i||a}))}function q(t){return t.replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&")}function G(t,e){var n,i=e;for("string"==typeof t&&(t=[t]),s(e)&&(i=function(t,n){n[e]=_(t)}),n=0;n=0&&isFinite(s.getFullYear())&&s.setFullYear(t),s}function at(t){var e=new Date(Date.UTC.apply(null,arguments));return t<100&&t>=0&&isFinite(e.getUTCFullYear())&&e.setUTCFullYear(t),e}function rt(t,e,n){var i=7+e-n;return-((7+at(t,0,i).getUTCDay()-e)%7)+i-1}function ot(t,e,n,i,a){var r,o,s=1+7*(e-1)+(7+n-i)%7+rt(t,i,a);return s<=0?o=et(r=t-1)+s:s>et(t)?(r=t+1,o=s-et(t)):(r=t,o=s),{year:r,dayOfYear:o}}function st(t,e,n){var i,a,r=rt(t.year(),e,n),o=Math.floor((t.dayOfYear()-r-1)/7)+1;return o<1?i=o+lt(a=t.year()-1,e,n):o>lt(t.year(),e,n)?(i=o-lt(t.year(),e,n),a=t.year()+1):(a=t.year(),i=o),{week:i,year:a}}function lt(t,e,n){var i=rt(t,e,n),a=rt(t+1,e,n);return(et(t)-i+a)/7}function ut(t,e){return"string"!=typeof t?t:isNaN(t)?"number"==typeof(t=e.weekdaysParse(t))?t:null:parseInt(t,10)}function dt(t,e){return"string"==typeof t?e.weekdaysParse(t)%7||7:isNaN(t)?null:t}function ct(t,e,n){var i,a,r,o=t.toLocaleLowerCase();if(!this._weekdaysParse)for(this._weekdaysParse=[],this._shortWeekdaysParse=[],this._minWeekdaysParse=[],i=0;i<7;++i)r=h([2e3,1]).day(i),this._minWeekdaysParse[i]=this.weekdaysMin(r,"").toLocaleLowerCase(),this._shortWeekdaysParse[i]=this.weekdaysShort(r,"").toLocaleLowerCase(),this._weekdaysParse[i]=this.weekdays(r,"").toLocaleLowerCase();return n?"dddd"===e?-1!==(a=un.call(this._weekdaysParse,o))?a:null:"ddd"===e?-1!==(a=un.call(this._shortWeekdaysParse,o))?a:null:-1!==(a=un.call(this._minWeekdaysParse,o))?a:null:"dddd"===e?-1!==(a=un.call(this._weekdaysParse,o))?a:-1!==(a=un.call(this._shortWeekdaysParse,o))?a:-1!==(a=un.call(this._minWeekdaysParse,o))?a:null:"ddd"===e?-1!==(a=un.call(this._shortWeekdaysParse,o))?a:-1!==(a=un.call(this._weekdaysParse,o))?a:-1!==(a=un.call(this._minWeekdaysParse,o))?a:null:-1!==(a=un.call(this._minWeekdaysParse,o))?a:-1!==(a=un.call(this._weekdaysParse,o))?a:-1!==(a=un.call(this._shortWeekdaysParse,o))?a:null}function ht(){function t(t,e){return e.length-t.length}var e,n,i,a,r,o=[],s=[],l=[],u=[];for(e=0;e<7;e++)n=h([2e3,1]).day(e),i=this.weekdaysMin(n,""),a=this.weekdaysShort(n,""),r=this.weekdays(n,""),o.push(i),s.push(a),l.push(r),u.push(i),u.push(a),u.push(r);for(o.sort(t),s.sort(t),l.sort(t),u.sort(t),e=0;e<7;e++)s[e]=q(s[e]),l[e]=q(l[e]),u[e]=q(u[e]);this._weekdaysRegex=new RegExp("^("+u.join("|")+")","i"),this._weekdaysShortRegex=this._weekdaysRegex,this._weekdaysMinRegex=this._weekdaysRegex,this._weekdaysStrictRegex=new RegExp("^("+l.join("|")+")","i"),this._weekdaysShortStrictRegex=new RegExp("^("+s.join("|")+")","i"),this._weekdaysMinStrictRegex=new RegExp("^("+o.join("|")+")","i")}function ft(){return this.hours()%12||12}function gt(t,e){N(t,0,0,function(){return this.localeData().meridiem(this.hours(),this.minutes(),e)})}function mt(t,e){return e._meridiemParse}function pt(t){return t?t.toLowerCase().replace("_","-"):t}function vt(t){for(var e,n,i,a,r=0;r0;){if(i=yt(a.slice(0,e).join("-")))return i;if(n&&n.length>=e&&k(a,n,!0)>=e-1)break;e--}r++}return null}function yt(n){var i=null;if(!Sn[n]&&void 0!==e&&e&&e.exports)try{i=kn._abbr,t("./locale/"+n),bt(i)}catch(t){}return Sn[n]}function bt(t,e){var n;return t&&(n=o(e)?_t(t):xt(t,e))&&(kn=n),kn._abbr}function xt(t,e){if(null!==e){var n=Mn;if(e.abbr=t,null!=Sn[t])S("defineLocaleOverride","use moment.updateLocale(localeName, config) to change an existing locale. moment.defineLocale(localeName, config) should only be used for creating a new locale See http://momentjs.com/guides/#/warnings/define-locale/ for more info."),n=Sn[t]._config;else if(null!=e.parentLocale){if(null==Sn[e.parentLocale])return Dn[e.parentLocale]||(Dn[e.parentLocale]=[]),Dn[e.parentLocale].push({name:t,config:e}),null;n=Sn[e.parentLocale]._config}return Sn[t]=new P(C(n,e)),Dn[t]&&Dn[t].forEach(function(t){xt(t.name,t.config)}),bt(t),Sn[t]}return delete Sn[t],null}function _t(t){var e;if(t&&t._locale&&t._locale._abbr&&(t=t._locale._abbr),!t)return kn;if(!i(t)){if(e=yt(t))return e;t=[t]}return vt(t)}function kt(t){var e,n=t._a;return n&&-2===g(t).overflow&&(e=n[tn]<0||n[tn]>11?tn:n[en]<1||n[en]>J(n[$e],n[tn])?en:n[nn]<0||n[nn]>24||24===n[nn]&&(0!==n[an]||0!==n[rn]||0!==n[on])?nn:n[an]<0||n[an]>59?an:n[rn]<0||n[rn]>59?rn:n[on]<0||n[on]>999?on:-1,g(t)._overflowDayOfYear&&(e<$e||e>en)&&(e=en),g(t)._overflowWeeks&&-1===e&&(e=sn),g(t)._overflowWeekday&&-1===e&&(e=ln),g(t).overflow=e),t}function wt(t){var e,n,i,a,r,o,s=t._i,l=Cn.exec(s)||Pn.exec(s);if(l){for(g(t).iso=!0,e=0,n=An.length;e10?"YYYY ":"YY "),r="HH:mm"+(n[4]?":ss":""),n[1]){var d=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"][new Date(n[2]).getDay()];if(n[1].substr(0,3)!==d)return g(t).weekdayMismatch=!0,void(t._isValid=!1)}switch(n[5].length){case 2:s=0===l?" +0000":((l="YXWVUTSRQPONZABCDEFGHIKLM".indexOf(n[5][1].toUpperCase())-12)<0?" -":" +")+(""+l).replace(/^-?/,"0").match(/..$/)[0]+"00";break;case 4:s=u[n[5]];break;default:s=u[" GMT"]}n[5]=s,t._i=n.splice(1).join(""),o=" ZZ",t._f=i+a+r+o,At(t),g(t).rfc2822=!0}else t._isValid=!1}function St(t){var e=On.exec(t._i);null===e?(wt(t),!1===t._isValid&&(delete t._isValid,Mt(t),!1===t._isValid&&(delete t._isValid,n.createFromInputFallback(t)))):t._d=new Date(+e[1])}function Dt(t,e,n){return null!=t?t:null!=e?e:n}function Ct(t){var e=new Date(n.now());return t._useUTC?[e.getUTCFullYear(),e.getUTCMonth(),e.getUTCDate()]:[e.getFullYear(),e.getMonth(),e.getDate()]}function Pt(t){var e,n,i,a,r=[];if(!t._d){for(i=Ct(t),t._w&&null==t._a[en]&&null==t._a[tn]&&Tt(t),null!=t._dayOfYear&&(a=Dt(t._a[$e],i[$e]),(t._dayOfYear>et(a)||0===t._dayOfYear)&&(g(t)._overflowDayOfYear=!0),n=at(a,0,t._dayOfYear),t._a[tn]=n.getUTCMonth(),t._a[en]=n.getUTCDate()),e=0;e<3&&null==t._a[e];++e)t._a[e]=r[e]=i[e];for(;e<7;e++)t._a[e]=r[e]=null==t._a[e]?2===e?1:0:t._a[e];24===t._a[nn]&&0===t._a[an]&&0===t._a[rn]&&0===t._a[on]&&(t._nextDay=!0,t._a[nn]=0),t._d=(t._useUTC?at:it).apply(null,r),null!=t._tzm&&t._d.setUTCMinutes(t._d.getUTCMinutes()-t._tzm),t._nextDay&&(t._a[nn]=24)}}function Tt(t){var e,n,i,a,r,o,s,l;if(null!=(e=t._w).GG||null!=e.W||null!=e.E)r=1,o=4,n=Dt(e.GG,t._a[$e],st(Nt(),1,4).year),i=Dt(e.W,1),((a=Dt(e.E,1))<1||a>7)&&(l=!0);else{r=t._locale._week.dow,o=t._locale._week.doy;var u=st(Nt(),r,o);n=Dt(e.gg,t._a[$e],u.year),i=Dt(e.w,u.week),null!=e.d?((a=e.d)<0||a>6)&&(l=!0):null!=e.e?(a=e.e+r,(e.e<0||e.e>6)&&(l=!0)):a=r}i<1||i>lt(n,r,o)?g(t)._overflowWeeks=!0:null!=l?g(t)._overflowWeekday=!0:(s=ot(n,i,a,r,o),t._a[$e]=s.year,t._dayOfYear=s.dayOfYear)}function At(t){if(t._f!==n.ISO_8601)if(t._f!==n.RFC_2822){t._a=[],g(t).empty=!0;var e,i,a,r,o,s=""+t._i,l=s.length,u=0;for(a=H(t._f,t._locale).match(Ie)||[],e=0;e0&&g(t).unusedInput.push(o),s=s.slice(s.indexOf(i)+i.length),u+=i.length),Re[r]?(i?g(t).empty=!1:g(t).unusedTokens.push(r),X(r,i,t)):t._strict&&!i&&g(t).unusedTokens.push(r);g(t).charsLeftOver=l-u,s.length>0&&g(t).unusedInput.push(s),t._a[nn]<=12&&!0===g(t).bigHour&&t._a[nn]>0&&(g(t).bigHour=void 0),g(t).parsedDateParts=t._a.slice(0),g(t).meridiem=t._meridiem,t._a[nn]=It(t._locale,t._a[nn],t._meridiem),Pt(t),kt(t)}else Mt(t);else wt(t)}function It(t,e,n){var i;return null==n?e:null!=t.meridiemHour?t.meridiemHour(e,n):null!=t.isPM?((i=t.isPM(n))&&e<12&&(e+=12),i||12!==e||(e=0),e):e}function Ot(t){var e,n,i,a,r;if(0===t._f.length)return g(t).invalidFormat=!0,void(t._d=new Date(NaN));for(a=0;ar&&(e=r),oe.call(this,t,e,n,i,a))}function oe(t,e,n,i,a){var r=ot(t,e,n,i,a),o=at(r.year,0,r.dayOfYear);return this.year(o.getUTCFullYear()),this.month(o.getUTCMonth()),this.date(o.getUTCDate()),this}function se(t){return t}function le(t,e,n,i){var a=_t(),r=h().set(i,e);return a[n](r,t)}function ue(t,e,n){if(s(t)&&(e=t,t=void 0),t=t||"",null!=e)return le(t,e,n,"month");var i,a=[];for(i=0;i<12;i++)a[i]=le(t,i,n,"month");return a}function de(t,e,n,i){"boolean"==typeof t?(s(e)&&(n=e,e=void 0),e=e||""):(n=e=t,t=!1,s(e)&&(n=e,e=void 0),e=e||"");var a=_t(),r=t?a._week.dow:0;if(null!=n)return le(e,(n+r)%7,i,"day");var o,l=[];for(o=0;o<7;o++)l[o]=le(e,(o+r)%7,i,"day");return l}function ce(t,e,n,i){var a=Xt(e,n);return t._milliseconds+=i*a._milliseconds,t._days+=i*a._days,t._months+=i*a._months,t._bubble()}function he(t){return t<0?Math.floor(t):Math.ceil(t)}function fe(t){return 4800*t/146097}function ge(t){return 146097*t/4800}function me(t){return function(){return this.as(t)}}function pe(t){return function(){return this.isValid()?this._data[t]:NaN}}function ve(t,e,n,i,a){return a.relativeTime(e||1,!!n,t,i)}function ye(t,e,n){var i=Xt(t).abs(),a=hi(i.as("s")),r=hi(i.as("m")),o=hi(i.as("h")),s=hi(i.as("d")),l=hi(i.as("M")),u=hi(i.as("y")),d=a<=fi.ss&&["s",a]||a0,d[4]=n,ve.apply(null,d)}function be(){if(!this.isValid())return this.localeData().invalidDate();var t,e,n,i=gi(this._milliseconds)/1e3,a=gi(this._days),r=gi(this._months);e=x((t=x(i/60))/60),i%=60,t%=60;var o=n=x(r/12),s=r%=12,l=a,u=e,d=t,c=i,h=this.asSeconds();return h?(h<0?"-":"")+"P"+(o?o+"Y":"")+(s?s+"M":"")+(l?l+"D":"")+(u||d||c?"T":"")+(u?u+"H":"")+(d?d+"M":"")+(c?c+"S":""):"P0D"}var xe,_e,ke=_e=Array.prototype.some?Array.prototype.some:function(t){for(var e=Object(this),n=e.length>>>0,i=0;i68?1900:2e3)};var mn=R("FullYear",!0);N("w",["ww",2],"wo","week"),N("W",["WW",2],"Wo","isoWeek"),T("week","w"),T("isoWeek","W"),O("week",5),O("isoWeek",5),E("w",Be),E("ww",Be,We),E("W",Be),E("WW",Be,We),Z(["w","ww","W","WW"],function(t,e,n,i){e[i.substr(0,1)]=_(t)});N("d",0,"do","day"),N("dd",0,0,function(t){return this.localeData().weekdaysMin(this,t)}),N("ddd",0,0,function(t){return this.localeData().weekdaysShort(this,t)}),N("dddd",0,0,function(t){return this.localeData().weekdays(this,t)}),N("e",0,0,"weekday"),N("E",0,0,"isoWeekday"),T("day","d"),T("weekday","e"),T("isoWeekday","E"),O("day",11),O("weekday",11),O("isoWeekday",11),E("d",Be),E("e",Be),E("E",Be),E("dd",function(t,e){return e.weekdaysMinRegex(t)}),E("ddd",function(t,e){return e.weekdaysShortRegex(t)}),E("dddd",function(t,e){return e.weekdaysRegex(t)}),Z(["dd","ddd","dddd"],function(t,e,n,i){var a=n._locale.weekdaysParse(t,i,n._strict);null!=a?e.d=a:g(n).invalidWeekday=t}),Z(["d","e","E"],function(t,e,n,i){e[i]=_(t)});var pn="Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),vn="Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),yn="Su_Mo_Tu_We_Th_Fr_Sa".split("_"),bn=Je,xn=Je,_n=Je;N("H",["HH",2],0,"hour"),N("h",["hh",2],0,ft),N("k",["kk",2],0,function(){return this.hours()||24}),N("hmm",0,0,function(){return""+ft.apply(this)+Y(this.minutes(),2)}),N("hmmss",0,0,function(){return""+ft.apply(this)+Y(this.minutes(),2)+Y(this.seconds(),2)}),N("Hmm",0,0,function(){return""+this.hours()+Y(this.minutes(),2)}),N("Hmmss",0,0,function(){return""+this.hours()+Y(this.minutes(),2)+Y(this.seconds(),2)}),gt("a",!0),gt("A",!1),T("hour","h"),O("hour",13),E("a",mt),E("A",mt),E("H",Be),E("h",Be),E("k",Be),E("HH",Be,We),E("hh",Be,We),E("kk",Be,We),E("hmm",Ve),E("hmmss",He),E("Hmm",Ve),E("Hmmss",He),G(["H","HH"],nn),G(["k","kk"],function(t,e,n){var i=_(t);e[nn]=24===i?0:i}),G(["a","A"],function(t,e,n){n._isPm=n._locale.isPM(t),n._meridiem=t}),G(["h","hh"],function(t,e,n){e[nn]=_(t),g(n).bigHour=!0}),G("hmm",function(t,e,n){var i=t.length-2;e[nn]=_(t.substr(0,i)),e[an]=_(t.substr(i)),g(n).bigHour=!0}),G("hmmss",function(t,e,n){var i=t.length-4,a=t.length-2;e[nn]=_(t.substr(0,i)),e[an]=_(t.substr(i,2)),e[rn]=_(t.substr(a)),g(n).bigHour=!0}),G("Hmm",function(t,e,n){var i=t.length-2;e[nn]=_(t.substr(0,i)),e[an]=_(t.substr(i))}),G("Hmmss",function(t,e,n){var i=t.length-4,a=t.length-2;e[nn]=_(t.substr(0,i)),e[an]=_(t.substr(i,2)),e[rn]=_(t.substr(a))});var kn,wn=R("Hours",!0),Mn={calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},longDateFormat:{LTS:"h:mm:ss A",LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY h:mm A",LLLL:"dddd, MMMM D, YYYY h:mm A"},invalidDate:"Invalid date",ordinal:"%d",dayOfMonthOrdinalParse:/\d{1,2}/,relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",ss:"%d seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},months:cn,monthsShort:hn,week:{dow:0,doy:6},weekdays:pn,weekdaysMin:yn,weekdaysShort:vn,meridiemParse:/[ap]\.?m?\.?/i},Sn={},Dn={},Cn=/^\s*((?:[+-]\d{6}|\d{4})-(?:\d\d-\d\d|W\d\d-\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?::\d\d(?::\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,Pn=/^\s*((?:[+-]\d{6}|\d{4})(?:\d\d\d\d|W\d\d\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?:\d\d(?:\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,Tn=/Z|[+-]\d\d(?::?\d\d)?/,An=[["YYYYYY-MM-DD",/[+-]\d{6}-\d\d-\d\d/],["YYYY-MM-DD",/\d{4}-\d\d-\d\d/],["GGGG-[W]WW-E",/\d{4}-W\d\d-\d/],["GGGG-[W]WW",/\d{4}-W\d\d/,!1],["YYYY-DDD",/\d{4}-\d{3}/],["YYYY-MM",/\d{4}-\d\d/,!1],["YYYYYYMMDD",/[+-]\d{10}/],["YYYYMMDD",/\d{8}/],["GGGG[W]WWE",/\d{4}W\d{3}/],["GGGG[W]WW",/\d{4}W\d{2}/,!1],["YYYYDDD",/\d{7}/]],In=[["HH:mm:ss.SSSS",/\d\d:\d\d:\d\d\.\d+/],["HH:mm:ss,SSSS",/\d\d:\d\d:\d\d,\d+/],["HH:mm:ss",/\d\d:\d\d:\d\d/],["HH:mm",/\d\d:\d\d/],["HHmmss.SSSS",/\d\d\d\d\d\d\.\d+/],["HHmmss,SSSS",/\d\d\d\d\d\d,\d+/],["HHmmss",/\d\d\d\d\d\d/],["HHmm",/\d\d\d\d/],["HH",/\d\d/]],On=/^\/?Date\((\-?\d+)/i,Fn=/^((?:Mon|Tue|Wed|Thu|Fri|Sat|Sun),?\s)?(\d?\d\s(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s(?:\d\d)?\d\d\s)(\d\d:\d\d)(\:\d\d)?(\s(?:UT|GMT|[ECMP][SD]T|[A-IK-Za-ik-z]|[+-]\d{4}))$/;n.createFromInputFallback=M("value provided is not in a recognized RFC2822 or ISO format. moment construction falls back to js Date(), which is not reliable across all browsers and versions. Non RFC2822/ISO date formats are discouraged and will be removed in an upcoming major release. Please refer to http://momentjs.com/guides/#/warnings/js-date/ for more info.",function(t){t._d=new Date(t._i+(t._useUTC?" UTC":""))}),n.ISO_8601=function(){},n.RFC_2822=function(){};var Rn=M("moment().min is deprecated, use moment.max instead. http://momentjs.com/guides/#/warnings/min-max/",function(){var t=Nt.apply(null,arguments);return this.isValid()&&t.isValid()?tthis?this:t:p()}),Wn=["year","quarter","month","week","day","hour","minute","second","millisecond"];jt("Z",":"),jt("ZZ",""),E("Z",Xe),E("ZZ",Xe),G(["Z","ZZ"],function(t,e,n){n._useUTC=!0,n._tzm=Ut(Xe,t)});var Yn=/([\+\-]|\d\d)/gi;n.updateOffset=function(){};var Nn=/^(\-)?(?:(\d*)[. ])?(\d+)\:(\d+)(?:\:(\d+)(\.\d*)?)?$/,zn=/^(-)?P(?:(-?[0-9,.]*)Y)?(?:(-?[0-9,.]*)M)?(?:(-?[0-9,.]*)W)?(?:(-?[0-9,.]*)D)?(?:T(?:(-?[0-9,.]*)H)?(?:(-?[0-9,.]*)M)?(?:(-?[0-9,.]*)S)?)?$/;Xt.fn=Vt.prototype,Xt.invalid=function(){return Xt(NaN)};var Bn=$t(1,"add"),Vn=$t(-1,"subtract");n.defaultFormat="YYYY-MM-DDTHH:mm:ssZ",n.defaultFormatUtc="YYYY-MM-DDTHH:mm:ss[Z]";var Hn=M("moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.",function(t){return void 0===t?this.localeData():this.locale(t)});N(0,["gg",2],0,function(){return this.weekYear()%100}),N(0,["GG",2],0,function(){return this.isoWeekYear()%100}),ae("gggg","weekYear"),ae("ggggg","weekYear"),ae("GGGG","isoWeekYear"),ae("GGGGG","isoWeekYear"),T("weekYear","gg"),T("isoWeekYear","GG"),O("weekYear",1),O("isoWeekYear",1),E("G",Ge),E("g",Ge),E("GG",Be,We),E("gg",Be,We),E("GGGG",je,Ne),E("gggg",je,Ne),E("GGGGG",Ue,ze),E("ggggg",Ue,ze),Z(["gggg","ggggg","GGGG","GGGGG"],function(t,e,n,i){e[i.substr(0,2)]=_(t)}),Z(["gg","GG"],function(t,e,i,a){e[a]=n.parseTwoDigitYear(t)}),N("Q",0,"Qo","quarter"),T("quarter","Q"),O("quarter",7),E("Q",Le),G("Q",function(t,e){e[tn]=3*(_(t)-1)}),N("D",["DD",2],"Do","date"),T("date","D"),O("date",9),E("D",Be),E("DD",Be,We),E("Do",function(t,e){return t?e._dayOfMonthOrdinalParse||e._ordinalParse:e._dayOfMonthOrdinalParseLenient}),G(["D","DD"],en),G("Do",function(t,e){e[en]=_(t.match(Be)[0],10)});var En=R("Date",!0);N("DDD",["DDDD",3],"DDDo","dayOfYear"),T("dayOfYear","DDD"),O("dayOfYear",4),E("DDD",Ee),E("DDDD",Ye),G(["DDD","DDDD"],function(t,e,n){n._dayOfYear=_(t)}),N("m",["mm",2],0,"minute"),T("minute","m"),O("minute",14),E("m",Be),E("mm",Be,We),G(["m","mm"],an);var jn=R("Minutes",!1);N("s",["ss",2],0,"second"),T("second","s"),O("second",15),E("s",Be),E("ss",Be,We),G(["s","ss"],rn);var Un=R("Seconds",!1);N("S",0,0,function(){return~~(this.millisecond()/100)}),N(0,["SS",2],0,function(){return~~(this.millisecond()/10)}),N(0,["SSS",3],0,"millisecond"),N(0,["SSSS",4],0,function(){return 10*this.millisecond()}),N(0,["SSSSS",5],0,function(){return 100*this.millisecond()}),N(0,["SSSSSS",6],0,function(){return 1e3*this.millisecond()}),N(0,["SSSSSSS",7],0,function(){return 1e4*this.millisecond()}),N(0,["SSSSSSSS",8],0,function(){return 1e5*this.millisecond()}),N(0,["SSSSSSSSS",9],0,function(){return 1e6*this.millisecond()}),T("millisecond","ms"),O("millisecond",16),E("S",Ee,Le),E("SS",Ee,We),E("SSS",Ee,Ye);var qn;for(qn="SSSS";qn.length<=9;qn+="S")E(qn,qe);for(qn="S";qn.length<=9;qn+="S")G(qn,function(t,e){e[on]=_(1e3*("0."+t))});var Gn=R("Milliseconds",!1);N("z",0,0,"zoneAbbr"),N("zz",0,0,"zoneName");var Zn=y.prototype;Zn.add=Bn,Zn.calendar=function(t,e){var i=t||Nt(),a=qt(i,this).startOf("day"),r=n.calendarFormat(this,a)||"sameElse",o=e&&(D(e[r])?e[r].call(this,i):e[r]);return this.format(o||this.localeData().calendar(r,this,Nt(i)))},Zn.clone=function(){return new y(this)},Zn.diff=function(t,e,n){var i,a,r,o;return this.isValid()&&(i=qt(t,this)).isValid()?(a=6e4*(i.utcOffset()-this.utcOffset()),"year"===(e=A(e))||"month"===e||"quarter"===e?(o=ee(this,i),"quarter"===e?o/=3:"year"===e&&(o/=12)):(r=this-i,o="second"===e?r/1e3:"minute"===e?r/6e4:"hour"===e?r/36e5:"day"===e?(r-a)/864e5:"week"===e?(r-a)/6048e5:r),n?o:x(o)):NaN},Zn.endOf=function(t){return void 0===(t=A(t))||"millisecond"===t?this:("date"===t&&(t="day"),this.startOf(t).add(1,"isoWeek"===t?"week":t).subtract(1,"ms"))},Zn.format=function(t){t||(t=this.isUtc()?n.defaultFormatUtc:n.defaultFormat);var e=V(this,t);return this.localeData().postformat(e)},Zn.from=function(t,e){return this.isValid()&&(b(t)&&t.isValid()||Nt(t).isValid())?Xt({to:this,from:t}).locale(this.locale()).humanize(!e):this.localeData().invalidDate()},Zn.fromNow=function(t){return this.from(Nt(),t)},Zn.to=function(t,e){return this.isValid()&&(b(t)&&t.isValid()||Nt(t).isValid())?Xt({from:this,to:t}).locale(this.locale()).humanize(!e):this.localeData().invalidDate()},Zn.toNow=function(t){return this.to(Nt(),t)},Zn.get=function(t){return t=A(t),D(this[t])?this[t]():this},Zn.invalidAt=function(){return g(this).overflow},Zn.isAfter=function(t,e){var n=b(t)?t:Nt(t);return!(!this.isValid()||!n.isValid())&&("millisecond"===(e=A(o(e)?"millisecond":e))?this.valueOf()>n.valueOf():n.valueOf()9999?V(t,"YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]"):D(Date.prototype.toISOString)?this.toDate().toISOString():V(t,"YYYY-MM-DD[T]HH:mm:ss.SSS[Z]")},Zn.inspect=function(){if(!this.isValid())return"moment.invalid(/* "+this._i+" */)";var t="moment",e="";this.isLocal()||(t=0===this.utcOffset()?"moment.utc":"moment.parseZone",e="Z");var n="["+t+'("]',i=0<=this.year()&&this.year()<=9999?"YYYY":"YYYYYY",a=e+'[")]';return this.format(n+i+"-MM-DD[T]HH:mm:ss.SSS"+a)},Zn.toJSON=function(){return this.isValid()?this.toISOString():null},Zn.toString=function(){return this.clone().locale("en").format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ")},Zn.unix=function(){return Math.floor(this.valueOf()/1e3)},Zn.valueOf=function(){return this._d.valueOf()-6e4*(this._offset||0)},Zn.creationData=function(){return{input:this._i,format:this._f,locale:this._locale,isUTC:this._isUTC,strict:this._strict}},Zn.year=mn,Zn.isLeapYear=function(){return nt(this.year())},Zn.weekYear=function(t){return re.call(this,t,this.week(),this.weekday(),this.localeData()._week.dow,this.localeData()._week.doy)},Zn.isoWeekYear=function(t){return re.call(this,t,this.isoWeek(),this.isoWeekday(),1,4)},Zn.quarter=Zn.quarters=function(t){return null==t?Math.ceil((this.month()+1)/3):this.month(3*(t-1)+this.month()%3)},Zn.month=$,Zn.daysInMonth=function(){return J(this.year(),this.month())},Zn.week=Zn.weeks=function(t){var e=this.localeData().week(this);return null==t?e:this.add(7*(t-e),"d")},Zn.isoWeek=Zn.isoWeeks=function(t){var e=st(this,1,4).week;return null==t?e:this.add(7*(t-e),"d")},Zn.weeksInYear=function(){var t=this.localeData()._week;return lt(this.year(),t.dow,t.doy)},Zn.isoWeeksInYear=function(){return lt(this.year(),1,4)},Zn.date=En,Zn.day=Zn.days=function(t){if(!this.isValid())return null!=t?this:NaN;var e=this._isUTC?this._d.getUTCDay():this._d.getDay();return null!=t?(t=ut(t,this.localeData()),this.add(t-e,"d")):e},Zn.weekday=function(t){if(!this.isValid())return null!=t?this:NaN;var e=(this.day()+7-this.localeData()._week.dow)%7;return null==t?e:this.add(t-e,"d")},Zn.isoWeekday=function(t){if(!this.isValid())return null!=t?this:NaN;if(null!=t){var e=dt(t,this.localeData());return this.day(this.day()%7?e:e-7)}return this.day()||7},Zn.dayOfYear=function(t){var e=Math.round((this.clone().startOf("day")-this.clone().startOf("year"))/864e5)+1;return null==t?e:this.add(t-e,"d")},Zn.hour=Zn.hours=wn,Zn.minute=Zn.minutes=jn,Zn.second=Zn.seconds=Un,Zn.millisecond=Zn.milliseconds=Gn,Zn.utcOffset=function(t,e,i){var a,r=this._offset||0;if(!this.isValid())return null!=t?this:NaN;if(null!=t){if("string"==typeof t){if(null===(t=Ut(Xe,t)))return this}else Math.abs(t)<16&&!i&&(t*=60);return!this._isUTC&&e&&(a=Gt(this)),this._offset=t,this._isUTC=!0,null!=a&&this.add(a,"m"),r!==t&&(!e||this._changeInProgress?te(this,Xt(t-r,"m"),1,!1):this._changeInProgress||(this._changeInProgress=!0,n.updateOffset(this,!0),this._changeInProgress=null)),this}return this._isUTC?r:Gt(this)},Zn.utc=function(t){return this.utcOffset(0,t)},Zn.local=function(t){return this._isUTC&&(this.utcOffset(0,t),this._isUTC=!1,t&&this.subtract(Gt(this),"m")),this},Zn.parseZone=function(){if(null!=this._tzm)this.utcOffset(this._tzm,!1,!0);else if("string"==typeof this._i){var t=Ut(Ze,this._i);null!=t?this.utcOffset(t):this.utcOffset(0,!0)}return this},Zn.hasAlignedHourOffset=function(t){return!!this.isValid()&&(t=t?Nt(t).utcOffset():0,(this.utcOffset()-t)%60==0)},Zn.isDST=function(){return this.utcOffset()>this.clone().month(0).utcOffset()||this.utcOffset()>this.clone().month(5).utcOffset()},Zn.isLocal=function(){return!!this.isValid()&&!this._isUTC},Zn.isUtcOffset=function(){return!!this.isValid()&&this._isUTC},Zn.isUtc=Zt,Zn.isUTC=Zt,Zn.zoneAbbr=function(){return this._isUTC?"UTC":""},Zn.zoneName=function(){return this._isUTC?"Coordinated Universal Time":""},Zn.dates=M("dates accessor is deprecated. Use date instead.",En),Zn.months=M("months accessor is deprecated. Use month instead",$),Zn.years=M("years accessor is deprecated. Use year instead",mn),Zn.zone=M("moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/",function(t,e){return null!=t?("string"!=typeof t&&(t=-t),this.utcOffset(t,e),this):-this.utcOffset()}),Zn.isDSTShifted=M("isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information",function(){if(!o(this._isDSTShifted))return this._isDSTShifted;var t={};if(v(t,this),(t=Lt(t))._a){var e=t._isUTC?h(t._a):Nt(t._a);this._isDSTShifted=this.isValid()&&k(t._a,e.toArray())>0}else this._isDSTShifted=!1;return this._isDSTShifted});var Xn=P.prototype;Xn.calendar=function(t,e,n){var i=this._calendar[t]||this._calendar.sameElse;return D(i)?i.call(e,n):i},Xn.longDateFormat=function(t){var e=this._longDateFormat[t],n=this._longDateFormat[t.toUpperCase()];return e||!n?e:(this._longDateFormat[t]=n.replace(/MMMM|MM|DD|dddd/g,function(t){return t.slice(1)}),this._longDateFormat[t])},Xn.invalidDate=function(){return this._invalidDate},Xn.ordinal=function(t){return this._ordinal.replace("%d",t)},Xn.preparse=se,Xn.postformat=se,Xn.relativeTime=function(t,e,n,i){var a=this._relativeTime[n];return D(a)?a(t,e,n,i):a.replace(/%d/i,t)},Xn.pastFuture=function(t,e){var n=this._relativeTime[t>0?"future":"past"];return D(n)?n(e):n.replace(/%s/i,e)},Xn.set=function(t){var e,n;for(n in t)D(e=t[n])?this[n]=e:this["_"+n]=e;this._config=t,this._dayOfMonthOrdinalParseLenient=new RegExp((this._dayOfMonthOrdinalParse.source||this._ordinalParse.source)+"|"+/\d{1,2}/.source)},Xn.months=function(t,e){return t?i(this._months)?this._months[t.month()]:this._months[(this._months.isFormat||dn).test(e)?"format":"standalone"][t.month()]:i(this._months)?this._months:this._months.standalone},Xn.monthsShort=function(t,e){return t?i(this._monthsShort)?this._monthsShort[t.month()]:this._monthsShort[dn.test(e)?"format":"standalone"][t.month()]:i(this._monthsShort)?this._monthsShort:this._monthsShort.standalone},Xn.monthsParse=function(t,e,n){var i,a,r;if(this._monthsParseExact)return K.call(this,t,e,n);for(this._monthsParse||(this._monthsParse=[],this._longMonthsParse=[],this._shortMonthsParse=[]),i=0;i<12;i++){if(a=h([2e3,i]),n&&!this._longMonthsParse[i]&&(this._longMonthsParse[i]=new RegExp("^"+this.months(a,"").replace(".","")+"$","i"),this._shortMonthsParse[i]=new RegExp("^"+this.monthsShort(a,"").replace(".","")+"$","i")),n||this._monthsParse[i]||(r="^"+this.months(a,"")+"|^"+this.monthsShort(a,""),this._monthsParse[i]=new RegExp(r.replace(".",""),"i")),n&&"MMMM"===e&&this._longMonthsParse[i].test(t))return i;if(n&&"MMM"===e&&this._shortMonthsParse[i].test(t))return i;if(!n&&this._monthsParse[i].test(t))return i}},Xn.monthsRegex=function(t){return this._monthsParseExact?(d(this,"_monthsRegex")||tt.call(this),t?this._monthsStrictRegex:this._monthsRegex):(d(this,"_monthsRegex")||(this._monthsRegex=gn),this._monthsStrictRegex&&t?this._monthsStrictRegex:this._monthsRegex)},Xn.monthsShortRegex=function(t){return this._monthsParseExact?(d(this,"_monthsRegex")||tt.call(this),t?this._monthsShortStrictRegex:this._monthsShortRegex):(d(this,"_monthsShortRegex")||(this._monthsShortRegex=fn),this._monthsShortStrictRegex&&t?this._monthsShortStrictRegex:this._monthsShortRegex)},Xn.week=function(t){return st(t,this._week.dow,this._week.doy).week},Xn.firstDayOfYear=function(){return this._week.doy},Xn.firstDayOfWeek=function(){return this._week.dow},Xn.weekdays=function(t,e){return t?i(this._weekdays)?this._weekdays[t.day()]:this._weekdays[this._weekdays.isFormat.test(e)?"format":"standalone"][t.day()]:i(this._weekdays)?this._weekdays:this._weekdays.standalone},Xn.weekdaysMin=function(t){return t?this._weekdaysMin[t.day()]:this._weekdaysMin},Xn.weekdaysShort=function(t){return t?this._weekdaysShort[t.day()]:this._weekdaysShort},Xn.weekdaysParse=function(t,e,n){var i,a,r;if(this._weekdaysParseExact)return ct.call(this,t,e,n);for(this._weekdaysParse||(this._weekdaysParse=[],this._minWeekdaysParse=[],this._shortWeekdaysParse=[],this._fullWeekdaysParse=[]),i=0;i<7;i++){if(a=h([2e3,1]).day(i),n&&!this._fullWeekdaysParse[i]&&(this._fullWeekdaysParse[i]=new RegExp("^"+this.weekdays(a,"").replace(".",".?")+"$","i"),this._shortWeekdaysParse[i]=new RegExp("^"+this.weekdaysShort(a,"").replace(".",".?")+"$","i"),this._minWeekdaysParse[i]=new RegExp("^"+this.weekdaysMin(a,"").replace(".",".?")+"$","i")),this._weekdaysParse[i]||(r="^"+this.weekdays(a,"")+"|^"+this.weekdaysShort(a,"")+"|^"+this.weekdaysMin(a,""),this._weekdaysParse[i]=new RegExp(r.replace(".",""),"i")),n&&"dddd"===e&&this._fullWeekdaysParse[i].test(t))return i;if(n&&"ddd"===e&&this._shortWeekdaysParse[i].test(t))return i;if(n&&"dd"===e&&this._minWeekdaysParse[i].test(t))return i;if(!n&&this._weekdaysParse[i].test(t))return i}},Xn.weekdaysRegex=function(t){return this._weekdaysParseExact?(d(this,"_weekdaysRegex")||ht.call(this),t?this._weekdaysStrictRegex:this._weekdaysRegex):(d(this,"_weekdaysRegex")||(this._weekdaysRegex=bn),this._weekdaysStrictRegex&&t?this._weekdaysStrictRegex:this._weekdaysRegex)},Xn.weekdaysShortRegex=function(t){return this._weekdaysParseExact?(d(this,"_weekdaysRegex")||ht.call(this),t?this._weekdaysShortStrictRegex:this._weekdaysShortRegex):(d(this,"_weekdaysShortRegex")||(this._weekdaysShortRegex=xn),this._weekdaysShortStrictRegex&&t?this._weekdaysShortStrictRegex:this._weekdaysShortRegex)},Xn.weekdaysMinRegex=function(t){return this._weekdaysParseExact?(d(this,"_weekdaysRegex")||ht.call(this),t?this._weekdaysMinStrictRegex:this._weekdaysMinRegex):(d(this,"_weekdaysMinRegex")||(this._weekdaysMinRegex=_n),this._weekdaysMinStrictRegex&&t?this._weekdaysMinStrictRegex:this._weekdaysMinRegex)},Xn.isPM=function(t){return"p"===(t+"").toLowerCase().charAt(0)},Xn.meridiem=function(t,e,n){return t>11?n?"pm":"PM":n?"am":"AM"},bt("en",{dayOfMonthOrdinalParse:/\d{1,2}(th|st|nd|rd)/,ordinal:function(t){var e=t%10;return t+(1===_(t%100/10)?"th":1===e?"st":2===e?"nd":3===e?"rd":"th")}}),n.lang=M("moment.lang is deprecated. Use moment.locale instead.",bt),n.langData=M("moment.langData is deprecated. Use moment.localeData instead.",_t);var Jn=Math.abs,Kn=me("ms"),Qn=me("s"),$n=me("m"),ti=me("h"),ei=me("d"),ni=me("w"),ii=me("M"),ai=me("y"),ri=pe("milliseconds"),oi=pe("seconds"),si=pe("minutes"),li=pe("hours"),ui=pe("days"),di=pe("months"),ci=pe("years"),hi=Math.round,fi={ss:44,s:45,m:45,h:22,d:26,M:11},gi=Math.abs,mi=Vt.prototype;return mi.isValid=function(){return this._isValid},mi.abs=function(){var t=this._data;return this._milliseconds=Jn(this._milliseconds),this._days=Jn(this._days),this._months=Jn(this._months),t.milliseconds=Jn(t.milliseconds),t.seconds=Jn(t.seconds),t.minutes=Jn(t.minutes),t.hours=Jn(t.hours),t.months=Jn(t.months),t.years=Jn(t.years),this},mi.add=function(t,e){return ce(this,t,e,1)},mi.subtract=function(t,e){return ce(this,t,e,-1)},mi.as=function(t){if(!this.isValid())return NaN;var e,n,i=this._milliseconds;if("month"===(t=A(t))||"year"===t)return e=this._days+i/864e5,n=this._months+fe(e),"month"===t?n:n/12;switch(e=this._days+Math.round(ge(this._months)),t){case"week":return e/7+i/6048e5;case"day":return e+i/864e5;case"hour":return 24*e+i/36e5;case"minute":return 1440*e+i/6e4;case"second":return 86400*e+i/1e3;case"millisecond":return Math.floor(864e5*e)+i;default:throw new Error("Unknown unit "+t)}},mi.asMilliseconds=Kn,mi.asSeconds=Qn,mi.asMinutes=$n,mi.asHours=ti,mi.asDays=ei,mi.asWeeks=ni,mi.asMonths=ii,mi.asYears=ai,mi.valueOf=function(){return this.isValid()?this._milliseconds+864e5*this._days+this._months%12*2592e6+31536e6*_(this._months/12):NaN},mi._bubble=function(){var t,e,n,i,a,r=this._milliseconds,o=this._days,s=this._months,l=this._data;return r>=0&&o>=0&&s>=0||r<=0&&o<=0&&s<=0||(r+=864e5*he(ge(s)+o),o=0,s=0),l.milliseconds=r%1e3,t=x(r/1e3),l.seconds=t%60,e=x(t/60),l.minutes=e%60,n=x(e/60),l.hours=n%24,o+=x(n/24),a=x(fe(o)),s+=a,o-=he(ge(a)),i=x(s/12),s%=12,l.days=o,l.months=s,l.years=i,this},mi.get=function(t){return t=A(t),this.isValid()?this[t+"s"]():NaN},mi.milliseconds=ri,mi.seconds=oi,mi.minutes=si,mi.hours=li,mi.days=ui,mi.weeks=function(){return x(this.days()/7)},mi.months=di,mi.years=ci,mi.humanize=function(t){if(!this.isValid())return this.localeData().invalidDate();var e=this.localeData(),n=ye(this,!t,e);return t&&(n=e.pastFuture(+this,n)),e.postformat(n)},mi.toISOString=be,mi.toString=be,mi.toJSON=be,mi.locale=ne,mi.localeData=ie,mi.toIsoString=M("toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)",be),mi.lang=Hn,N("X",0,0,"unix"),N("x",0,0,"valueOf"),E("x",Ge),E("X",/[+-]?\d+(\.\d{1,3})?/),G("X",function(t,e,n){n._d=new Date(1e3*parseFloat(t,10))}),G("x",function(t,e,n){n._d=new Date(_(t))}),n.version="2.18.1",function(t){xe=t}(Nt),n.fn=Zn,n.min=function(){return zt("isBefore",[].slice.call(arguments,0))},n.max=function(){return zt("isAfter",[].slice.call(arguments,0))},n.now=function(){return Date.now?Date.now():+new Date},n.utc=h,n.unix=function(t){return Nt(1e3*t)},n.months=function(t,e){return ue(t,e,"months")},n.isDate=l,n.locale=bt,n.invalid=p,n.duration=Xt,n.isMoment=b,n.weekdays=function(t,e,n){return de(t,e,n,"weekdays")},n.parseZone=function(){return Nt.apply(null,arguments).parseZone()},n.localeData=_t,n.isDuration=Ht,n.monthsShort=function(t,e){return ue(t,e,"monthsShort")},n.weekdaysMin=function(t,e,n){return de(t,e,n,"weekdaysMin")},n.defineLocale=xt,n.updateLocale=function(t,e){if(null!=e){var n,i=Mn;null!=Sn[t]&&(i=Sn[t]._config),(n=new P(e=C(i,e))).parentLocale=Sn[t],Sn[t]=n,bt(t)}else null!=Sn[t]&&(null!=Sn[t].parentLocale?Sn[t]=Sn[t].parentLocale:null!=Sn[t]&&delete Sn[t]);return Sn[t]},n.locales=function(){return Pe(Sn)},n.weekdaysShort=function(t,e,n){return de(t,e,n,"weekdaysShort")},n.normalizeUnits=A,n.relativeTimeRounding=function(t){return void 0===t?hi:"function"==typeof t&&(hi=t,!0)},n.relativeTimeThreshold=function(t,e){return void 0!==fi[t]&&(void 0===e?fi[t]:(fi[t]=e,"s"===t&&(fi.ss=e-1),!0))},n.calendarFormat=function(t,e){var n=t.diff(e,"days",!0);return n<-6?"sameElse":n<-1?"lastWeek":n<0?"lastDay":n<1?"sameDay":n<2?"nextDay":n<7?"nextWeek":"sameElse"},n.prototype=Zn,n})},{}],7:[function(t,e,n){var i=t(29)();i.helpers=t(45),t(27)(i),i.defaults=t(25),i.Element=t(26),i.elements=t(40),i.Interaction=t(28),i.platform=t(48),t(31)(i),t(22)(i),t(23)(i),t(24)(i),t(30)(i),t(33)(i),t(32)(i),t(35)(i),t(54)(i),t(52)(i),t(53)(i),t(55)(i),t(56)(i),t(57)(i),t(15)(i),t(16)(i),t(17)(i),t(18)(i),t(19)(i),t(20)(i),t(21)(i),t(8)(i),t(9)(i),t(10)(i),t(11)(i),t(12)(i),t(13)(i),t(14)(i);var a=[];a.push(t(49)(i),t(50)(i),t(51)(i)),i.plugins.register(a),i.platform.initialize(),e.exports=i,"undefined"!=typeof window&&(window.Chart=i),i.canvasHelpers=i.helpers.canvas},{10:10,11:11,12:12,13:13,14:14,15:15,16:16,17:17,18:18,19:19,20:20,21:21,22:22,23:23,24:24,25:25,26:26,27:27,28:28,29:29,30:30,31:31,32:32,33:33,35:35,40:40,45:45,48:48,49:49,50:50,51:51,52:52,53:53,54:54,55:55,56:56,57:57,8:8,9:9}],8:[function(t,e,n){"use strict";e.exports=function(t){t.Bar=function(e,n){return n.type="bar",new t(e,n)}}},{}],9:[function(t,e,n){"use strict";e.exports=function(t){t.Bubble=function(e,n){return n.type="bubble",new t(e,n)}}},{}],10:[function(t,e,n){"use strict";e.exports=function(t){t.Doughnut=function(e,n){return n.type="doughnut",new t(e,n)}}},{}],11:[function(t,e,n){"use strict";e.exports=function(t){t.Line=function(e,n){return n.type="line",new t(e,n)}}},{}],12:[function(t,e,n){"use strict";e.exports=function(t){t.PolarArea=function(e,n){return n.type="polarArea",new t(e,n)}}},{}],13:[function(t,e,n){"use strict";e.exports=function(t){t.Radar=function(e,n){return n.type="radar",new t(e,n)}}},{}],14:[function(t,e,n){"use strict";e.exports=function(t){t.Scatter=function(e,n){return n.type="scatter",new t(e,n)}}},{}],15:[function(t,e,n){"use strict";var i=t(25),a=t(40),r=t(45);i._set("bar",{hover:{mode:"label"},scales:{xAxes:[{type:"category",categoryPercentage:.8,barPercentage:.9,offset:!0,gridLines:{offsetGridLines:!0}}],yAxes:[{type:"linear"}]}}),i._set("horizontalBar",{hover:{mode:"index",axis:"y"},scales:{xAxes:[{type:"linear",position:"bottom"}],yAxes:[{position:"left",type:"category",categoryPercentage:.8,barPercentage:.9,offset:!0,gridLines:{offsetGridLines:!0}}]},elements:{rectangle:{borderSkipped:"left"}},tooltips:{callbacks:{title:function(t,e){var n="";return t.length>0&&(t[0].yLabel?n=t[0].yLabel:e.labels.length>0&&t[0].index=0&&a>0)&&(p+=a));return r=c.getPixelForValue(p),o=c.getPixelForValue(p+f),s=(o-r)/2,{size:s,base:r,head:o,center:o+s/2}},calculateBarIndexPixels:function(t,e,n){var i,a,o,s,l,u,d=this,c=n.scale.options,h=d.getStackIndex(t),f=n.pixels,g=f[e],m=f.length,p=n.start,v=n.end;return 1===m?(i=g>p?g-p:v-g,a=g0&&(i=(g-f[e-1])/2,e===m-1&&(a=i)),e');var n=t.data,i=n.datasets,a=n.labels;if(i.length)for(var r=0;r'),a[r]&&e.push(a[r]),e.push("");return e.push(""),e.join("")},legend:{labels:{generateLabels:function(t){var e=t.data;return e.labels.length&&e.datasets.length?e.labels.map(function(n,i){var a=t.getDatasetMeta(0),o=e.datasets[0],s=a.data[i],l=s&&s.custom||{},u=r.valueAtIndexOrDefault,d=t.options.elements.arc;return{text:n,fillStyle:l.backgroundColor?l.backgroundColor:u(o.backgroundColor,i,d.backgroundColor),strokeStyle:l.borderColor?l.borderColor:u(o.borderColor,i,d.borderColor),lineWidth:l.borderWidth?l.borderWidth:u(o.borderWidth,i,d.borderWidth),hidden:isNaN(o.data[i])||a.data[i].hidden,index:i}}):[]}},onClick:function(t,e){var n,i,a,r=e.index,o=this.chart;for(n=0,i=(o.data.datasets||[]).length;n=Math.PI?-1:g<-Math.PI?1:0))+f,p={x:Math.cos(g),y:Math.sin(g)},v={x:Math.cos(m),y:Math.sin(m)},y=g<=0&&m>=0||g<=2*Math.PI&&2*Math.PI<=m,b=g<=.5*Math.PI&&.5*Math.PI<=m||g<=2.5*Math.PI&&2.5*Math.PI<=m,x=g<=-Math.PI&&-Math.PI<=m||g<=Math.PI&&Math.PI<=m,_=g<=.5*-Math.PI&&.5*-Math.PI<=m||g<=1.5*Math.PI&&1.5*Math.PI<=m,k=h/100,w={x:x?-1:Math.min(p.x*(p.x<0?1:k),v.x*(v.x<0?1:k)),y:_?-1:Math.min(p.y*(p.y<0?1:k),v.y*(v.y<0?1:k))},M={x:y?1:Math.max(p.x*(p.x>0?1:k),v.x*(v.x>0?1:k)),y:b?1:Math.max(p.y*(p.y>0?1:k),v.y*(v.y>0?1:k))},S={width:.5*(M.x-w.x),height:.5*(M.y-w.y)};u=Math.min(s/S.width,l/S.height),d={x:-.5*(M.x+w.x),y:-.5*(M.y+w.y)}}n.borderWidth=e.getMaxBorderWidth(c.data),n.outerRadius=Math.max((u-n.borderWidth)/2,0),n.innerRadius=Math.max(h?n.outerRadius/100*h:0,0),n.radiusLength=(n.outerRadius-n.innerRadius)/n.getVisibleDatasetCount(),n.offsetX=d.x*n.outerRadius,n.offsetY=d.y*n.outerRadius,c.total=e.calculateTotal(),e.outerRadius=n.outerRadius-n.radiusLength*e.getRingIndex(e.index),e.innerRadius=Math.max(e.outerRadius-n.radiusLength,0),r.each(c.data,function(n,i){e.updateElement(n,i,t)})},updateElement:function(t,e,n){var i=this,a=i.chart,o=a.chartArea,s=a.options,l=s.animation,u=(o.left+o.right)/2,d=(o.top+o.bottom)/2,c=s.rotation,h=s.rotation,f=i.getDataset(),g=n&&l.animateRotate?0:t.hidden?0:i.calculateCircumference(f.data[e])*(s.circumference/(2*Math.PI)),m=n&&l.animateScale?0:i.innerRadius,p=n&&l.animateScale?0:i.outerRadius,v=r.valueAtIndexOrDefault;r.extend(t,{_datasetIndex:i.index,_index:e,_model:{x:u+a.offsetX,y:d+a.offsetY,startAngle:c,endAngle:h,circumference:g,outerRadius:p,innerRadius:m,label:v(f.label,e,a.data.labels[e])}});var y=t._model;this.removeHoverStyle(t),n&&l.animateRotate||(y.startAngle=0===e?s.rotation:i.getMeta().data[e-1]._model.endAngle,y.endAngle=y.startAngle+y.circumference),t.pivot()},removeHoverStyle:function(e){t.DatasetController.prototype.removeHoverStyle.call(this,e,this.chart.options.elements.arc)},calculateTotal:function(){var t,e=this.getDataset(),n=this.getMeta(),i=0;return r.each(n.data,function(n,a){t=e.data[a],isNaN(t)||n.hidden||(i+=Math.abs(t))}),i},calculateCircumference:function(t){var e=this.getMeta().total;return e>0&&!isNaN(t)?2*Math.PI*(t/e):0},getMaxBorderWidth:function(t){for(var e,n,i=0,a=this.index,r=t.length,o=0;o(i=e>i?e:i)?n:i;return i}})}},{25:25,40:40,45:45}],18:[function(t,e,n){"use strict";var i=t(25),a=t(40),r=t(45);i._set("line",{showLines:!0,spanGaps:!1,hover:{mode:"label"},scales:{xAxes:[{type:"category",id:"x-axis-0"}],yAxes:[{type:"linear",id:"y-axis-0"}]}}),e.exports=function(t){function e(t,e){return r.valueOrDefault(t.showLine,e.showLines)}t.controllers.line=t.DatasetController.extend({datasetElementType:a.Line,dataElementType:a.Point,update:function(t){var n,i,a,o=this,s=o.getMeta(),l=s.dataset,u=s.data||[],d=o.chart.options,c=d.elements.line,h=o.getScaleForId(s.yAxisID),f=o.getDataset(),g=e(f,d);for(g&&(a=l.custom||{},void 0!==f.tension&&void 0===f.lineTension&&(f.lineTension=f.tension),l._scale=h,l._datasetIndex=o.index,l._children=u,l._model={spanGaps:f.spanGaps?f.spanGaps:d.spanGaps,tension:a.tension?a.tension:r.valueOrDefault(f.lineTension,c.tension),backgroundColor:a.backgroundColor?a.backgroundColor:f.backgroundColor||c.backgroundColor,borderWidth:a.borderWidth?a.borderWidth:f.borderWidth||c.borderWidth,borderColor:a.borderColor?a.borderColor:f.borderColor||c.borderColor,borderCapStyle:a.borderCapStyle?a.borderCapStyle:f.borderCapStyle||c.borderCapStyle,borderDash:a.borderDash?a.borderDash:f.borderDash||c.borderDash,borderDashOffset:a.borderDashOffset?a.borderDashOffset:f.borderDashOffset||c.borderDashOffset,borderJoinStyle:a.borderJoinStyle?a.borderJoinStyle:f.borderJoinStyle||c.borderJoinStyle,fill:a.fill?a.fill:void 0!==f.fill?f.fill:c.fill,steppedLine:a.steppedLine?a.steppedLine:r.valueOrDefault(f.steppedLine,c.stepped),cubicInterpolationMode:a.cubicInterpolationMode?a.cubicInterpolationMode:r.valueOrDefault(f.cubicInterpolationMode,c.cubicInterpolationMode)},l.pivot()),n=0,i=u.length;n');var n=t.data,i=n.datasets,a=n.labels;if(i.length)for(var r=0;r'),a[r]&&e.push(a[r]),e.push("");return e.push(""),e.join("")},legend:{labels:{generateLabels:function(t){var e=t.data;return e.labels.length&&e.datasets.length?e.labels.map(function(n,i){var a=t.getDatasetMeta(0),o=e.datasets[0],s=a.data[i].custom||{},l=r.valueAtIndexOrDefault,u=t.options.elements.arc;return{text:n,fillStyle:s.backgroundColor?s.backgroundColor:l(o.backgroundColor,i,u.backgroundColor),strokeStyle:s.borderColor?s.borderColor:l(o.borderColor,i,u.borderColor),lineWidth:s.borderWidth?s.borderWidth:l(o.borderWidth,i,u.borderWidth),hidden:isNaN(o.data[i])||a.data[i].hidden,index:i}}):[]}},onClick:function(t,e){var n,i,a,r=e.index,o=this.chart;for(n=0,i=(o.data.datasets||[]).length;n0&&!isNaN(t)?2*Math.PI/e:0}})}},{25:25,40:40,45:45}],20:[function(t,e,n){"use strict";var i=t(25),a=t(40),r=t(45);i._set("radar",{scale:{type:"radialLinear"},elements:{line:{tension:0}}}),e.exports=function(t){t.controllers.radar=t.DatasetController.extend({datasetElementType:a.Line,dataElementType:a.Point,linkScales:r.noop,update:function(t){var e=this,n=e.getMeta(),i=n.dataset,a=n.data,o=i.custom||{},s=e.getDataset(),l=e.chart.options.elements.line,u=e.chart.scale;void 0!==s.tension&&void 0===s.lineTension&&(s.lineTension=s.tension),r.extend(n.dataset,{_datasetIndex:e.index,_scale:u,_children:a,_loop:!0,_model:{tension:o.tension?o.tension:r.valueOrDefault(s.lineTension,l.tension),backgroundColor:o.backgroundColor?o.backgroundColor:s.backgroundColor||l.backgroundColor,borderWidth:o.borderWidth?o.borderWidth:s.borderWidth||l.borderWidth,borderColor:o.borderColor?o.borderColor:s.borderColor||l.borderColor,fill:o.fill?o.fill:void 0!==s.fill?s.fill:l.fill,borderCapStyle:o.borderCapStyle?o.borderCapStyle:s.borderCapStyle||l.borderCapStyle,borderDash:o.borderDash?o.borderDash:s.borderDash||l.borderDash,borderDashOffset:o.borderDashOffset?o.borderDashOffset:s.borderDashOffset||l.borderDashOffset,borderJoinStyle:o.borderJoinStyle?o.borderJoinStyle:s.borderJoinStyle||l.borderJoinStyle}}),n.dataset.pivot(),r.each(a,function(n,i){e.updateElement(n,i,t)},e),e.updateBezierControlPoints()},updateElement:function(t,e,n){var i=this,a=t.custom||{},o=i.getDataset(),s=i.chart.scale,l=i.chart.options.elements.point,u=s.getPointPositionForValue(e,o.data[e]);void 0!==o.radius&&void 0===o.pointRadius&&(o.pointRadius=o.radius),void 0!==o.hitRadius&&void 0===o.pointHitRadius&&(o.pointHitRadius=o.hitRadius),r.extend(t,{_datasetIndex:i.index,_index:e,_scale:s,_model:{x:n?s.xCenter:u.x,y:n?s.yCenter:u.y,tension:a.tension?a.tension:r.valueOrDefault(o.lineTension,i.chart.options.elements.line.tension),radius:a.radius?a.radius:r.valueAtIndexOrDefault(o.pointRadius,e,l.radius),backgroundColor:a.backgroundColor?a.backgroundColor:r.valueAtIndexOrDefault(o.pointBackgroundColor,e,l.backgroundColor),borderColor:a.borderColor?a.borderColor:r.valueAtIndexOrDefault(o.pointBorderColor,e,l.borderColor),borderWidth:a.borderWidth?a.borderWidth:r.valueAtIndexOrDefault(o.pointBorderWidth,e,l.borderWidth),pointStyle:a.pointStyle?a.pointStyle:r.valueAtIndexOrDefault(o.pointStyle,e,l.pointStyle),hitRadius:a.hitRadius?a.hitRadius:r.valueAtIndexOrDefault(o.pointHitRadius,e,l.hitRadius)}}),t._model.skip=a.skip?a.skip:isNaN(t._model.x)||isNaN(t._model.y)},updateBezierControlPoints:function(){var t=this.chart.chartArea,e=this.getMeta();r.each(e.data,function(n,i){var a=n._model,o=r.splineCurve(r.previousItem(e.data,i,!0)._model,a,r.nextItem(e.data,i,!0)._model,a.tension);a.controlPointPreviousX=Math.max(Math.min(o.previous.x,t.right),t.left),a.controlPointPreviousY=Math.max(Math.min(o.previous.y,t.bottom),t.top),a.controlPointNextX=Math.max(Math.min(o.next.x,t.right),t.left),a.controlPointNextY=Math.max(Math.min(o.next.y,t.bottom),t.top),n.pivot()})},setHoverStyle:function(t){var e=this.chart.data.datasets[t._datasetIndex],n=t.custom||{},i=t._index,a=t._model;a.radius=n.hoverRadius?n.hoverRadius:r.valueAtIndexOrDefault(e.pointHoverRadius,i,this.chart.options.elements.point.hoverRadius),a.backgroundColor=n.hoverBackgroundColor?n.hoverBackgroundColor:r.valueAtIndexOrDefault(e.pointHoverBackgroundColor,i,r.getHoverColor(a.backgroundColor)),a.borderColor=n.hoverBorderColor?n.hoverBorderColor:r.valueAtIndexOrDefault(e.pointHoverBorderColor,i,r.getHoverColor(a.borderColor)),a.borderWidth=n.hoverBorderWidth?n.hoverBorderWidth:r.valueAtIndexOrDefault(e.pointHoverBorderWidth,i,a.borderWidth)},removeHoverStyle:function(t){var e=this.chart.data.datasets[t._datasetIndex],n=t.custom||{},i=t._index,a=t._model,o=this.chart.options.elements.point;a.radius=n.radius?n.radius:r.valueAtIndexOrDefault(e.pointRadius,i,o.radius),a.backgroundColor=n.backgroundColor?n.backgroundColor:r.valueAtIndexOrDefault(e.pointBackgroundColor,i,o.backgroundColor),a.borderColor=n.borderColor?n.borderColor:r.valueAtIndexOrDefault(e.pointBorderColor,i,o.borderColor),a.borderWidth=n.borderWidth?n.borderWidth:r.valueAtIndexOrDefault(e.pointBorderWidth,i,o.borderWidth)}})}},{25:25,40:40,45:45}],21:[function(t,e,n){"use strict";t(25)._set("scatter",{hover:{mode:"single"},scales:{xAxes:[{id:"x-axis-1",type:"linear",position:"bottom"}],yAxes:[{id:"y-axis-1",type:"linear",position:"left"}]},showLines:!1,tooltips:{callbacks:{title:function(){return""},label:function(t){return"("+t.xLabel+", "+t.yLabel+")"}}}}),e.exports=function(t){t.controllers.scatter=t.controllers.line}},{25:25}],22:[function(t,e,n){"use strict";var i=t(25),a=t(26),r=t(45);i._set("global",{animation:{duration:1e3,easing:"easeOutQuart",onProgress:r.noop,onComplete:r.noop}}),e.exports=function(t){t.Animation=a.extend({chart:null,currentStep:0,numSteps:60,easing:"",render:null,onAnimationProgress:null,onAnimationComplete:null}),t.animationService={frameDuration:17,animations:[],dropFrames:0,request:null,addAnimation:function(t,e,n,i){var a,r,o=this.animations;for(e.chart=t,i||(t.animating=!0),a=0,r=o.length;a1&&(n=Math.floor(t.dropFrames),t.dropFrames=t.dropFrames%1),t.advance(1+n);var i=Date.now();t.dropFrames+=(i-e)/t.frameDuration,t.animations.length>0&&t.requestAnimationFrame()},advance:function(t){for(var e,n,i=this.animations,a=0;a=e.numSteps?(r.callback(e.onAnimationComplete,[e],n),n.animating=!1,i.splice(a,1)):++a}},Object.defineProperty(t.Animation.prototype,"animationObject",{get:function(){return this}}),Object.defineProperty(t.Animation.prototype,"chartInstance",{get:function(){return this.chart},set:function(t){this.chart=t}})}},{25:25,26:26,45:45}],23:[function(t,e,n){"use strict";var i=t(25),a=t(45),r=t(28),o=t(48);e.exports=function(t){function e(t){var e=(t=t||{}).data=t.data||{};return e.datasets=e.datasets||[],e.labels=e.labels||[],t.options=a.configMerge(i.global,i[t.type],t.options||{}),t}function n(t){var e=t.options;e.scale?t.scale.options=e.scale:e.scales&&e.scales.xAxes.concat(e.scales.yAxes).forEach(function(e){t.scales[e.id].options=e}),t.tooltip._options=e.tooltips}function s(t){return"top"===t||"bottom"===t}var l=t.plugins;t.types={},t.instances={},t.controllers={},a.extend(t.prototype,{construct:function(n,i){var r=this;i=e(i);var s=o.acquireContext(n,i),l=s&&s.canvas,u=l&&l.height,d=l&&l.width;r.id=a.uid(),r.ctx=s,r.canvas=l,r.config=i,r.width=d,r.height=u,r.aspectRatio=u?d/u:null,r.options=i.options,r._bufferedRender=!1,r.chart=r,r.controller=r,t.instances[r.id]=r,Object.defineProperty(r,"data",{get:function(){return r.config.data},set:function(t){r.config.data=t}}),s&&l?(r.initialize(),r.update()):console.error("Failed to create chart: can't acquire context from the given item")},initialize:function(){var t=this;return l.notify(t,"beforeInit"),a.retinaScale(t,t.options.devicePixelRatio),t.bindEvents(),t.options.responsive&&t.resize(!0),t.ensureScalesHaveIDs(),t.buildScales(),t.initToolTip(),l.notify(t,"afterInit"),t},clear:function(){return a.canvas.clear(this),this},stop:function(){return t.animationService.cancelAnimation(this),this},resize:function(t){var e=this,n=e.options,i=e.canvas,r=n.maintainAspectRatio&&e.aspectRatio||null,o=Math.max(0,Math.floor(a.getMaximumWidth(i))),s=Math.max(0,Math.floor(r?o/r:a.getMaximumHeight(i)));if((e.width!==o||e.height!==s)&&(i.width=e.width=o,i.height=e.height=s,i.style.width=o+"px",i.style.height=s+"px",a.retinaScale(e,n.devicePixelRatio),!t)){var u={width:o,height:s};l.notify(e,"resize",[u]),e.options.onResize&&e.options.onResize(e,u),e.stop(),e.update(e.options.responsiveAnimationDuration)}},ensureScalesHaveIDs:function(){var t=this.options,e=t.scales||{},n=t.scale;a.each(e.xAxes,function(t,e){t.id=t.id||"x-axis-"+e}),a.each(e.yAxes,function(t,e){t.id=t.id||"y-axis-"+e}),n&&(n.id=n.id||"scale")},buildScales:function(){var e=this,n=e.options,i=e.scales={},r=[];n.scales&&(r=r.concat((n.scales.xAxes||[]).map(function(t){return{options:t,dtype:"category",dposition:"bottom"}}),(n.scales.yAxes||[]).map(function(t){return{options:t,dtype:"linear",dposition:"left"}}))),n.scale&&r.push({options:n.scale,dtype:"radialLinear",isDefault:!0,dposition:"chartArea"}),a.each(r,function(n){var r=n.options,o=a.valueOrDefault(r.type,n.dtype),l=t.scaleService.getScaleConstructor(o);if(l){s(r.position)!==s(n.dposition)&&(r.position=n.dposition);var u=new l({id:r.id,options:r,ctx:e.ctx,chart:e});i[u.id]=u,u.mergeTicksOptions(),n.isDefault&&(e.scale=u)}}),t.scaleService.addScalesToLayout(this)},buildOrUpdateControllers:function(){var e=this,n=[],i=[];return a.each(e.data.datasets,function(a,r){var o=e.getDatasetMeta(r),s=a.type||e.config.type;if(o.type&&o.type!==s&&(e.destroyDatasetMeta(r),o=e.getDatasetMeta(r)),o.type=s,n.push(o.type),o.controller)o.controller.updateIndex(r);else{var l=t.controllers[o.type];if(void 0===l)throw new Error('"'+o.type+'" is not a chart type.');o.controller=new l(e,r),i.push(o.controller)}},e),i},resetElements:function(){var t=this;a.each(t.data.datasets,function(e,n){t.getDatasetMeta(n).controller.reset()},t)},reset:function(){this.resetElements(),this.tooltip.initialize()},update:function(t){var e=this;if(t&&"object"==typeof t||(t={duration:t,lazy:arguments[1]}),n(e),!1!==l.notify(e,"beforeUpdate")){e.tooltip._data=e.data;var i=e.buildOrUpdateControllers();a.each(e.data.datasets,function(t,n){e.getDatasetMeta(n).controller.buildOrUpdateElements()},e),e.updateLayout(),a.each(i,function(t){t.reset()}),e.updateDatasets(),e.tooltip.initialize(),e.lastActive=[],l.notify(e,"afterUpdate"),e._bufferedRender?e._bufferedRequest={duration:t.duration,easing:t.easing,lazy:t.lazy}:e.render(t)}},updateLayout:function(){var e=this;!1!==l.notify(e,"beforeLayout")&&(t.layoutService.update(this,this.width,this.height),l.notify(e,"afterScaleUpdate"),l.notify(e,"afterLayout"))},updateDatasets:function(){var t=this;if(!1!==l.notify(t,"beforeDatasetsUpdate")){for(var e=0,n=t.data.datasets.length;e=0;--n)e.isDatasetVisible(n)&&e.drawDataset(n,t);l.notify(e,"afterDatasetsDraw",[t])}},drawDataset:function(t,e){var n=this,i=n.getDatasetMeta(t),a={meta:i,index:t,easingValue:e};!1!==l.notify(n,"beforeDatasetDraw",[a])&&(i.controller.draw(e),l.notify(n,"afterDatasetDraw",[a]))},_drawTooltip:function(t){var e=this,n=e.tooltip,i={tooltip:n,easingValue:t};!1!==l.notify(e,"beforeTooltipDraw",[i])&&(n.draw(),l.notify(e,"afterTooltipDraw",[i]))},getElementAtEvent:function(t){return r.modes.single(this,t)},getElementsAtEvent:function(t){return r.modes.label(this,t,{intersect:!0})},getElementsAtXAxis:function(t){return r.modes["x-axis"](this,t,{intersect:!0})},getElementsAtEventForMode:function(t,e,n){var i=r.modes[e];return"function"==typeof i?i(this,t,n):[]},getDatasetAtEvent:function(t){return r.modes.dataset(this,t,{intersect:!0})},getDatasetMeta:function(t){var e=this,n=e.data.datasets[t];n._meta||(n._meta={});var i=n._meta[e.id];return i||(i=n._meta[e.id]={type:null,data:[],dataset:null,controller:null,hidden:null,xAxisID:null,yAxisID:null}),i},getVisibleDatasetCount:function(){for(var t=0,e=0,n=this.data.datasets.length;e0||(a.forEach(function(e){delete t[e]}),delete t._chartjs)}}var a=["push","pop","shift","splice","unshift"];t.DatasetController=function(t,e){this.initialize(t,e)},i.extend(t.DatasetController.prototype,{datasetElementType:null,dataElementType:null,initialize:function(t,e){var n=this;n.chart=t,n.index=e,n.linkScales(),n.addElements()},updateIndex:function(t){this.index=t},linkScales:function(){var t=this,e=t.getMeta(),n=t.getDataset();null===e.xAxisID&&(e.xAxisID=n.xAxisID||t.chart.options.scales.xAxes[0].id),null===e.yAxisID&&(e.yAxisID=n.yAxisID||t.chart.options.scales.yAxes[0].id)},getDataset:function(){return this.chart.data.datasets[this.index]},getMeta:function(){return this.chart.getDatasetMeta(this.index)},getScaleForId:function(t){return this.chart.scales[t]},reset:function(){this.update(!0)},destroy:function(){this._data&&n(this._data,this)},createMetaDataset:function(){var t=this,e=t.datasetElementType;return e&&new e({_chart:t.chart,_datasetIndex:t.index})},createMetaData:function(t){var e=this,n=e.dataElementType;return n&&new n({_chart:e.chart,_datasetIndex:e.index,_index:t})},addElements:function(){var t,e,n=this,i=n.getMeta(),a=n.getDataset().data||[],r=i.data;for(t=0,e=a.length;ti&&t.insertElements(i,a-i)},insertElements:function(t,e){for(var n=0;n=n[e].length&&n[e].push({}),!n[e][o].type||l.type&&l.type!==n[e][o].type?r.merge(n[e][o],[t.scaleService.getScaleDefaults(s),l]):r.merge(n[e][o],l)}else r._merger(e,n,i,a)}})},r.where=function(t,e){if(r.isArray(t)&&Array.prototype.filter)return t.filter(e);var n=[];return r.each(t,function(t){e(t)&&n.push(t)}),n},r.findIndex=Array.prototype.findIndex?function(t,e,n){return t.findIndex(e,n)}:function(t,e,n){n=void 0===n?t:n;for(var i=0,a=t.length;i=0;i--){var a=t[i];if(e(a))return a}},r.isNumber=function(t){return!isNaN(parseFloat(t))&&isFinite(t)},r.almostEquals=function(t,e,n){return Math.abs(t-e)t},r.max=function(t){return t.reduce(function(t,e){return isNaN(e)?t:Math.max(t,e)},Number.NEGATIVE_INFINITY)},r.min=function(t){return t.reduce(function(t,e){return isNaN(e)?t:Math.min(t,e)},Number.POSITIVE_INFINITY)},r.sign=Math.sign?function(t){return Math.sign(t)}:function(t){return 0==(t=+t)||isNaN(t)?t:t>0?1:-1},r.log10=Math.log10?function(t){return Math.log10(t)}:function(t){return Math.log(t)/Math.LN10},r.toRadians=function(t){return t*(Math.PI/180)},r.toDegrees=function(t){return t*(180/Math.PI)},r.getAngleFromPoint=function(t,e){var n=e.x-t.x,i=e.y-t.y,a=Math.sqrt(n*n+i*i),r=Math.atan2(i,n);return r<-.5*Math.PI&&(r+=2*Math.PI),{angle:r,distance:a}},r.distanceBetweenPoints=function(t,e){return Math.sqrt(Math.pow(e.x-t.x,2)+Math.pow(e.y-t.y,2))},r.aliasPixel=function(t){return t%2==0?0:.5},r.splineCurve=function(t,e,n,i){var a=t.skip?e:t,r=e,o=n.skip?e:n,s=Math.sqrt(Math.pow(r.x-a.x,2)+Math.pow(r.y-a.y,2)),l=Math.sqrt(Math.pow(o.x-r.x,2)+Math.pow(o.y-r.y,2)),u=s/(s+l),d=l/(s+l),c=i*(u=isNaN(u)?0:u),h=i*(d=isNaN(d)?0:d);return{previous:{x:r.x-c*(o.x-a.x),y:r.y-c*(o.y-a.y)},next:{x:r.x+h*(o.x-a.x),y:r.y+h*(o.y-a.y)}}},r.EPSILON=Number.EPSILON||1e-14,r.splineCurveMonotone=function(t){var e,n,i,a,o=(t||[]).map(function(t){return{model:t._model,deltaK:0,mK:0}}),s=o.length;for(e=0;e0?o[e-1]:null,(a=e0?o[e-1]:null,a=e=t.length-1?t[0]:t[e+1]:e>=t.length-1?t[t.length-1]:t[e+1]},r.previousItem=function(t,e,n){return n?e<=0?t[t.length-1]:t[e-1]:e<=0?t[0]:t[e-1]},r.niceNum=function(t,e){var n=Math.floor(r.log10(t)),i=t/Math.pow(10,n);return(e?i<1.5?1:i<3?2:i<7?5:10:i<=1?1:i<=2?2:i<=5?5:10)*Math.pow(10,n)},r.requestAnimFrame="undefined"==typeof window?function(t){t()}:window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(t){return window.setTimeout(t,1e3/60)},r.getRelativePosition=function(t,e){var n,i,a=t.originalEvent||t,o=t.currentTarget||t.srcElement,s=o.getBoundingClientRect(),l=a.touches;l&&l.length>0?(n=l[0].clientX,i=l[0].clientY):(n=a.clientX,i=a.clientY);var u=parseFloat(r.getStyle(o,"padding-left")),d=parseFloat(r.getStyle(o,"padding-top")),c=parseFloat(r.getStyle(o,"padding-right")),h=parseFloat(r.getStyle(o,"padding-bottom")),f=s.right-s.left-u-c,g=s.bottom-s.top-d-h;return n=Math.round((n-s.left-u)/f*o.width/e.currentDevicePixelRatio),i=Math.round((i-s.top-d)/g*o.height/e.currentDevicePixelRatio),{x:n,y:i}},r.getConstraintWidth=function(t){return o(t,"max-width","clientWidth")},r.getConstraintHeight=function(t){return o(t,"max-height","clientHeight")},r.getMaximumWidth=function(t){var e=t.parentNode;if(!e)return t.clientWidth;var n=parseInt(r.getStyle(e,"padding-left"),10),i=parseInt(r.getStyle(e,"padding-right"),10),a=e.clientWidth-n-i,o=r.getConstraintWidth(t);return isNaN(o)?a:Math.min(a,o)},r.getMaximumHeight=function(t){var e=t.parentNode;if(!e)return t.clientHeight;var n=parseInt(r.getStyle(e,"padding-top"),10),i=parseInt(r.getStyle(e,"padding-bottom"),10),a=e.clientHeight-n-i,o=r.getConstraintHeight(t);return isNaN(o)?a:Math.min(a,o)},r.getStyle=function(t,e){return t.currentStyle?t.currentStyle[e]:document.defaultView.getComputedStyle(t,null).getPropertyValue(e)},r.retinaScale=function(t,e){var n=t.currentDevicePixelRatio=e||window.devicePixelRatio||1;if(1!==n){var i=t.canvas,a=t.height,r=t.width;i.height=a*n,i.width=r*n,t.ctx.scale(n,n),i.style.height=a+"px",i.style.width=r+"px"}},r.fontString=function(t,e,n){return e+" "+t+"px "+n},r.longestText=function(t,e,n,i){var a=(i=i||{}).data=i.data||{},o=i.garbageCollect=i.garbageCollect||[];i.font!==e&&(a=i.data={},o=i.garbageCollect=[],i.font=e),t.font=e;var s=0;r.each(n,function(e){void 0!==e&&null!==e&&!0!==r.isArray(e)?s=r.measureText(t,a,o,s,e):r.isArray(e)&&r.each(e,function(e){void 0===e||null===e||r.isArray(e)||(s=r.measureText(t,a,o,s,e))})});var l=o.length/2;if(l>n.length){for(var u=0;ui&&(i=r),i},r.numberOfLabelLines=function(t){var e=1;return r.each(t,function(t){r.isArray(t)&&t.length>e&&(e=t.length)}),e},r.color=i?function(t){return t instanceof CanvasGradient&&(t=a.global.defaultColor),i(t)}:function(t){return console.error("Color.js not found!"),t},r.getHoverColor=function(t){return t instanceof CanvasPattern?t:r.color(t).saturate(.5).darken(.1).rgbString()}}},{2:2,25:25,45:45}],28:[function(t,e,n){"use strict";function i(t,e){return t.native?{x:t.x,y:t.y}:u.getRelativePosition(t,e)}function a(t,e){var n,i,a,r,o;for(i=0,r=t.data.datasets.length;i0&&(u=t.getDatasetMeta(u[0]._datasetIndex).data),u},"x-axis":function(t,e){return l(t,e,{intersect:!1})},point:function(t,e){return r(t,i(e,t))},nearest:function(t,e,n){var a=i(e,t);n.axis=n.axis||"xy";var r=s(n.axis),l=o(t,a,n.intersect,r);return l.length>1&&l.sort(function(t,e){var n=t.getArea()-e.getArea();return 0===n&&(n=t._datasetIndex-e._datasetIndex),n}),l.slice(0,1)},x:function(t,e,n){var r=i(e,t),o=[],s=!1;return a(t,function(t){t.inXRange(r.x)&&o.push(t),t.inRange(r.x,r.y)&&(s=!0)}),n.intersect&&!s&&(o=[]),o},y:function(t,e,n){var r=i(e,t),o=[],s=!1;return a(t,function(t){t.inYRange(r.y)&&o.push(t),t.inRange(r.x,r.y)&&(s=!0)}),n.intersect&&!s&&(o=[]),o}}}},{45:45}],29:[function(t,e,n){"use strict";t(25)._set("global",{responsive:!0,responsiveAnimationDuration:0,maintainAspectRatio:!0,events:["mousemove","mouseout","click","touchstart","touchmove"],hover:{onHover:null,mode:"nearest",intersect:!0,animationDuration:400},onClick:null,defaultColor:"rgba(0,0,0,0.1)",defaultFontColor:"#666",defaultFontFamily:"'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",defaultFontSize:12,defaultFontStyle:"normal",showLines:!0,elements:{},layout:{padding:{top:0,right:0,bottom:0,left:0}}}),e.exports=function(){var t=function(t,e){return this.construct(t,e),this};return t.Chart=t,t}},{25:25}],30:[function(t,e,n){"use strict";var i=t(45);e.exports=function(t){function e(t,e){return i.where(t,function(t){return t.position===e})}function n(t,e){t.forEach(function(t,e){return t._tmpIndex_=e,t}),t.sort(function(t,n){var i=e?n:t,a=e?t:n;return i.weight===a.weight?i._tmpIndex_-a._tmpIndex_:i.weight-a.weight}),t.forEach(function(t){delete t._tmpIndex_})}t.layoutService={defaults:{},addBox:function(t,e){t.boxes||(t.boxes=[]),e.fullWidth=e.fullWidth||!1,e.position=e.position||"top",e.weight=e.weight||0,t.boxes.push(e)},removeBox:function(t,e){var n=t.boxes?t.boxes.indexOf(e):-1;-1!==n&&t.boxes.splice(n,1)},configure:function(t,e,n){for(var i,a=["fullWidth","position","weight"],r=a.length,o=0;oh&&lt.maxHeight){l--;break}l++,c=u*d}t.labelRotation=l},afterCalculateTickRotation:function(){s.callback(this.options.afterCalculateTickRotation,[this])},beforeFit:function(){s.callback(this.options.beforeFit,[this])},fit:function(){var t=this,a=t.minSize={width:0,height:0},r=i(t._ticks),o=t.options,u=o.ticks,d=o.scaleLabel,c=o.gridLines,h=o.display,f=t.isHorizontal(),g=n(u),m=o.gridLines.tickMarkLength;if(a.width=f?t.isFullWidth()?t.maxWidth-t.margins.left-t.margins.right:t.maxWidth:h&&c.drawTicks?m:0,a.height=f?h&&c.drawTicks?m:0:t.maxHeight,d.display&&h){var p=l(d)+s.options.toPadding(d.padding).height;f?a.height+=p:a.width+=p}if(u.display&&h){var v=s.longestText(t.ctx,g.font,r,t.longestTextCache),y=s.numberOfLabelLines(r),b=.5*g.size,x=t.options.ticks.padding;if(f){t.longestLabelWidth=v;var _=s.toRadians(t.labelRotation),k=Math.cos(_),w=Math.sin(_)*v+g.size*y+b*(y-1)+b;a.height=Math.min(t.maxHeight,a.height+w+x),t.ctx.font=g.font;var M=e(t.ctx,r[0],g.font),S=e(t.ctx,r[r.length-1],g.font);0!==t.labelRotation?(t.paddingLeft="bottom"===o.position?k*M+3:k*b+3,t.paddingRight="bottom"===o.position?k*b+3:k*S+3):(t.paddingLeft=M/2+3,t.paddingRight=S/2+3)}else u.mirror?v=0:v+=x+b,a.width=Math.min(t.maxWidth,a.width+v),t.paddingTop=g.size/2,t.paddingBottom=g.size/2}t.handleMargins(),t.width=a.width,t.height=a.height},handleMargins:function(){var t=this;t.margins&&(t.paddingLeft=Math.max(t.paddingLeft-t.margins.left,0),t.paddingTop=Math.max(t.paddingTop-t.margins.top,0),t.paddingRight=Math.max(t.paddingRight-t.margins.right,0),t.paddingBottom=Math.max(t.paddingBottom-t.margins.bottom,0))},afterFit:function(){s.callback(this.options.afterFit,[this])},isHorizontal:function(){return"top"===this.options.position||"bottom"===this.options.position},isFullWidth:function(){return this.options.fullWidth},getRightValue:function(t){if(s.isNullOrUndef(t))return NaN;if("number"==typeof t&&!isFinite(t))return NaN;if(t)if(this.isHorizontal()){if(void 0!==t.x)return this.getRightValue(t.x)}else if(void 0!==t.y)return this.getRightValue(t.y);return t},getLabelForIndex:s.noop,getPixelForValue:s.noop,getValueForPixel:s.noop,getPixelForTick:function(t){var e=this,n=e.options.offset;if(e.isHorizontal()){var i=(e.width-(e.paddingLeft+e.paddingRight))/Math.max(e._ticks.length-(n?0:1),1),a=i*t+e.paddingLeft;n&&(a+=i/2);var r=e.left+Math.round(a);return r+=e.isFullWidth()?e.margins.left:0}var o=e.height-(e.paddingTop+e.paddingBottom);return e.top+t*(o/(e._ticks.length-1))},getPixelForDecimal:function(t){var e=this;if(e.isHorizontal()){var n=(e.width-(e.paddingLeft+e.paddingRight))*t+e.paddingLeft,i=e.left+Math.round(n);return i+=e.isFullWidth()?e.margins.left:0}return e.top+t*e.height},getBasePixel:function(){return this.getPixelForValue(this.getBaseValue())},getBaseValue:function(){var t=this,e=t.min,n=t.max;return t.beginAtZero?0:e<0&&n<0?n:e>0&&n>0?e:0},_autoSkip:function(t){var e,n,i,a,r=this,o=r.isHorizontal(),l=r.options.ticks.minor,u=t.length,d=s.toRadians(r.labelRotation),c=Math.cos(d),h=r.longestLabelWidth*c,f=[];for(l.maxTicksLimit&&(a=l.maxTicksLimit),o&&(e=!1,(h+l.autoSkipPadding)*u>r.width-(r.paddingLeft+r.paddingRight)&&(e=1+Math.floor((h+l.autoSkipPadding)*u/(r.width-(r.paddingLeft+r.paddingRight)))),a&&u>a&&(e=Math.max(e,Math.floor(u/a)))),n=0;n1&&n%e>0||n%e==0&&n+e>=u)&&n!==u-1&&delete i.label,f.push(i);return f},draw:function(t){var e=this,i=e.options;if(i.display){var o=e.ctx,u=r.global,d=i.ticks.minor,c=i.ticks.major||d,h=i.gridLines,f=i.scaleLabel,g=0!==e.labelRotation,m=e.isHorizontal(),p=d.autoSkip?e._autoSkip(e.getTicks()):e.getTicks(),v=s.valueOrDefault(d.fontColor,u.defaultFontColor),y=n(d),b=s.valueOrDefault(c.fontColor,u.defaultFontColor),x=n(c),_=h.drawTicks?h.tickMarkLength:0,k=s.valueOrDefault(f.fontColor,u.defaultFontColor),w=n(f),M=s.options.toPadding(f.padding),S=s.toRadians(e.labelRotation),D=[],C="right"===i.position?e.left:e.right-_,P="right"===i.position?e.left+_:e.right,T="bottom"===i.position?e.top:e.bottom-_,A="bottom"===i.position?e.top+_:e.bottom;if(s.each(p,function(n,r){if(!s.isNullOrUndef(n.label)){var o,l,c,f,v=n.label;r===e.zeroLineIndex&&i.offset===h.offsetGridLines?(o=h.zeroLineWidth,l=h.zeroLineColor,c=h.zeroLineBorderDash,f=h.zeroLineBorderDashOffset):(o=s.valueAtIndexOrDefault(h.lineWidth,r),l=s.valueAtIndexOrDefault(h.color,r),c=s.valueOrDefault(h.borderDash,u.borderDash),f=s.valueOrDefault(h.borderDashOffset,u.borderDashOffset));var y,b,x,k,w,M,I,O,F,R,L="middle",W="middle",Y=d.padding;if(m){var N=_+Y;"bottom"===i.position?(W=g?"middle":"top",L=g?"right":"center",R=e.top+N):(W=g?"middle":"bottom",L=g?"left":"center",R=e.bottom-N);var z=a(e,r,h.offsetGridLines&&p.length>1);z1);H0)n=t.stepSize;else{var r=i.niceNum(e.max-e.min,!1);n=i.niceNum(r/(t.maxTicks-1),!0)}var o=Math.floor(e.min/n)*n,s=Math.ceil(e.max/n)*n;t.min&&t.max&&t.stepSize&&i.almostWhole((t.max-t.min)/t.stepSize,n/1e3)&&(o=t.min,s=t.max);var l=(s-o)/n;l=i.almostEquals(l,Math.round(l),n/1e3)?Math.round(l):Math.ceil(l),a.push(void 0!==t.min?t.min:o);for(var u=1;u3?n[2]-n[1]:n[1]-n[0];Math.abs(a)>1&&t!==Math.floor(t)&&(a=t-Math.floor(t));var r=i.log10(Math.abs(a)),o="";if(0!==t){var s=-1*Math.floor(r);s=Math.max(Math.min(s,20),0),o=t.toFixed(s)}else o="0";return o},logarithmic:function(t,e,n){var a=t/Math.pow(10,Math.floor(i.log10(t)));return 0===t?"0":1===a||2===a||5===a||0===e||e===n.length-1?t.toExponential():""}}}},{45:45}],35:[function(t,e,n){"use strict";var i=t(25),a=t(26),r=t(45);i._set("global",{tooltips:{enabled:!0,custom:null,mode:"nearest",position:"average",intersect:!0,backgroundColor:"rgba(0,0,0,0.8)",titleFontStyle:"bold",titleSpacing:2,titleMarginBottom:6,titleFontColor:"#fff",titleAlign:"left",bodySpacing:2,bodyFontColor:"#fff",bodyAlign:"left",footerFontStyle:"bold",footerSpacing:2,footerMarginTop:6,footerFontColor:"#fff",footerAlign:"left",yPadding:6,xPadding:6,caretPadding:2,caretSize:5,cornerRadius:6,multiKeyBackground:"#fff",displayColors:!0,borderColor:"rgba(0,0,0,0)",borderWidth:0,callbacks:{beforeTitle:r.noop,title:function(t,e){var n="",i=e.labels,a=i?i.length:0;if(t.length>0){var r=t[0];r.xLabel?n=r.xLabel:a>0&&r.indexi.height-e.height&&(o="bottom");var s,l,u,d,c,h=(a.left+a.right)/2,f=(a.top+a.bottom)/2;"center"===o?(s=function(t){return t<=h},l=function(t){return t>h}):(s=function(t){return t<=e.width/2},l=function(t){return t>=i.width-e.width/2}),u=function(t){return t+e.width>i.width},d=function(t){return t-e.width<0},c=function(t){return t<=f?"top":"bottom"},s(n.x)?(r="left",u(n.x)&&(r="center",o=c(n.y))):l(n.x)&&(r="right",d(n.x)&&(r="center",o=c(n.y)));var g=t._options;return{xAlign:g.xAlign?g.xAlign:r,yAlign:g.yAlign?g.yAlign:o}}function d(t,e,n){var i=t.x,a=t.y,r=t.caretSize,o=t.caretPadding,s=t.cornerRadius,l=n.xAlign,u=n.yAlign,d=r+o,c=s+o;return"right"===l?i-=e.width:"center"===l&&(i-=e.width/2),"top"===u?a+=d:a-="bottom"===u?e.height+d:e.height/2,"center"===u?"left"===l?i+=d:"right"===l&&(i-=d):"left"===l?i-=c:"right"===l&&(i+=c),{x:i,y:a}}t.Tooltip=a.extend({initialize:function(){this._model=s(this._options),this._lastActive=[]},getTitle:function(){var t=this,e=t._options.callbacks,i=e.beforeTitle.apply(t,arguments),a=e.title.apply(t,arguments),r=e.afterTitle.apply(t,arguments),o=[];return o=n(o,i),o=n(o,a),o=n(o,r)},getBeforeBody:function(){var t=this._options.callbacks.beforeBody.apply(this,arguments);return r.isArray(t)?t:void 0!==t?[t]:[]},getBody:function(t,e){var i=this,a=i._options.callbacks,o=[];return r.each(t,function(t){var r={before:[],lines:[],after:[]};n(r.before,a.beforeLabel.call(i,t,e)),n(r.lines,a.label.call(i,t,e)),n(r.after,a.afterLabel.call(i,t,e)),o.push(r)}),o},getAfterBody:function(){var t=this._options.callbacks.afterBody.apply(this,arguments);return r.isArray(t)?t:void 0!==t?[t]:[]},getFooter:function(){var t=this,e=t._options.callbacks,i=e.beforeFooter.apply(t,arguments),a=e.footer.apply(t,arguments),r=e.afterFooter.apply(t,arguments),o=[];return o=n(o,i),o=n(o,a),o=n(o,r)},update:function(e){var n,i,a=this,c=a._options,h=a._model,f=a._model=s(c),g=a._active,m=a._data,p={xAlign:h.xAlign,yAlign:h.yAlign},v={x:h.x,y:h.y},y={width:h.width,height:h.height},b={x:h.caretX,y:h.caretY};if(g.length){f.opacity=1;var x=[],_=[];b=t.Tooltip.positioners[c.position].call(a,g,a._eventPosition);var k=[];for(n=0,i=g.length;n0&&i.stroke()},draw:function(){var t=this._chart.ctx,e=this._view;if(0!==e.opacity){var n={width:e.width,height:e.height},i={x:e.x,y:e.y},a=Math.abs(e.opacity<.001)?0:e.opacity,r=e.title.length||e.beforeBody.length||e.body.length||e.afterBody.length||e.footer.length;this._options.enabled&&r&&(this.drawBackground(i,e,t,n,a),i.x+=e.xPadding,i.y+=e.yPadding,this.drawTitle(i,e,t,a),this.drawBody(i,e,t,a),this.drawFooter(i,e,t,a))}},handleEvent:function(t){var e=this,n=e._options,i=!1;if(e._lastActive=e._lastActive||[],"mouseout"===t.type?e._active=[]:e._active=e._chart.getElementsAtEventForMode(t,n.mode,n),!(i=!r.arrayEquals(e._active,e._lastActive)))return!1;if(e._lastActive=e._active,n.enabled||n.custom){e._eventPosition={x:t.x,y:t.y};var a=e._model;e.update(!0),e.pivot(),i|=a.x!==e._model.x||a.y!==e._model.y}return i}}),t.Tooltip.positioners={average:function(t){if(!t.length)return!1;var e,n,i=0,a=0,r=0;for(e=0,n=t.length;el;)a-=2*Math.PI;for(;a=s&&a<=l,d=o>=n.innerRadius&&o<=n.outerRadius;return u&&d}return!1},getCenterPoint:function(){var t=this._view,e=(t.startAngle+t.endAngle)/2,n=(t.innerRadius+t.outerRadius)/2;return{x:t.x+Math.cos(e)*n,y:t.y+Math.sin(e)*n}},getArea:function(){var t=this._view;return Math.PI*((t.endAngle-t.startAngle)/(2*Math.PI))*(Math.pow(t.outerRadius,2)-Math.pow(t.innerRadius,2))},tooltipPosition:function(){var t=this._view,e=t.startAngle+(t.endAngle-t.startAngle)/2,n=(t.outerRadius-t.innerRadius)/2+t.innerRadius;return{x:t.x+Math.cos(e)*n,y:t.y+Math.sin(e)*n}},draw:function(){var t=this._chart.ctx,e=this._view,n=e.startAngle,i=e.endAngle;t.beginPath(),t.arc(e.x,e.y,e.outerRadius,n,i),t.arc(e.x,e.y,e.innerRadius,i,n,!0),t.closePath(),t.strokeStyle=e.borderColor,t.lineWidth=e.borderWidth,t.fillStyle=e.backgroundColor,t.fill(),t.lineJoin="bevel",e.borderWidth&&t.stroke()}})},{25:25,26:26,45:45}],37:[function(t,e,n){"use strict";var i=t(25),a=t(26),r=t(45),o=i.global;i._set("global",{elements:{line:{tension:.4,backgroundColor:o.defaultColor,borderWidth:3,borderColor:o.defaultColor,borderCapStyle:"butt",borderDash:[],borderDashOffset:0,borderJoinStyle:"miter",capBezierPoints:!0,fill:!0}}}),e.exports=a.extend({draw:function(){var t,e,n,i,a=this,s=a._view,l=a._chart.ctx,u=s.spanGaps,d=a._children.slice(),c=o.elements.line,h=-1;for(a._loop&&d.length&&d.push(d[0]),l.save(),l.lineCap=s.borderCapStyle||c.borderCapStyle,l.setLineDash&&l.setLineDash(s.borderDash||c.borderDash),l.lineDashOffset=s.borderDashOffset||c.borderDashOffset,l.lineJoin=s.borderJoinStyle||c.borderJoinStyle,l.lineWidth=s.borderWidth||c.borderWidth,l.strokeStyle=s.borderColor||o.defaultColor,l.beginPath(),h=-1,t=0;te?1:-1,o=1,s=u.borderSkipped||"left"):(e=u.x-u.width/2,n=u.x+u.width/2,i=u.y,r=1,o=(a=u.base)>i?1:-1,s=u.borderSkipped||"bottom"),d){var c=Math.min(Math.abs(e-n),Math.abs(i-a)),h=(d=d>c?c:d)/2,f=e+("left"!==s?h*r:0),g=n+("right"!==s?-h*r:0),m=i+("top"!==s?h*o:0),p=a+("bottom"!==s?-h*o:0);f!==g&&(i=m,a=p),m!==p&&(e=f,n=g)}l.beginPath(),l.fillStyle=u.backgroundColor,l.strokeStyle=u.borderColor,l.lineWidth=d;var v=[[e,a],[e,i],[n,i],[n,a]],y=["bottom","left","top","right"].indexOf(s,0);-1===y&&(y=0);var b=t(0);l.moveTo(b[0],b[1]);for(var x=1;x<4;x++)b=t(x),l.lineTo(b[0],b[1]);l.fill(),d&&l.stroke()},height:function(){var t=this._view;return t.base-t.y},inRange:function(t,e){var n=!1;if(this._view){var i=a(this);n=t>=i.left&&t<=i.right&&e>=i.top&&e<=i.bottom}return n},inLabelRange:function(t,e){var n=this;if(!n._view)return!1;var r=a(n);return i(n)?t>=r.left&&t<=r.right:e>=r.top&&e<=r.bottom},inXRange:function(t){var e=a(this);return t>=e.left&&t<=e.right},inYRange:function(t){var e=a(this);return t>=e.top&&t<=e.bottom},getCenterPoint:function(){var t,e,n=this._view;return i(this)?(t=n.x,e=(n.y+n.base)/2):(t=(n.x+n.base)/2,e=n.y),{x:t,y:e}},getArea:function(){var t=this._view;return t.width*Math.abs(t.y-t.base)},tooltipPosition:function(){var t=this._view;return{x:t.x,y:t.y}}})},{25:25,26:26}],40:[function(t,e,n){"use strict";e.exports={},e.exports.Arc=t(36),e.exports.Line=t(37),e.exports.Point=t(38),e.exports.Rectangle=t(39)},{36:36,37:37,38:38,39:39}],41:[function(t,e,n){"use strict";var i=t(42),n=e.exports={clear:function(t){t.ctx.clearRect(0,0,t.width,t.height)},roundedRect:function(t,e,n,i,a,r){if(r){var o=Math.min(r,i/2),s=Math.min(r,a/2);t.moveTo(e+o,n),t.lineTo(e+i-o,n),t.quadraticCurveTo(e+i,n,e+i,n+s),t.lineTo(e+i,n+a-s),t.quadraticCurveTo(e+i,n+a,e+i-o,n+a),t.lineTo(e+o,n+a),t.quadraticCurveTo(e,n+a,e,n+a-s),t.lineTo(e,n+s),t.quadraticCurveTo(e,n,e+o,n)}else t.rect(e,n,i,a)},drawPoint:function(t,e,n,i,a){var r,o,s,l,u,d;if(!e||"object"!=typeof e||"[object HTMLImageElement]"!==(r=e.toString())&&"[object HTMLCanvasElement]"!==r){if(!(isNaN(n)||n<=0)){switch(e){default:t.beginPath(),t.arc(i,a,n,0,2*Math.PI),t.closePath(),t.fill();break;case"triangle":t.beginPath(),u=(o=3*n/Math.sqrt(3))*Math.sqrt(3)/2,t.moveTo(i-o/2,a+u/3),t.lineTo(i+o/2,a+u/3),t.lineTo(i,a-2*u/3),t.closePath(),t.fill();break;case"rect":d=1/Math.SQRT2*n,t.beginPath(),t.fillRect(i-d,a-d,2*d,2*d),t.strokeRect(i-d,a-d,2*d,2*d);break;case"rectRounded":var c=n/Math.SQRT2,h=i-c,f=a-c,g=Math.SQRT2*n;t.beginPath(),this.roundedRect(t,h,f,g,g,n/2),t.closePath(),t.fill();break;case"rectRot":d=1/Math.SQRT2*n,t.beginPath(),t.moveTo(i-d,a),t.lineTo(i,a+d),t.lineTo(i+d,a),t.lineTo(i,a-d),t.closePath(),t.fill();break;case"cross":t.beginPath(),t.moveTo(i,a+n),t.lineTo(i,a-n),t.moveTo(i-n,a),t.lineTo(i+n,a),t.closePath();break;case"crossRot":t.beginPath(),s=Math.cos(Math.PI/4)*n,l=Math.sin(Math.PI/4)*n,t.moveTo(i-s,a-l),t.lineTo(i+s,a+l),t.moveTo(i-s,a+l),t.lineTo(i+s,a-l),t.closePath();break;case"star":t.beginPath(),t.moveTo(i,a+n),t.lineTo(i,a-n),t.moveTo(i-n,a),t.lineTo(i+n,a),s=Math.cos(Math.PI/4)*n,l=Math.sin(Math.PI/4)*n,t.moveTo(i-s,a-l),t.lineTo(i+s,a+l),t.moveTo(i-s,a+l),t.lineTo(i+s,a-l),t.closePath();break;case"line":t.beginPath(),t.moveTo(i-n,a),t.lineTo(i+n,a),t.closePath();break;case"dash":t.beginPath(),t.moveTo(i,a),t.lineTo(i+n,a),t.closePath()}t.stroke()}}else t.drawImage(e,i-e.width/2,a-e.height/2,e.width,e.height)},clipArea:function(t,e){t.save(),t.beginPath(),t.rect(e.left,e.top,e.right-e.left,e.bottom-e.top),t.clip()},unclipArea:function(t){t.restore()},lineTo:function(t,e,n,i){if(n.steppedLine)return"after"===n.steppedLine&&!i||"after"!==n.steppedLine&&i?t.lineTo(e.x,n.y):t.lineTo(n.x,e.y),void t.lineTo(n.x,n.y);n.tension?t.bezierCurveTo(i?e.controlPointPreviousX:e.controlPointNextX,i?e.controlPointPreviousY:e.controlPointNextY,i?n.controlPointNextX:n.controlPointPreviousX,i?n.controlPointNextY:n.controlPointPreviousY,n.x,n.y):t.lineTo(n.x,n.y)}};i.clear=n.clear,i.drawRoundedRectangle=function(t){t.beginPath(),n.roundedRect.apply(n,arguments),t.closePath()}},{42:42}],42:[function(t,e,n){"use strict";var i={noop:function(){},uid:function(){var t=0;return function(){return t++}}(),isNullOrUndef:function(t){return null===t||void 0===t},isArray:Array.isArray?Array.isArray:function(t){return"[object Array]"===Object.prototype.toString.call(t)},isObject:function(t){return null!==t&&"[object Object]"===Object.prototype.toString.call(t)},valueOrDefault:function(t,e){return void 0===t?e:t},valueAtIndexOrDefault:function(t,e,n){return i.valueOrDefault(i.isArray(t)?t[e]:t,n)},callback:function(t,e,n){if(t&&"function"==typeof t.call)return t.apply(n,e)},each:function(t,e,n,a){var r,o,s;if(i.isArray(t))if(o=t.length,a)for(r=o-1;r>=0;r--)e.call(n,t[r],r);else for(r=0;r=1?t:-(Math.sqrt(1-t*t)-1)},easeOutCirc:function(t){return Math.sqrt(1-(t-=1)*t)},easeInOutCirc:function(t){return(t/=.5)<1?-.5*(Math.sqrt(1-t*t)-1):.5*(Math.sqrt(1-(t-=2)*t)+1)},easeInElastic:function(t){var e=1.70158,n=0,i=1;return 0===t?0:1===t?1:(n||(n=.3),i<1?(i=1,e=n/4):e=n/(2*Math.PI)*Math.asin(1/i),-i*Math.pow(2,10*(t-=1))*Math.sin((t-e)*(2*Math.PI)/n))},easeOutElastic:function(t){var e=1.70158,n=0,i=1;return 0===t?0:1===t?1:(n||(n=.3),i<1?(i=1,e=n/4):e=n/(2*Math.PI)*Math.asin(1/i),i*Math.pow(2,-10*t)*Math.sin((t-e)*(2*Math.PI)/n)+1)},easeInOutElastic:function(t){var e=1.70158,n=0,i=1;return 0===t?0:2==(t/=.5)?1:(n||(n=.45),i<1?(i=1,e=n/4):e=n/(2*Math.PI)*Math.asin(1/i),t<1?i*Math.pow(2,10*(t-=1))*Math.sin((t-e)*(2*Math.PI)/n)*-.5:i*Math.pow(2,-10*(t-=1))*Math.sin((t-e)*(2*Math.PI)/n)*.5+1)},easeInBack:function(t){var e=1.70158;return t*t*((e+1)*t-e)},easeOutBack:function(t){var e=1.70158;return(t-=1)*t*((e+1)*t+e)+1},easeInOutBack:function(t){var e=1.70158;return(t/=.5)<1?t*t*((1+(e*=1.525))*t-e)*.5:.5*((t-=2)*t*((1+(e*=1.525))*t+e)+2)},easeInBounce:function(t){return 1-a.easeOutBounce(1-t)},easeOutBounce:function(t){return t<1/2.75?7.5625*t*t:t<2/2.75?7.5625*(t-=1.5/2.75)*t+.75:t<2.5/2.75?7.5625*(t-=2.25/2.75)*t+.9375:7.5625*(t-=2.625/2.75)*t+.984375},easeInOutBounce:function(t){return t<.5?.5*a.easeInBounce(2*t):.5*a.easeOutBounce(2*t-1)+.5}};e.exports={effects:a},i.easingEffects=a},{42:42}],44:[function(t,e,n){"use strict";var i=t(42);e.exports={toLineHeight:function(t,e){var n=(""+t).match(/^(normal|(\d+(?:\.\d+)?)(px|em|%)?)$/);if(!n||"normal"===n[1])return 1.2*e;switch(t=+n[2],n[3]){case"px":return t;case"%":t/=100}return e*t},toPadding:function(t){var e,n,a,r;return i.isObject(t)?(e=+t.top||0,n=+t.right||0,a=+t.bottom||0,r=+t.left||0):e=n=a=r=+t||0,{top:e,right:n,bottom:a,left:r,height:e+a,width:r+n}},resolve:function(t,e,n){var a,r,o;for(a=0,r=t.length;a
';var a=e.childNodes[0],o=e.childNodes[1];e._reset=function(){a.scrollLeft=1e6,a.scrollTop=1e6,o.scrollLeft=1e6,o.scrollTop=1e6};var s=function(){e._reset(),t()};return r(a,"scroll",s.bind(a,"expand")),r(o,"scroll",s.bind(o,"shrink")),e}function c(t,e){var n=t[v]||(t[v]={}),i=n.renderProxy=function(t){t.animationName===x&&e()};p.each(_,function(e){r(t,e,i)}),n.reflow=!!t.offsetParent,t.classList.add(b)}function h(t){var e=t[v]||{},n=e.renderProxy;n&&(p.each(_,function(e){o(t,e,n)}),delete e.renderProxy),t.classList.remove(b)}function f(t,e,n){var i=t[v]||(t[v]={}),a=i.resizer=d(u(function(){if(i.resizer)return e(s("resize",n))}));c(t,function(){if(i.resizer){var e=t.parentNode;e&&e!==a.parentNode&&e.insertBefore(a,e.firstChild),a._reset()}})}function g(t){var e=t[v]||{},n=e.resizer;delete e.resizer,h(t),n&&n.parentNode&&n.parentNode.removeChild(n)}function m(t,e){var n=t._style||document.createElement("style");t._style||(t._style=n,e="/* Chart.js */\n"+e,n.setAttribute("type","text/css"),document.getElementsByTagName("head")[0].appendChild(n)),n.appendChild(document.createTextNode(e))}var p=t(45),v="$chartjs",y="chartjs-",b=y+"render-monitor",x=y+"render-animation",_=["animationstart","webkitAnimationStart"],k={touchstart:"mousedown",touchmove:"mousemove",touchend:"mouseup",pointerenter:"mouseenter",pointerdown:"mousedown",pointermove:"mousemove",pointerup:"mouseup",pointerleave:"mouseout",pointerout:"mouseout"},w=!!function(){var t=!1;try{var e=Object.defineProperty({},"passive",{get:function(){t=!0}});window.addEventListener("e",null,e)}catch(t){}return t}()&&{passive:!0};e.exports={_enabled:"undefined"!=typeof window&&"undefined"!=typeof document,initialize:function(){var t="from{opacity:0.99}to{opacity:1}";m(this,"@-webkit-keyframes "+x+"{"+t+"}@keyframes "+x+"{"+t+"}."+b+"{-webkit-animation:"+x+" 0.001s;animation:"+x+" 0.001s;}")},acquireContext:function(t,e){"string"==typeof t?t=document.getElementById(t):t.length&&(t=t[0]),t&&t.canvas&&(t=t.canvas);var n=t&&t.getContext&&t.getContext("2d");return n&&n.canvas===t?(a(t,e),n):null},releaseContext:function(t){var e=t.canvas;if(e[v]){var n=e[v].initial;["height","width"].forEach(function(t){var i=n[t];p.isNullOrUndef(i)?e.removeAttribute(t):e.setAttribute(t,i)}),p.each(n.style||{},function(t,n){e.style[n]=t}),e.width=e.width,delete e[v]}},addEventListener:function(t,e,n){var i=t.canvas;if("resize"!==e){var a=n[v]||(n[v]={});r(i,e,(a.proxies||(a.proxies={}))[t.id+"_"+e]=function(e){n(l(e,t))})}else f(i,n,t)},removeEventListener:function(t,e,n){var i=t.canvas;if("resize"!==e){var a=((n[v]||{}).proxies||{})[t.id+"_"+e];a&&o(i,e,a)}else g(i)}},p.addEvent=r,p.removeEvent=o},{45:45}],48:[function(t,e,n){"use strict";var i=t(45),a=t(46),r=t(47),o=r._enabled?r:a;e.exports=i.extend({initialize:function(){},acquireContext:function(){},releaseContext:function(){},addEventListener:function(){},removeEventListener:function(){}},o)},{45:45,46:46,47:47}],49:[function(t,e,n){"use strict";var i=t(25),a=t(40),r=t(45);i._set("global",{plugins:{filler:{propagate:!0}}}),e.exports=function(){function t(t,e,n){var i,a=t._model||{},r=a.fill;if(void 0===r&&(r=!!a.backgroundColor),!1===r||null===r)return!1;if(!0===r)return"origin";if(i=parseFloat(r,10),isFinite(i)&&Math.floor(i)===i)return"-"!==r[0]&&"+"!==r[0]||(i=e+i),!(i===e||i<0||i>=n)&&i;switch(r){case"bottom":return"start";case"top":return"end";case"zero":return"origin";case"origin":case"start":case"end":return r;default:return!1}}function e(t){var e,n=t.el._model||{},i=t.el._scale||{},a=t.fill,r=null;if(isFinite(a))return null;if("start"===a?r=void 0===n.scaleBottom?i.bottom:n.scaleBottom:"end"===a?r=void 0===n.scaleTop?i.top:n.scaleTop:void 0!==n.scaleZero?r=n.scaleZero:i.getBasePosition?r=i.getBasePosition():i.getBasePixel&&(r=i.getBasePixel()),void 0!==r&&null!==r){if(void 0!==r.x&&void 0!==r.y)return r;if("number"==typeof r&&isFinite(r))return e=i.isHorizontal(),{x:e?r:null,y:e?null:r}}return null}function n(t,e,n){var i,a=t[e].fill,r=[e];if(!n)return a;for(;!1!==a&&-1===r.indexOf(a);){if(!isFinite(a))return a;if(!(i=t[a]))return!1;if(i.visible)return a;r.push(a),a=i.fill}return!1}function o(t){var e=t.fill,n="dataset";return!1===e?null:(isFinite(e)||(n="boundary"),d[n](t))}function s(t){return t&&!t.skip}function l(t,e,n,i,a){var o;if(i&&a){for(t.moveTo(e[0].x,e[0].y),o=1;o0;--o)r.canvas.lineTo(t,n[o],n[o-1],!0)}}function u(t,e,n,i,a,r){var o,u,d,c,h,f,g,m=e.length,p=i.spanGaps,v=[],y=[],b=0,x=0;for(t.beginPath(),o=0,u=m+!!r;o');for(var n=0;n'),t.data.datasets[n].label&&e.push(t.data.datasets[n].label),e.push("");return e.push(""),e.join("")}}),e.exports=function(t){function e(t,e){return t.usePointStyle?e*Math.SQRT2:t.boxWidth}function n(e,n){var i=new t.Legend({ctx:e.ctx,options:n,chart:e});o.configure(e,i,n),o.addBox(e,i),e.legend=i}var o=t.layoutService,s=r.noop;return t.Legend=a.extend({initialize:function(t){r.extend(this,t),this.legendHitBoxes=[],this.doughnutMode=!1},beforeUpdate:s,update:function(t,e,n){var i=this;return i.beforeUpdate(),i.maxWidth=t,i.maxHeight=e,i.margins=n,i.beforeSetDimensions(),i.setDimensions(),i.afterSetDimensions(),i.beforeBuildLabels(),i.buildLabels(),i.afterBuildLabels(),i.beforeFit(),i.fit(),i.afterFit(),i.afterUpdate(),i.minSize},afterUpdate:s,beforeSetDimensions:s,setDimensions:function(){var t=this;t.isHorizontal()?(t.width=t.maxWidth,t.left=0,t.right=t.width):(t.height=t.maxHeight,t.top=0,t.bottom=t.height),t.paddingLeft=0,t.paddingTop=0,t.paddingRight=0,t.paddingBottom=0,t.minSize={width:0,height:0}},afterSetDimensions:s,beforeBuildLabels:s,buildLabels:function(){var t=this,e=t.options.labels||{},n=r.callback(e.generateLabels,[t.chart],t)||[];e.filter&&(n=n.filter(function(n){return e.filter(n,t.chart.data)})),t.options.reverse&&n.reverse(),t.legendItems=n},afterBuildLabels:s,beforeFit:s,fit:function(){var t=this,n=t.options,a=n.labels,o=n.display,s=t.ctx,l=i.global,u=r.valueOrDefault,d=u(a.fontSize,l.defaultFontSize),c=u(a.fontStyle,l.defaultFontStyle),h=u(a.fontFamily,l.defaultFontFamily),f=r.fontString(d,c,h),g=t.legendHitBoxes=[],m=t.minSize,p=t.isHorizontal();if(p?(m.width=t.maxWidth,m.height=o?10:0):(m.width=o?10:0,m.height=t.maxHeight),o)if(s.font=f,p){var v=t.lineWidths=[0],y=t.legendItems.length?d+a.padding:0;s.textAlign="left",s.textBaseline="top",r.each(t.legendItems,function(n,i){var r=e(a,d)+d/2+s.measureText(n.text).width;v[v.length-1]+r+a.padding>=t.width&&(y+=d+a.padding,v[v.length]=t.left),g[i]={left:0,top:0,width:r,height:d},v[v.length-1]+=r+a.padding}),m.height+=y}else{var b=a.padding,x=t.columnWidths=[],_=a.padding,k=0,w=0,M=d+b;r.each(t.legendItems,function(t,n){var i=e(a,d)+d/2+s.measureText(t.text).width;w+M>m.height&&(_+=k+a.padding,x.push(k),k=0,w=0),k=Math.max(k,i),w+=M,g[n]={left:0,top:0,width:i,height:d}}),_+=k,x.push(k),m.width+=_}t.width=m.width,t.height=m.height},afterFit:s,isHorizontal:function(){return"top"===this.options.position||"bottom"===this.options.position},draw:function(){var t=this,n=t.options,a=n.labels,o=i.global,s=o.elements.line,l=t.width,u=t.lineWidths;if(n.display){var d,c=t.ctx,h=r.valueOrDefault,f=h(a.fontColor,o.defaultFontColor),g=h(a.fontSize,o.defaultFontSize),m=h(a.fontStyle,o.defaultFontStyle),p=h(a.fontFamily,o.defaultFontFamily),v=r.fontString(g,m,p);c.textAlign="left",c.textBaseline="middle",c.lineWidth=.5,c.strokeStyle=f,c.fillStyle=f,c.font=v;var y=e(a,g),b=t.legendHitBoxes,x=function(t,e,i){if(!(isNaN(y)||y<=0)){c.save(),c.fillStyle=h(i.fillStyle,o.defaultColor),c.lineCap=h(i.lineCap,s.borderCapStyle),c.lineDashOffset=h(i.lineDashOffset,s.borderDashOffset),c.lineJoin=h(i.lineJoin,s.borderJoinStyle),c.lineWidth=h(i.lineWidth,s.borderWidth),c.strokeStyle=h(i.strokeStyle,o.defaultColor);var a=0===h(i.lineWidth,s.borderWidth);if(c.setLineDash&&c.setLineDash(h(i.lineDash,s.borderDash)),n.labels&&n.labels.usePointStyle){var l=g*Math.SQRT2/2,u=l/Math.SQRT2,d=t+u,f=e+u;r.canvas.drawPoint(c,i.pointStyle,l,d,f)}else a||c.strokeRect(t,e,y,g),c.fillRect(t,e,y,g);c.restore()}},_=function(t,e,n,i){var a=g/2,r=y+a+t,o=e+a;c.fillText(n.text,r,o),n.hidden&&(c.beginPath(),c.lineWidth=2,c.moveTo(r,o),c.lineTo(r+i,o),c.stroke())},k=t.isHorizontal();d=k?{x:t.left+(l-u[0])/2,y:t.top+a.padding,line:0}:{x:t.left+a.padding,y:t.top+a.padding,line:0};var w=g+a.padding;r.each(t.legendItems,function(e,n){var i=c.measureText(e.text).width,r=y+g/2+i,o=d.x,s=d.y;k?o+r>=l&&(s=d.y+=w,d.line++,o=d.x=t.left+(l-u[d.line])/2):s+w>t.bottom&&(o=d.x=o+t.columnWidths[d.line]+a.padding,s=d.y=t.top+a.padding,d.line++),x(o,s,e),b[n].left=o,b[n].top=s,_(o,s,e,i),k?d.x+=r+a.padding:d.y+=w})}},handleEvent:function(t){var e=this,n=e.options,i="mouseup"===t.type?"click":t.type,a=!1;if("mousemove"===i){if(!n.onHover)return}else{if("click"!==i)return;if(!n.onClick)return}var r=t.x,o=t.y;if(r>=e.left&&r<=e.right&&o>=e.top&&o<=e.bottom)for(var s=e.legendHitBoxes,l=0;l=u.left&&r<=u.left+u.width&&o>=u.top&&o<=u.top+u.height){if("click"===i){n.onClick.call(e,t.native,e.legendItems[l]),a=!0;break}if("mousemove"===i){n.onHover.call(e,t.native,e.legendItems[l]),a=!0;break}}}return a}}),{id:"legend",beforeInit:function(t){var e=t.options.legend;e&&n(t,e)},beforeUpdate:function(t){var e=t.options.legend,a=t.legend;e?(r.mergeIf(e,i.global.legend),a?(o.configure(t,a,e),a.options=e):n(t,e)):a&&(o.removeBox(t,a),delete t.legend)},afterEvent:function(t,e){var n=t.legend;n&&n.handleEvent(e)}}}},{25:25,26:26,45:45}],51:[function(t,e,n){"use strict";var i=t(25),a=t(26),r=t(45);i._set("global",{title:{display:!1,fontStyle:"bold",fullWidth:!0,lineHeight:1.2,padding:10,position:"top",text:"",weight:2e3}}),e.exports=function(t){function e(e,i){var a=new t.Title({ctx:e.ctx,options:i,chart:e});n.configure(e,a,i),n.addBox(e,a),e.titleBlock=a}var n=t.layoutService,o=r.noop;return t.Title=a.extend({initialize:function(t){var e=this;r.extend(e,t),e.legendHitBoxes=[]},beforeUpdate:o,update:function(t,e,n){var i=this;return i.beforeUpdate(),i.maxWidth=t,i.maxHeight=e,i.margins=n,i.beforeSetDimensions(),i.setDimensions(),i.afterSetDimensions(),i.beforeBuildLabels(),i.buildLabels(),i.afterBuildLabels(),i.beforeFit(),i.fit(),i.afterFit(),i.afterUpdate(),i.minSize},afterUpdate:o,beforeSetDimensions:o,setDimensions:function(){var t=this;t.isHorizontal()?(t.width=t.maxWidth,t.left=0,t.right=t.width):(t.height=t.maxHeight,t.top=0,t.bottom=t.height),t.paddingLeft=0,t.paddingTop=0,t.paddingRight=0,t.paddingBottom=0,t.minSize={width:0,height:0}},afterSetDimensions:o,beforeBuildLabels:o,buildLabels:o,afterBuildLabels:o,beforeFit:o,fit:function(){var t=this,e=r.valueOrDefault,n=t.options,a=n.display,o=e(n.fontSize,i.global.defaultFontSize),s=t.minSize,l=r.isArray(n.text)?n.text.length:1,u=r.options.toLineHeight(n.lineHeight,o),d=a?l*u+2*n.padding:0;t.isHorizontal()?(s.width=t.maxWidth,s.height=d):(s.width=d,s.height=t.maxHeight),t.width=s.width,t.height=s.height},afterFit:o,isHorizontal:function(){var t=this.options.position;return"top"===t||"bottom"===t},draw:function(){var t=this,e=t.ctx,n=r.valueOrDefault,a=t.options,o=i.global;if(a.display){var s,l,u,d=n(a.fontSize,o.defaultFontSize),c=n(a.fontStyle,o.defaultFontStyle),h=n(a.fontFamily,o.defaultFontFamily),f=r.fontString(d,c,h),g=r.options.toLineHeight(a.lineHeight,d),m=g/2+a.padding,p=0,v=t.top,y=t.left,b=t.bottom,x=t.right;e.fillStyle=n(a.fontColor,o.defaultFontColor),e.font=f,t.isHorizontal()?(l=y+(x-y)/2,u=v+m,s=x-y):(l="left"===a.position?y+m:x-m,u=v+(b-v)/2,s=b-v,p=Math.PI*("left"===a.position?-.5:.5)),e.save(),e.translate(l,u),e.rotate(p),e.textAlign="center",e.textBaseline="middle";var _=a.text;if(r.isArray(_))for(var k=0,w=0;w<_.length;++w)e.fillText(_[w],0,k,s),k+=g;else e.fillText(_,0,0,s);e.restore()}}}),{id:"title",beforeInit:function(t){var n=t.options.title;n&&e(t,n)},beforeUpdate:function(a){var o=a.options.title,s=a.titleBlock;o?(r.mergeIf(o,i.global.title),s?(n.configure(a,s,o),s.options=o):e(a,o)):s&&(t.layoutService.removeBox(a,s),delete a.titleBlock)}}}},{25:25,26:26,45:45}],52:[function(t,e,n){"use strict";e.exports=function(t){var e=t.Scale.extend({getLabels:function(){var t=this.chart.data;return this.options.labels||(this.isHorizontal()?t.xLabels:t.yLabels)||t.labels},determineDataLimits:function(){var t=this,e=t.getLabels();t.minIndex=0,t.maxIndex=e.length-1;var n;void 0!==t.options.ticks.min&&(n=e.indexOf(t.options.ticks.min),t.minIndex=-1!==n?n:t.minIndex),void 0!==t.options.ticks.max&&(n=e.indexOf(t.options.ticks.max),t.maxIndex=-1!==n?n:t.maxIndex),t.min=e[t.minIndex],t.max=e[t.maxIndex]},buildTicks:function(){var t=this,e=t.getLabels();t.ticks=0===t.minIndex&&t.maxIndex===e.length-1?e:e.slice(t.minIndex,t.maxIndex+1)},getLabelForIndex:function(t,e){var n=this,i=n.chart.data,a=n.isHorizontal();return i.yLabels&&!a?n.getRightValue(i.datasets[e].data[t]):n.ticks[t-n.minIndex]},getPixelForValue:function(t,e){var n,i=this,a=i.options.offset,r=Math.max(i.maxIndex+1-i.minIndex-(a?0:1),1);if(void 0!==t&&null!==t&&(n=i.isHorizontal()?t.x:t.y),void 0!==n||void 0!==t&&isNaN(e)){var o=i.getLabels();t=n||t;var s=o.indexOf(t);e=-1!==s?s:e}if(i.isHorizontal()){var l=i.width/r,u=l*(e-i.minIndex);return a&&(u+=l/2),i.left+Math.round(u)}var d=i.height/r,c=d*(e-i.minIndex);return a&&(c+=d/2),i.top+Math.round(c)},getPixelForTick:function(t){return this.getPixelForValue(this.ticks[t],t+this.minIndex,null)},getValueForPixel:function(t){var e=this,n=e.options.offset,i=Math.max(e._ticks.length-(n?0:1),1),a=e.isHorizontal(),r=(a?e.width:e.height)/i;return t-=a?e.left:e.top,n&&(t-=r/2),(t<=0?0:Math.round(t/r))+e.minIndex},getBasePixel:function(){return this.bottom}});t.scaleService.registerScaleType("category",e,{position:"bottom"})}},{}],53:[function(t,e,n){"use strict";var i=t(25),a=t(45),r=t(34);e.exports=function(t){var e={position:"left",ticks:{callback:r.formatters.linear}},n=t.LinearScaleBase.extend({determineDataLimits:function(){function t(t){return o?t.xAxisID===e.id:t.yAxisID===e.id}var e=this,n=e.options,i=e.chart,r=i.data.datasets,o=e.isHorizontal();e.min=null,e.max=null;var s=n.stacked;if(void 0===s&&a.each(r,function(e,n){if(!s){var a=i.getDatasetMeta(n);i.isDatasetVisible(n)&&t(a)&&void 0!==a.stack&&(s=!0)}}),n.stacked||s){var l={};a.each(r,function(r,o){var s=i.getDatasetMeta(o),u=[s.type,void 0===n.stacked&&void 0===s.stack?o:"",s.stack].join(".");void 0===l[u]&&(l[u]={positiveValues:[],negativeValues:[]});var d=l[u].positiveValues,c=l[u].negativeValues;i.isDatasetVisible(o)&&t(s)&&a.each(r.data,function(t,i){var a=+e.getRightValue(t);isNaN(a)||s.data[i].hidden||(d[i]=d[i]||0,c[i]=c[i]||0,n.relativePoints?d[i]=100:a<0?c[i]+=a:d[i]+=a)})}),a.each(l,function(t){var n=t.positiveValues.concat(t.negativeValues),i=a.min(n),r=a.max(n);e.min=null===e.min?i:Math.min(e.min,i),e.max=null===e.max?r:Math.max(e.max,r)})}else a.each(r,function(n,r){var o=i.getDatasetMeta(r);i.isDatasetVisible(r)&&t(o)&&a.each(n.data,function(t,n){var i=+e.getRightValue(t);isNaN(i)||o.data[n].hidden||(null===e.min?e.min=i:ie.max&&(e.max=i))})});e.min=isFinite(e.min)&&!isNaN(e.min)?e.min:0,e.max=isFinite(e.max)&&!isNaN(e.max)?e.max:1,this.handleTickRangeOptions()},getTickLimit:function(){var t,e=this,n=e.options.ticks;if(e.isHorizontal())t=Math.min(n.maxTicksLimit?n.maxTicksLimit:11,Math.ceil(e.width/50));else{var r=a.valueOrDefault(n.fontSize,i.global.defaultFontSize);t=Math.min(n.maxTicksLimit?n.maxTicksLimit:11,Math.ceil(e.height/(2*r)))}return t},handleDirectionalChanges:function(){this.isHorizontal()||this.ticks.reverse()},getLabelForIndex:function(t,e){return+this.getRightValue(this.chart.data.datasets[e].data[t])},getPixelForValue:function(t){var e,n=this,i=n.start,a=+n.getRightValue(t),r=n.end-i;return n.isHorizontal()?(e=n.left+n.width/r*(a-i),Math.round(e)):(e=n.bottom-n.height/r*(a-i),Math.round(e))},getValueForPixel:function(t){var e=this,n=e.isHorizontal(),i=n?e.width:e.height,a=(n?t-e.left:e.bottom-t)/i;return e.start+(e.end-e.start)*a},getPixelForTick:function(t){return this.getPixelForValue(this.ticksAsNumbers[t])}});t.scaleService.registerScaleType("linear",n,e)}},{25:25,34:34,45:45}],54:[function(t,e,n){"use strict";var i=t(45),a=t(34);e.exports=function(t){var e=i.noop;t.LinearScaleBase=t.Scale.extend({getRightValue:function(e){return"string"==typeof e?+e:t.Scale.prototype.getRightValue.call(this,e)},handleTickRangeOptions:function(){var t=this,e=t.options.ticks;if(e.beginAtZero){var n=i.sign(t.min),a=i.sign(t.max);n<0&&a<0?t.max=0:n>0&&a>0&&(t.min=0)}var r=void 0!==e.min||void 0!==e.suggestedMin,o=void 0!==e.max||void 0!==e.suggestedMax;void 0!==e.min?t.min=e.min:void 0!==e.suggestedMin&&(null===t.min?t.min=e.suggestedMin:t.min=Math.min(t.min,e.suggestedMin)),void 0!==e.max?t.max=e.max:void 0!==e.suggestedMax&&(null===t.max?t.max=e.suggestedMax:t.max=Math.max(t.max,e.suggestedMax)),r!==o&&t.min>=t.max&&(r?t.max=t.min+1:t.min=t.max-1),t.min===t.max&&(t.max++,e.beginAtZero||t.min--)},getTickLimit:e,handleDirectionalChanges:e,buildTicks:function(){var t=this,e=t.options.ticks,n=t.getTickLimit(),r={maxTicks:n=Math.max(2,n),min:e.min,max:e.max,stepSize:i.valueOrDefault(e.fixedStepSize,e.stepSize)},o=t.ticks=a.generators.linear(r,t);t.handleDirectionalChanges(),t.max=i.max(o),t.min=i.min(o),e.reverse?(o.reverse(),t.start=t.max,t.end=t.min):(t.start=t.min,t.end=t.max)},convertTicksToLabels:function(){var e=this;e.ticksAsNumbers=e.ticks.slice(),e.zeroLineIndex=e.ticks.indexOf(0),t.Scale.prototype.convertTicksToLabels.call(e)}})}},{34:34,45:45}],55:[function(t,e,n){"use strict";var i=t(45),a=t(34);e.exports=function(t){var e={position:"left",ticks:{callback:a.formatters.logarithmic}},n=t.Scale.extend({determineDataLimits:function(){function t(t){return l?t.xAxisID===e.id:t.yAxisID===e.id}var e=this,n=e.options,a=n.ticks,r=e.chart,o=r.data.datasets,s=i.valueOrDefault,l=e.isHorizontal();e.min=null,e.max=null,e.minNotZero=null;var u=n.stacked;if(void 0===u&&i.each(o,function(e,n){if(!u){var i=r.getDatasetMeta(n);r.isDatasetVisible(n)&&t(i)&&void 0!==i.stack&&(u=!0)}}),n.stacked||u){var d={};i.each(o,function(a,o){var s=r.getDatasetMeta(o),l=[s.type,void 0===n.stacked&&void 0===s.stack?o:"",s.stack].join(".");r.isDatasetVisible(o)&&t(s)&&(void 0===d[l]&&(d[l]=[]),i.each(a.data,function(t,i){var a=d[l],r=+e.getRightValue(t);isNaN(r)||s.data[i].hidden||(a[i]=a[i]||0,n.relativePoints?a[i]=100:a[i]+=r)}))}),i.each(d,function(t){var n=i.min(t),a=i.max(t);e.min=null===e.min?n:Math.min(e.min,n),e.max=null===e.max?a:Math.max(e.max,a)})}else i.each(o,function(n,a){var o=r.getDatasetMeta(a);r.isDatasetVisible(a)&&t(o)&&i.each(n.data,function(t,n){var i=+e.getRightValue(t);isNaN(i)||o.data[n].hidden||(null===e.min?e.min=i:ie.max&&(e.max=i),0!==i&&(null===e.minNotZero||ia?{start:e-n-5,end:e}:{start:e,end:e+n+5}}function l(t){var i,r,l,u=n(t),d=Math.min(t.height/2,t.width/2),c={r:t.width,l:0,t:t.height,b:0},h={};t.ctx.font=u.font,t._pointLabelSizes=[];var f=e(t);for(i=0;ic.r&&(c.r=p.end,h.r=g),v.startc.b&&(c.b=v.end,h.b=g)}t.setReductions(d,c,h)}function u(t){var e=Math.min(t.height/2,t.width/2);t.drawingArea=Math.round(e),t.setCenterPoint(0,0,0,0)}function d(t){return 0===t||180===t?"center":t<180?"left":"right"}function c(t,e,n,i){if(a.isArray(e))for(var r=n.y,o=1.5*i,s=0;s270||t<90)&&(n.y-=e.h)}function f(t){var i=t.ctx,r=a.valueOrDefault,o=t.options,s=o.angleLines,l=o.pointLabels;i.lineWidth=s.lineWidth,i.strokeStyle=s.color;var u=t.getDistanceFromCenterForValue(o.ticks.reverse?t.min:t.max),f=n(t);i.textBaseline="top";for(var g=e(t)-1;g>=0;g--){if(s.display){var m=t.getPointPosition(g,u);i.beginPath(),i.moveTo(t.xCenter,t.yCenter),i.lineTo(m.x,m.y),i.stroke(),i.closePath()}if(l.display){var v=t.getPointPosition(g,u+5),y=r(l.fontColor,p.defaultFontColor);i.font=f.font,i.fillStyle=y;var b=t.getIndexAngle(g),x=a.toDegrees(b);i.textAlign=d(x),h(x,t._pointLabelSizes[g],v),c(i,t.pointLabels[g]||"",v,f.size)}}}function g(t,n,i,r){var o=t.ctx;if(o.strokeStyle=a.valueAtIndexOrDefault(n.color,r-1),o.lineWidth=a.valueAtIndexOrDefault(n.lineWidth,r-1),t.options.gridLines.circular)o.beginPath(),o.arc(t.xCenter,t.yCenter,i,0,2*Math.PI),o.closePath(),o.stroke();else{var s=e(t);if(0===s)return;o.beginPath();var l=t.getPointPosition(0,i);o.moveTo(l.x,l.y);for(var u=1;u0&&n>0?e:0)},draw:function(){var t=this,e=t.options,n=e.gridLines,i=e.ticks,r=a.valueOrDefault;if(e.display){var o=t.ctx,s=this.getIndexAngle(0),l=r(i.fontSize,p.defaultFontSize),u=r(i.fontStyle,p.defaultFontStyle),d=r(i.fontFamily,p.defaultFontFamily),c=a.fontString(l,u,d);a.each(t.ticks,function(e,a){if(a>0||i.reverse){var u=t.getDistanceFromCenterForValue(t.ticksAsNumbers[a]);if(n.display&&0!==a&&g(t,n,u,a),i.display){var d=r(i.fontColor,p.defaultFontColor);if(o.font=c,o.save(),o.translate(t.xCenter,t.yCenter),o.rotate(s),i.showLabelBackdrop){var h=o.measureText(e).width;o.fillStyle=i.backdropColor,o.fillRect(-h/2-i.backdropPaddingX,-u-l/2-i.backdropPaddingY,h+2*i.backdropPaddingX,l+2*i.backdropPaddingY)}o.textAlign="center",o.textBaseline="middle",o.fillStyle=d,o.fillText(e,0,-u),o.restore()}}}),(e.angleLines.display||e.pointLabels.display)&&f(t)}}});t.scaleService.registerScaleType("radialLinear",y,v)}},{25:25,34:34,45:45}],57:[function(t,e,n){"use strict";function i(t,e){return t-e}function a(t){var e,n,i,a={},r=[];for(e=0,n=t.length;ee&&s=0&&o<=s;){if(i=o+s>>1,a=t[i-1]||null,r=t[i],!a)return{lo:null,hi:r};if(r[e]n))return{lo:a,hi:r};s=i-1}}return{lo:r,hi:null}}function s(t,e,n,i){var a=o(t,e,n),r=a.lo?a.hi?a.lo:t[t.length-2]:t[0],s=a.lo?a.hi?a.hi:t[t.length-1]:t[1],l=s[e]-r[e],u=l?(n-r[e])/l:0,d=(s[i]-r[i])*u;return r[i]+d}function l(t,e){var n=e.parser,i=e.parser||e.format;return"function"==typeof n?n(t):"string"==typeof t&&"string"==typeof i?v(t,i):(t instanceof v||(t=v(t)),t.isValid()?t:"function"==typeof i?i(t):t)}function u(t,e){if(b.isNullOrUndef(t))return null;var n=e.options.time,i=l(e.getRightValue(t),n);return i.isValid()?(n.round&&i.startOf(n.round),i.valueOf()):null}function d(t,e,n,i){var a,r,o,s=e-t,l=k[n],u=l.size,d=l.steps;if(!d)return Math.ceil(s/((i||1)*u));for(a=0,r=d.length;a=w.indexOf(e);a--)if(r=w[a],k[r].common&&o.as(r)>=t.length)return r;return w[e?w.indexOf(e):0]}function f(t){for(var e=w.indexOf(t)+1,n=w.length;e1?e[1]:i,o=e[0],l=(s(t,"time",r,"pos")-s(t,"time",o,"pos"))/2),a.time.max||(r=e[e.length-1],o=e.length>1?e[e.length-2]:n,u=(s(t,"time",r,"pos")-s(t,"time",o,"pos"))/2)),{left:l,right:u}}function p(t,e){var n,i,a,r,o=[];for(n=0,i=t.length;n=a&&n<=o&&c.push(n);return i.min=a,i.max=o,i._unit=l.unit||h(c,l.minUnit,i.min,i.max),i._majorUnit=f(i._unit),i._table=r(i._timestamps.data,a,o,s.distribution),i._offsets=m(i._table,c,a,o,s),p(c,i._majorUnit)},getLabelForIndex:function(t,e){var n=this,i=n.chart.data,a=n.options.time,r=i.labels&&t=0&&tn?(e+.05)/(n+.05):(n+.05)/(e+.05)},level:function(t){var e=this.contrast(t);return e>=7.1?"AAA":e>=4.5?"AA":""},dark:function(){var t=this.values.rgb;return(299*t[0]+587*t[1]+114*t[2])/1e3<128},light:function(){return!this.dark()},negate:function(){for(var t=[],e=0;e<3;e++)t[e]=255-this.values.rgb[e];return this.setValues("rgb",t),this},lighten:function(t){var e=this.values.hsl;return e[2]+=e[2]*t,this.setValues("hsl",e),this},darken:function(t){var e=this.values.hsl;return e[2]-=e[2]*t,this.setValues("hsl",e),this},saturate:function(t){var e=this.values.hsl;return e[1]+=e[1]*t,this.setValues("hsl",e),this},desaturate:function(t){var e=this.values.hsl;return e[1]-=e[1]*t,this.setValues("hsl",e),this},whiten:function(t){var e=this.values.hwb;return e[1]+=e[1]*t,this.setValues("hwb",e),this},blacken:function(t){var e=this.values.hwb;return e[2]+=e[2]*t,this.setValues("hwb",e),this},greyscale:function(){var t=this.values.rgb,e=.3*t[0]+.59*t[1]+.11*t[2];return this.setValues("rgb",[e,e,e]),this},clearer:function(t){var e=this.values.alpha;return this.setValues("alpha",e-e*t),this},opaquer:function(t){var e=this.values.alpha;return this.setValues("alpha",e+e*t),this},rotate:function(t){var e=this.values.hsl,n=(e[0]+t)%360;return e[0]=n<0?360+n:n,this.setValues("hsl",e),this},mix:function(t,e){var n=this,i=t,a=void 0===e?.5:e,r=2*a-1,o=n.alpha()-i.alpha(),s=((r*o==-1?r:(r+o)/(1+r*o))+1)/2,l=1-s;return this.rgb(s*n.red()+l*i.red(),s*n.green()+l*i.green(),s*n.blue()+l*i.blue()).alpha(n.alpha()*a+i.alpha()*(1-a))},toJSON:function(){return this.rgb()},clone:function(){var t,e,n=new r,i=this.values,a=n.values;for(var o in i)i.hasOwnProperty(o)&&(t=i[o],"[object Array]"===(e={}.toString.call(t))?a[o]=t.slice(0):"[object Number]"===e?a[o]=t:console.error("unexpected color value:",t));return n}},r.prototype.spaces={rgb:["red","green","blue"],hsl:["hue","saturation","lightness"],hsv:["hue","saturation","value"],hwb:["hue","whiteness","blackness"],cmyk:["cyan","magenta","yellow","black"]},r.prototype.maxes={rgb:[255,255,255],hsl:[360,100,100],hsv:[360,100,100],hwb:[360,100,100],cmyk:[100,100,100,100]},r.prototype.getValues=function(t){for(var e=this.values,n={},i=0;i.04045?Math.pow((e+.055)/1.055,2.4):e/12.92)+.3576*(n=n>.04045?Math.pow((n+.055)/1.055,2.4):n/12.92)+.1805*(i=i>.04045?Math.pow((i+.055)/1.055,2.4):i/12.92)),100*(.2126*e+.7152*n+.0722*i),100*(.0193*e+.1192*n+.9505*i)]}function d(t){var e,n,i,a=u(t),r=a[0],o=a[1],s=a[2];return r/=95.047,o/=100,s/=108.883,r=r>.008856?Math.pow(r,1/3):7.787*r+16/116,o=o>.008856?Math.pow(o,1/3):7.787*o+16/116,s=s>.008856?Math.pow(s,1/3):7.787*s+16/116,e=116*o-16,n=500*(r-o),i=200*(o-s),[e,n,i]}function c(t){var e,n,i,a,r,o=t[0]/360,s=t[1]/100,l=t[2]/100;if(0==s)return r=255*l,[r,r,r];e=2*l-(n=l<.5?l*(1+s):l+s-l*s),a=[0,0,0];for(var u=0;u<3;u++)(i=o+1/3*-(u-1))<0&&i++,i>1&&i--,r=6*i<1?e+6*(n-e)*i:2*i<1?n:3*i<2?e+(n-e)*(2/3-i)*6:e,a[u]=255*r;return a}function h(t){var e=t[0]/60,n=t[1]/100,i=t[2]/100,a=Math.floor(e)%6,r=e-Math.floor(e),o=255*i*(1-n),s=255*i*(1-n*r),l=255*i*(1-n*(1-r)),i=255*i;switch(a){case 0:return[i,l,o];case 1:return[s,i,o];case 2:return[o,i,l];case 3:return[o,s,i];case 4:return[l,o,i];case 5:return[i,o,s]}}function f(t){var e,n,i,a,o=t[0]/360,s=t[1]/100,l=t[2]/100,u=s+l;switch(u>1&&(s/=u,l/=u),e=Math.floor(6*o),n=1-l,i=6*o-e,0!=(1&e)&&(i=1-i),a=s+i*(n-s),e){default:case 6:case 0:r=n,g=a,b=s;break;case 1:r=a,g=n,b=s;break;case 2:r=s,g=n,b=a;break;case 3:r=s,g=a,b=n;break;case 4:r=a,g=s,b=n;break;case 5:r=n,g=s,b=a}return[255*r,255*g,255*b]}function m(t){var e,n,i,a=t[0]/100,r=t[1]/100,o=t[2]/100,s=t[3]/100;return e=1-Math.min(1,a*(1-s)+s),n=1-Math.min(1,r*(1-s)+s),i=1-Math.min(1,o*(1-s)+s),[255*e,255*n,255*i]}function p(t){var e,n,i,a=t[0]/100,r=t[1]/100,o=t[2]/100;return e=3.2406*a+-1.5372*r+-.4986*o,n=-.9689*a+1.8758*r+.0415*o,i=.0557*a+-.204*r+1.057*o,e=e>.0031308?1.055*Math.pow(e,1/2.4)-.055:e*=12.92,n=n>.0031308?1.055*Math.pow(n,1/2.4)-.055:n*=12.92,i=i>.0031308?1.055*Math.pow(i,1/2.4)-.055:i*=12.92,e=Math.min(Math.max(0,e),1),n=Math.min(Math.max(0,n),1),i=Math.min(Math.max(0,i),1),[255*e,255*n,255*i]}function v(t){var e,n,i,a=t[0],r=t[1],o=t[2];return a/=95.047,r/=100,o/=108.883,a=a>.008856?Math.pow(a,1/3):7.787*a+16/116,r=r>.008856?Math.pow(r,1/3):7.787*r+16/116,o=o>.008856?Math.pow(o,1/3):7.787*o+16/116,e=116*r-16,n=500*(a-r),i=200*(r-o),[e,n,i]}function y(t){var e,n,i,a,r=t[0],o=t[1],s=t[2];return r<=8?a=(n=100*r/903.3)/100*7.787+16/116:(n=100*Math.pow((r+16)/116,3),a=Math.pow(n/100,1/3)),e=e/95.047<=.008856?e=95.047*(o/500+a-16/116)/7.787:95.047*Math.pow(o/500+a,3),i=i/108.883<=.008859?i=108.883*(a-s/200-16/116)/7.787:108.883*Math.pow(a-s/200,3),[e,n,i]}function x(t){var e,n,i,a=t[0],r=t[1],o=t[2];return e=Math.atan2(o,r),(n=360*e/2/Math.PI)<0&&(n+=360),i=Math.sqrt(r*r+o*o),[a,i,n]}function _(t){return p(y(t))}function k(t){var e,n,i,a=t[0],r=t[1];return i=t[2]/360*2*Math.PI,e=r*Math.cos(i),n=r*Math.sin(i),[a,e,n]}function w(t){return M[t]}e.exports={rgb2hsl:i,rgb2hsv:a,rgb2hwb:o,rgb2cmyk:s,rgb2keyword:l,rgb2xyz:u,rgb2lab:d,rgb2lch:function(t){return x(d(t))},hsl2rgb:c,hsl2hsv:function(t){var e,n,i=t[0],a=t[1]/100,r=t[2]/100;return 0===r?[0,0,0]:(r*=2,a*=r<=1?r:2-r,n=(r+a)/2,e=2*a/(r+a),[i,100*e,100*n])},hsl2hwb:function(t){return o(c(t))},hsl2cmyk:function(t){return s(c(t))},hsl2keyword:function(t){return l(c(t))},hsv2rgb:h,hsv2hsl:function(t){var e,n,i=t[0],a=t[1]/100,r=t[2]/100;return n=(2-a)*r,e=a*r,e/=n<=1?n:2-n,e=e||0,n/=2,[i,100*e,100*n]},hsv2hwb:function(t){return o(h(t))},hsv2cmyk:function(t){return s(h(t))},hsv2keyword:function(t){return l(h(t))},hwb2rgb:f,hwb2hsl:function(t){return i(f(t))},hwb2hsv:function(t){return a(f(t))},hwb2cmyk:function(t){return s(f(t))},hwb2keyword:function(t){return l(f(t))},cmyk2rgb:m,cmyk2hsl:function(t){return i(m(t))},cmyk2hsv:function(t){return a(m(t))},cmyk2hwb:function(t){return o(m(t))},cmyk2keyword:function(t){return l(m(t))},keyword2rgb:w,keyword2hsl:function(t){return i(w(t))},keyword2hsv:function(t){return a(w(t))},keyword2hwb:function(t){return o(w(t))},keyword2cmyk:function(t){return s(w(t))},keyword2lab:function(t){return d(w(t))},keyword2xyz:function(t){return u(w(t))},xyz2rgb:p,xyz2lab:v,xyz2lch:function(t){return x(v(t))},lab2xyz:y,lab2rgb:_,lab2lch:x,lch2lab:k,lch2xyz:function(t){return y(k(t))},lch2rgb:function(t){return _(k(t))}};var M={aliceblue:[240,248,255],antiquewhite:[250,235,215],aqua:[0,255,255],aquamarine:[127,255,212],azure:[240,255,255],beige:[245,245,220],bisque:[255,228,196],black:[0,0,0],blanchedalmond:[255,235,205],blue:[0,0,255],blueviolet:[138,43,226],brown:[165,42,42],burlywood:[222,184,135],cadetblue:[95,158,160],chartreuse:[127,255,0],chocolate:[210,105,30],coral:[255,127,80],cornflowerblue:[100,149,237],cornsilk:[255,248,220],crimson:[220,20,60],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgoldenrod:[184,134,11],darkgray:[169,169,169],darkgreen:[0,100,0],darkgrey:[169,169,169],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkseagreen:[143,188,143],darkslateblue:[72,61,139],darkslategray:[47,79,79],darkslategrey:[47,79,79],darkturquoise:[0,206,209],darkviolet:[148,0,211],deeppink:[255,20,147],deepskyblue:[0,191,255],dimgray:[105,105,105],dimgrey:[105,105,105],dodgerblue:[30,144,255],firebrick:[178,34,34],floralwhite:[255,250,240],forestgreen:[34,139,34],fuchsia:[255,0,255],gainsboro:[220,220,220],ghostwhite:[248,248,255],gold:[255,215,0],goldenrod:[218,165,32],gray:[128,128,128],green:[0,128,0],greenyellow:[173,255,47],grey:[128,128,128],honeydew:[240,255,240],hotpink:[255,105,180],indianred:[205,92,92],indigo:[75,0,130],ivory:[255,255,240],khaki:[240,230,140],lavender:[230,230,250],lavenderblush:[255,240,245],lawngreen:[124,252,0],lemonchiffon:[255,250,205],lightblue:[173,216,230],lightcoral:[240,128,128],lightcyan:[224,255,255],lightgoldenrodyellow:[250,250,210],lightgray:[211,211,211],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightsalmon:[255,160,122],lightseagreen:[32,178,170],lightskyblue:[135,206,250],lightslategray:[119,136,153],lightslategrey:[119,136,153],lightsteelblue:[176,196,222],lightyellow:[255,255,224],lime:[0,255,0],limegreen:[50,205,50],linen:[250,240,230],magenta:[255,0,255],maroon:[128,0,0],mediumaquamarine:[102,205,170],mediumblue:[0,0,205],mediumorchid:[186,85,211],mediumpurple:[147,112,219],mediumseagreen:[60,179,113],mediumslateblue:[123,104,238],mediumspringgreen:[0,250,154],mediumturquoise:[72,209,204],mediumvioletred:[199,21,133],midnightblue:[25,25,112],mintcream:[245,255,250],mistyrose:[255,228,225],moccasin:[255,228,181],navajowhite:[255,222,173],navy:[0,0,128],oldlace:[253,245,230],olive:[128,128,0],olivedrab:[107,142,35],orange:[255,165,0],orangered:[255,69,0],orchid:[218,112,214],palegoldenrod:[238,232,170],palegreen:[152,251,152],paleturquoise:[175,238,238],palevioletred:[219,112,147],papayawhip:[255,239,213],peachpuff:[255,218,185],peru:[205,133,63],pink:[255,192,203],plum:[221,160,221],powderblue:[176,224,230],purple:[128,0,128],rebeccapurple:[102,51,153],red:[255,0,0],rosybrown:[188,143,143],royalblue:[65,105,225],saddlebrown:[139,69,19],salmon:[250,128,114],sandybrown:[244,164,96],seagreen:[46,139,87],seashell:[255,245,238],sienna:[160,82,45],silver:[192,192,192],skyblue:[135,206,235],slateblue:[106,90,205],slategray:[112,128,144],slategrey:[112,128,144],snow:[255,250,250],springgreen:[0,255,127],steelblue:[70,130,180],tan:[210,180,140],teal:[0,128,128],thistle:[216,191,216],tomato:[255,99,71],turquoise:[64,224,208],violet:[238,130,238],wheat:[245,222,179],white:[255,255,255],whitesmoke:[245,245,245],yellow:[255,255,0],yellowgreen:[154,205,50]},S={};for(var D in M)S[JSON.stringify(M[D])]=D},{}],4:[function(t,e,n){var i=t(3),a=function(){return new u};for(var r in i){a[r+"Raw"]=function(t){return function(e){return"number"==typeof e&&(e=Array.prototype.slice.call(arguments)),i[t](e)}}(r);var o=/(\w+)2(\w+)/.exec(r),s=o[1],l=o[2];(a[s]=a[s]||{})[l]=a[r]=function(t){return function(e){"number"==typeof e&&(e=Array.prototype.slice.call(arguments));var n=i[t](e);if("string"==typeof n||void 0===n)return n;for(var a=0;a0)for(n=0;n=0?n?"+":"":"-")+Math.pow(10,Math.max(0,a)).toString().substr(1)+i}function N(t,e,n,i){var a=i;"string"==typeof i&&(a=function(){return this[i]()}),t&&(Re[t]=a),e&&(Re[e[0]]=function(){return Y(a.apply(this,arguments),e[1],e[2])}),n&&(Re[n]=function(){return this.localeData().ordinal(a.apply(this,arguments),t)})}function z(t){return t.match(/\[[\s\S]/)?t.replace(/^\[|\]$/g,""):t.replace(/\\/g,"")}function B(t){var e,n,i=t.match(Ie);for(e=0,n=i.length;e=0&&Oe.test(t);)t=t.replace(Oe,function(t){return e.longDateFormat(t)||t}),Oe.lastIndex=0,n-=1;return t}function E(t,e,n){Ke[t]=D(e)?e:function(t,i){return t&&n?n:e}}function j(t,e){return d(Ke,t)?Ke[t](e._strict,e._locale):new RegExp(U(t))}function U(t){return q(t.replace("\\","").replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g,function(t,e,n,i,a){return e||n||i||a}))}function q(t){return t.replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&")}function G(t,e){var n,i=e;for("string"==typeof t&&(t=[t]),s(e)&&(i=function(t,n){n[e]=_(t)}),n=0;n=0&&isFinite(s.getFullYear())&&s.setFullYear(t),s}function at(t){var e=new Date(Date.UTC.apply(null,arguments));return t<100&&t>=0&&isFinite(e.getUTCFullYear())&&e.setUTCFullYear(t),e}function rt(t,e,n){var i=7+e-n;return-((7+at(t,0,i).getUTCDay()-e)%7)+i-1}function ot(t,e,n,i,a){var r,o,s=1+7*(e-1)+(7+n-i)%7+rt(t,i,a);return s<=0?o=et(r=t-1)+s:s>et(t)?(r=t+1,o=s-et(t)):(r=t,o=s),{year:r,dayOfYear:o}}function st(t,e,n){var i,a,r=rt(t.year(),e,n),o=Math.floor((t.dayOfYear()-r-1)/7)+1;return o<1?i=o+lt(a=t.year()-1,e,n):o>lt(t.year(),e,n)?(i=o-lt(t.year(),e,n),a=t.year()+1):(a=t.year(),i=o),{week:i,year:a}}function lt(t,e,n){var i=rt(t,e,n),a=rt(t+1,e,n);return(et(t)-i+a)/7}function ut(t,e){return"string"!=typeof t?t:isNaN(t)?"number"==typeof(t=e.weekdaysParse(t))?t:null:parseInt(t,10)}function dt(t,e){return"string"==typeof t?e.weekdaysParse(t)%7||7:isNaN(t)?null:t}function ct(t,e,n){var i,a,r,o=t.toLocaleLowerCase();if(!this._weekdaysParse)for(this._weekdaysParse=[],this._shortWeekdaysParse=[],this._minWeekdaysParse=[],i=0;i<7;++i)r=h([2e3,1]).day(i),this._minWeekdaysParse[i]=this.weekdaysMin(r,"").toLocaleLowerCase(),this._shortWeekdaysParse[i]=this.weekdaysShort(r,"").toLocaleLowerCase(),this._weekdaysParse[i]=this.weekdays(r,"").toLocaleLowerCase();return n?"dddd"===e?-1!==(a=un.call(this._weekdaysParse,o))?a:null:"ddd"===e?-1!==(a=un.call(this._shortWeekdaysParse,o))?a:null:-1!==(a=un.call(this._minWeekdaysParse,o))?a:null:"dddd"===e?-1!==(a=un.call(this._weekdaysParse,o))?a:-1!==(a=un.call(this._shortWeekdaysParse,o))?a:-1!==(a=un.call(this._minWeekdaysParse,o))?a:null:"ddd"===e?-1!==(a=un.call(this._shortWeekdaysParse,o))?a:-1!==(a=un.call(this._weekdaysParse,o))?a:-1!==(a=un.call(this._minWeekdaysParse,o))?a:null:-1!==(a=un.call(this._minWeekdaysParse,o))?a:-1!==(a=un.call(this._weekdaysParse,o))?a:-1!==(a=un.call(this._shortWeekdaysParse,o))?a:null}function ht(){function t(t,e){return e.length-t.length}var e,n,i,a,r,o=[],s=[],l=[],u=[];for(e=0;e<7;e++)n=h([2e3,1]).day(e),i=this.weekdaysMin(n,""),a=this.weekdaysShort(n,""),r=this.weekdays(n,""),o.push(i),s.push(a),l.push(r),u.push(i),u.push(a),u.push(r);for(o.sort(t),s.sort(t),l.sort(t),u.sort(t),e=0;e<7;e++)s[e]=q(s[e]),l[e]=q(l[e]),u[e]=q(u[e]);this._weekdaysRegex=new RegExp("^("+u.join("|")+")","i"),this._weekdaysShortRegex=this._weekdaysRegex,this._weekdaysMinRegex=this._weekdaysRegex,this._weekdaysStrictRegex=new RegExp("^("+l.join("|")+")","i"),this._weekdaysShortStrictRegex=new RegExp("^("+s.join("|")+")","i"),this._weekdaysMinStrictRegex=new RegExp("^("+o.join("|")+")","i")}function ft(){return this.hours()%12||12}function gt(t,e){N(t,0,0,function(){return this.localeData().meridiem(this.hours(),this.minutes(),e)})}function mt(t,e){return e._meridiemParse}function pt(t){return t?t.toLowerCase().replace("_","-"):t}function vt(t){for(var e,n,i,a,r=0;r0;){if(i=yt(a.slice(0,e).join("-")))return i;if(n&&n.length>=e&&k(a,n,!0)>=e-1)break;e--}r++}return null}function yt(n){var i=null;if(!Sn[n]&&void 0!==e&&e&&e.exports)try{i=kn._abbr,t("./locale/"+n),bt(i)}catch(t){}return Sn[n]}function bt(t,e){var n;return t&&(n=o(e)?_t(t):xt(t,e))&&(kn=n),kn._abbr}function xt(t,e){if(null!==e){var n=Mn;if(e.abbr=t,null!=Sn[t])S("defineLocaleOverride","use moment.updateLocale(localeName, config) to change an existing locale. moment.defineLocale(localeName, config) should only be used for creating a new locale See http://momentjs.com/guides/#/warnings/define-locale/ for more info."),n=Sn[t]._config;else if(null!=e.parentLocale){if(null==Sn[e.parentLocale])return Dn[e.parentLocale]||(Dn[e.parentLocale]=[]),Dn[e.parentLocale].push({name:t,config:e}),null;n=Sn[e.parentLocale]._config}return Sn[t]=new P(C(n,e)),Dn[t]&&Dn[t].forEach(function(t){xt(t.name,t.config)}),bt(t),Sn[t]}return delete Sn[t],null}function _t(t){var e;if(t&&t._locale&&t._locale._abbr&&(t=t._locale._abbr),!t)return kn;if(!i(t)){if(e=yt(t))return e;t=[t]}return vt(t)}function kt(t){var e,n=t._a;return n&&-2===g(t).overflow&&(e=n[tn]<0||n[tn]>11?tn:n[en]<1||n[en]>J(n[$e],n[tn])?en:n[nn]<0||n[nn]>24||24===n[nn]&&(0!==n[an]||0!==n[rn]||0!==n[on])?nn:n[an]<0||n[an]>59?an:n[rn]<0||n[rn]>59?rn:n[on]<0||n[on]>999?on:-1,g(t)._overflowDayOfYear&&(e<$e||e>en)&&(e=en),g(t)._overflowWeeks&&-1===e&&(e=sn),g(t)._overflowWeekday&&-1===e&&(e=ln),g(t).overflow=e),t}function wt(t){var e,n,i,a,r,o,s=t._i,l=Cn.exec(s)||Pn.exec(s);if(l){for(g(t).iso=!0,e=0,n=An.length;e10?"YYYY ":"YY "),r="HH:mm"+(n[4]?":ss":""),n[1]){var d=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"][new Date(n[2]).getDay()];if(n[1].substr(0,3)!==d)return g(t).weekdayMismatch=!0,void(t._isValid=!1)}switch(n[5].length){case 2:s=0===l?" +0000":((l="YXWVUTSRQPONZABCDEFGHIKLM".indexOf(n[5][1].toUpperCase())-12)<0?" -":" +")+(""+l).replace(/^-?/,"0").match(/..$/)[0]+"00";break;case 4:s=u[n[5]];break;default:s=u[" GMT"]}n[5]=s,t._i=n.splice(1).join(""),o=" ZZ",t._f=i+a+r+o,At(t),g(t).rfc2822=!0}else t._isValid=!1}function St(t){var e=On.exec(t._i);null===e?(wt(t),!1===t._isValid&&(delete t._isValid,Mt(t),!1===t._isValid&&(delete t._isValid,n.createFromInputFallback(t)))):t._d=new Date(+e[1])}function Dt(t,e,n){return null!=t?t:null!=e?e:n}function Ct(t){var e=new Date(n.now());return t._useUTC?[e.getUTCFullYear(),e.getUTCMonth(),e.getUTCDate()]:[e.getFullYear(),e.getMonth(),e.getDate()]}function Pt(t){var e,n,i,a,r=[];if(!t._d){for(i=Ct(t),t._w&&null==t._a[en]&&null==t._a[tn]&&Tt(t),null!=t._dayOfYear&&(a=Dt(t._a[$e],i[$e]),(t._dayOfYear>et(a)||0===t._dayOfYear)&&(g(t)._overflowDayOfYear=!0),n=at(a,0,t._dayOfYear),t._a[tn]=n.getUTCMonth(),t._a[en]=n.getUTCDate()),e=0;e<3&&null==t._a[e];++e)t._a[e]=r[e]=i[e];for(;e<7;e++)t._a[e]=r[e]=null==t._a[e]?2===e?1:0:t._a[e];24===t._a[nn]&&0===t._a[an]&&0===t._a[rn]&&0===t._a[on]&&(t._nextDay=!0,t._a[nn]=0),t._d=(t._useUTC?at:it).apply(null,r),null!=t._tzm&&t._d.setUTCMinutes(t._d.getUTCMinutes()-t._tzm),t._nextDay&&(t._a[nn]=24)}}function Tt(t){var e,n,i,a,r,o,s,l;if(null!=(e=t._w).GG||null!=e.W||null!=e.E)r=1,o=4,n=Dt(e.GG,t._a[$e],st(Nt(),1,4).year),i=Dt(e.W,1),((a=Dt(e.E,1))<1||a>7)&&(l=!0);else{r=t._locale._week.dow,o=t._locale._week.doy;var u=st(Nt(),r,o);n=Dt(e.gg,t._a[$e],u.year),i=Dt(e.w,u.week),null!=e.d?((a=e.d)<0||a>6)&&(l=!0):null!=e.e?(a=e.e+r,(e.e<0||e.e>6)&&(l=!0)):a=r}i<1||i>lt(n,r,o)?g(t)._overflowWeeks=!0:null!=l?g(t)._overflowWeekday=!0:(s=ot(n,i,a,r,o),t._a[$e]=s.year,t._dayOfYear=s.dayOfYear)}function At(t){if(t._f!==n.ISO_8601)if(t._f!==n.RFC_2822){t._a=[],g(t).empty=!0;var e,i,a,r,o,s=""+t._i,l=s.length,u=0;for(a=H(t._f,t._locale).match(Ie)||[],e=0;e0&&g(t).unusedInput.push(o),s=s.slice(s.indexOf(i)+i.length),u+=i.length),Re[r]?(i?g(t).empty=!1:g(t).unusedTokens.push(r),X(r,i,t)):t._strict&&!i&&g(t).unusedTokens.push(r);g(t).charsLeftOver=l-u,s.length>0&&g(t).unusedInput.push(s),t._a[nn]<=12&&!0===g(t).bigHour&&t._a[nn]>0&&(g(t).bigHour=void 0),g(t).parsedDateParts=t._a.slice(0),g(t).meridiem=t._meridiem,t._a[nn]=It(t._locale,t._a[nn],t._meridiem),Pt(t),kt(t)}else Mt(t);else wt(t)}function It(t,e,n){var i;return null==n?e:null!=t.meridiemHour?t.meridiemHour(e,n):null!=t.isPM?((i=t.isPM(n))&&e<12&&(e+=12),i||12!==e||(e=0),e):e}function Ot(t){var e,n,i,a,r;if(0===t._f.length)return g(t).invalidFormat=!0,void(t._d=new Date(NaN));for(a=0;ar&&(e=r),oe.call(this,t,e,n,i,a))}function oe(t,e,n,i,a){var r=ot(t,e,n,i,a),o=at(r.year,0,r.dayOfYear);return this.year(o.getUTCFullYear()),this.month(o.getUTCMonth()),this.date(o.getUTCDate()),this}function se(t){return t}function le(t,e,n,i){var a=_t(),r=h().set(i,e);return a[n](r,t)}function ue(t,e,n){if(s(t)&&(e=t,t=void 0),t=t||"",null!=e)return le(t,e,n,"month");var i,a=[];for(i=0;i<12;i++)a[i]=le(t,i,n,"month");return a}function de(t,e,n,i){"boolean"==typeof t?(s(e)&&(n=e,e=void 0),e=e||""):(n=e=t,t=!1,s(e)&&(n=e,e=void 0),e=e||"");var a=_t(),r=t?a._week.dow:0;if(null!=n)return le(e,(n+r)%7,i,"day");var o,l=[];for(o=0;o<7;o++)l[o]=le(e,(o+r)%7,i,"day");return l}function ce(t,e,n,i){var a=Xt(e,n);return t._milliseconds+=i*a._milliseconds,t._days+=i*a._days,t._months+=i*a._months,t._bubble()}function he(t){return t<0?Math.floor(t):Math.ceil(t)}function fe(t){return 4800*t/146097}function ge(t){return 146097*t/4800}function me(t){return function(){return this.as(t)}}function pe(t){return function(){return this.isValid()?this._data[t]:NaN}}function ve(t,e,n,i,a){return a.relativeTime(e||1,!!n,t,i)}function ye(t,e,n){var i=Xt(t).abs(),a=hi(i.as("s")),r=hi(i.as("m")),o=hi(i.as("h")),s=hi(i.as("d")),l=hi(i.as("M")),u=hi(i.as("y")),d=a<=fi.ss&&["s",a]||a0,d[4]=n,ve.apply(null,d)}function be(){if(!this.isValid())return this.localeData().invalidDate();var t,e,n,i=gi(this._milliseconds)/1e3,a=gi(this._days),r=gi(this._months);e=x((t=x(i/60))/60),i%=60,t%=60;var o=n=x(r/12),s=r%=12,l=a,u=e,d=t,c=i,h=this.asSeconds();return h?(h<0?"-":"")+"P"+(o?o+"Y":"")+(s?s+"M":"")+(l?l+"D":"")+(u||d||c?"T":"")+(u?u+"H":"")+(d?d+"M":"")+(c?c+"S":""):"P0D"}var xe,_e,ke=_e=Array.prototype.some?Array.prototype.some:function(t){for(var e=Object(this),n=e.length>>>0,i=0;i68?1900:2e3)};var mn=R("FullYear",!0);N("w",["ww",2],"wo","week"),N("W",["WW",2],"Wo","isoWeek"),T("week","w"),T("isoWeek","W"),O("week",5),O("isoWeek",5),E("w",Be),E("ww",Be,We),E("W",Be),E("WW",Be,We),Z(["w","ww","W","WW"],function(t,e,n,i){e[i.substr(0,1)]=_(t)});N("d",0,"do","day"),N("dd",0,0,function(t){return this.localeData().weekdaysMin(this,t)}),N("ddd",0,0,function(t){return this.localeData().weekdaysShort(this,t)}),N("dddd",0,0,function(t){return this.localeData().weekdays(this,t)}),N("e",0,0,"weekday"),N("E",0,0,"isoWeekday"),T("day","d"),T("weekday","e"),T("isoWeekday","E"),O("day",11),O("weekday",11),O("isoWeekday",11),E("d",Be),E("e",Be),E("E",Be),E("dd",function(t,e){return e.weekdaysMinRegex(t)}),E("ddd",function(t,e){return e.weekdaysShortRegex(t)}),E("dddd",function(t,e){return e.weekdaysRegex(t)}),Z(["dd","ddd","dddd"],function(t,e,n,i){var a=n._locale.weekdaysParse(t,i,n._strict);null!=a?e.d=a:g(n).invalidWeekday=t}),Z(["d","e","E"],function(t,e,n,i){e[i]=_(t)});var pn="Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),vn="Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),yn="Su_Mo_Tu_We_Th_Fr_Sa".split("_"),bn=Je,xn=Je,_n=Je;N("H",["HH",2],0,"hour"),N("h",["hh",2],0,ft),N("k",["kk",2],0,function(){return this.hours()||24}),N("hmm",0,0,function(){return""+ft.apply(this)+Y(this.minutes(),2)}),N("hmmss",0,0,function(){return""+ft.apply(this)+Y(this.minutes(),2)+Y(this.seconds(),2)}),N("Hmm",0,0,function(){return""+this.hours()+Y(this.minutes(),2)}),N("Hmmss",0,0,function(){return""+this.hours()+Y(this.minutes(),2)+Y(this.seconds(),2)}),gt("a",!0),gt("A",!1),T("hour","h"),O("hour",13),E("a",mt),E("A",mt),E("H",Be),E("h",Be),E("k",Be),E("HH",Be,We),E("hh",Be,We),E("kk",Be,We),E("hmm",Ve),E("hmmss",He),E("Hmm",Ve),E("Hmmss",He),G(["H","HH"],nn),G(["k","kk"],function(t,e,n){var i=_(t);e[nn]=24===i?0:i}),G(["a","A"],function(t,e,n){n._isPm=n._locale.isPM(t),n._meridiem=t}),G(["h","hh"],function(t,e,n){e[nn]=_(t),g(n).bigHour=!0}),G("hmm",function(t,e,n){var i=t.length-2;e[nn]=_(t.substr(0,i)),e[an]=_(t.substr(i)),g(n).bigHour=!0}),G("hmmss",function(t,e,n){var i=t.length-4,a=t.length-2;e[nn]=_(t.substr(0,i)),e[an]=_(t.substr(i,2)),e[rn]=_(t.substr(a)),g(n).bigHour=!0}),G("Hmm",function(t,e,n){var i=t.length-2;e[nn]=_(t.substr(0,i)),e[an]=_(t.substr(i))}),G("Hmmss",function(t,e,n){var i=t.length-4,a=t.length-2;e[nn]=_(t.substr(0,i)),e[an]=_(t.substr(i,2)),e[rn]=_(t.substr(a))});var kn,wn=R("Hours",!0),Mn={calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},longDateFormat:{LTS:"h:mm:ss A",LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY h:mm A",LLLL:"dddd, MMMM D, YYYY h:mm A"},invalidDate:"Invalid date",ordinal:"%d",dayOfMonthOrdinalParse:/\d{1,2}/,relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",ss:"%d seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},months:cn,monthsShort:hn,week:{dow:0,doy:6},weekdays:pn,weekdaysMin:yn,weekdaysShort:vn,meridiemParse:/[ap]\.?m?\.?/i},Sn={},Dn={},Cn=/^\s*((?:[+-]\d{6}|\d{4})-(?:\d\d-\d\d|W\d\d-\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?::\d\d(?::\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,Pn=/^\s*((?:[+-]\d{6}|\d{4})(?:\d\d\d\d|W\d\d\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?:\d\d(?:\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,Tn=/Z|[+-]\d\d(?::?\d\d)?/,An=[["YYYYYY-MM-DD",/[+-]\d{6}-\d\d-\d\d/],["YYYY-MM-DD",/\d{4}-\d\d-\d\d/],["GGGG-[W]WW-E",/\d{4}-W\d\d-\d/],["GGGG-[W]WW",/\d{4}-W\d\d/,!1],["YYYY-DDD",/\d{4}-\d{3}/],["YYYY-MM",/\d{4}-\d\d/,!1],["YYYYYYMMDD",/[+-]\d{10}/],["YYYYMMDD",/\d{8}/],["GGGG[W]WWE",/\d{4}W\d{3}/],["GGGG[W]WW",/\d{4}W\d{2}/,!1],["YYYYDDD",/\d{7}/]],In=[["HH:mm:ss.SSSS",/\d\d:\d\d:\d\d\.\d+/],["HH:mm:ss,SSSS",/\d\d:\d\d:\d\d,\d+/],["HH:mm:ss",/\d\d:\d\d:\d\d/],["HH:mm",/\d\d:\d\d/],["HHmmss.SSSS",/\d\d\d\d\d\d\.\d+/],["HHmmss,SSSS",/\d\d\d\d\d\d,\d+/],["HHmmss",/\d\d\d\d\d\d/],["HHmm",/\d\d\d\d/],["HH",/\d\d/]],On=/^\/?Date\((\-?\d+)/i,Fn=/^((?:Mon|Tue|Wed|Thu|Fri|Sat|Sun),?\s)?(\d?\d\s(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s(?:\d\d)?\d\d\s)(\d\d:\d\d)(\:\d\d)?(\s(?:UT|GMT|[ECMP][SD]T|[A-IK-Za-ik-z]|[+-]\d{4}))$/;n.createFromInputFallback=M("value provided is not in a recognized RFC2822 or ISO format. moment construction falls back to js Date(), which is not reliable across all browsers and versions. Non RFC2822/ISO date formats are discouraged and will be removed in an upcoming major release. Please refer to http://momentjs.com/guides/#/warnings/js-date/ for more info.",function(t){t._d=new Date(t._i+(t._useUTC?" UTC":""))}),n.ISO_8601=function(){},n.RFC_2822=function(){};var Rn=M("moment().min is deprecated, use moment.max instead. http://momentjs.com/guides/#/warnings/min-max/",function(){var t=Nt.apply(null,arguments);return this.isValid()&&t.isValid()?tthis?this:t:p()}),Wn=["year","quarter","month","week","day","hour","minute","second","millisecond"];jt("Z",":"),jt("ZZ",""),E("Z",Xe),E("ZZ",Xe),G(["Z","ZZ"],function(t,e,n){n._useUTC=!0,n._tzm=Ut(Xe,t)});var Yn=/([\+\-]|\d\d)/gi;n.updateOffset=function(){};var Nn=/^(\-)?(?:(\d*)[. ])?(\d+)\:(\d+)(?:\:(\d+)(\.\d*)?)?$/,zn=/^(-)?P(?:(-?[0-9,.]*)Y)?(?:(-?[0-9,.]*)M)?(?:(-?[0-9,.]*)W)?(?:(-?[0-9,.]*)D)?(?:T(?:(-?[0-9,.]*)H)?(?:(-?[0-9,.]*)M)?(?:(-?[0-9,.]*)S)?)?$/;Xt.fn=Vt.prototype,Xt.invalid=function(){return Xt(NaN)};var Bn=$t(1,"add"),Vn=$t(-1,"subtract");n.defaultFormat="YYYY-MM-DDTHH:mm:ssZ",n.defaultFormatUtc="YYYY-MM-DDTHH:mm:ss[Z]";var Hn=M("moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.",function(t){return void 0===t?this.localeData():this.locale(t)});N(0,["gg",2],0,function(){return this.weekYear()%100}),N(0,["GG",2],0,function(){return this.isoWeekYear()%100}),ae("gggg","weekYear"),ae("ggggg","weekYear"),ae("GGGG","isoWeekYear"),ae("GGGGG","isoWeekYear"),T("weekYear","gg"),T("isoWeekYear","GG"),O("weekYear",1),O("isoWeekYear",1),E("G",Ge),E("g",Ge),E("GG",Be,We),E("gg",Be,We),E("GGGG",je,Ne),E("gggg",je,Ne),E("GGGGG",Ue,ze),E("ggggg",Ue,ze),Z(["gggg","ggggg","GGGG","GGGGG"],function(t,e,n,i){e[i.substr(0,2)]=_(t)}),Z(["gg","GG"],function(t,e,i,a){e[a]=n.parseTwoDigitYear(t)}),N("Q",0,"Qo","quarter"),T("quarter","Q"),O("quarter",7),E("Q",Le),G("Q",function(t,e){e[tn]=3*(_(t)-1)}),N("D",["DD",2],"Do","date"),T("date","D"),O("date",9),E("D",Be),E("DD",Be,We),E("Do",function(t,e){return t?e._dayOfMonthOrdinalParse||e._ordinalParse:e._dayOfMonthOrdinalParseLenient}),G(["D","DD"],en),G("Do",function(t,e){e[en]=_(t.match(Be)[0],10)});var En=R("Date",!0);N("DDD",["DDDD",3],"DDDo","dayOfYear"),T("dayOfYear","DDD"),O("dayOfYear",4),E("DDD",Ee),E("DDDD",Ye),G(["DDD","DDDD"],function(t,e,n){n._dayOfYear=_(t)}),N("m",["mm",2],0,"minute"),T("minute","m"),O("minute",14),E("m",Be),E("mm",Be,We),G(["m","mm"],an);var jn=R("Minutes",!1);N("s",["ss",2],0,"second"),T("second","s"),O("second",15),E("s",Be),E("ss",Be,We),G(["s","ss"],rn);var Un=R("Seconds",!1);N("S",0,0,function(){return~~(this.millisecond()/100)}),N(0,["SS",2],0,function(){return~~(this.millisecond()/10)}),N(0,["SSS",3],0,"millisecond"),N(0,["SSSS",4],0,function(){return 10*this.millisecond()}),N(0,["SSSSS",5],0,function(){return 100*this.millisecond()}),N(0,["SSSSSS",6],0,function(){return 1e3*this.millisecond()}),N(0,["SSSSSSS",7],0,function(){return 1e4*this.millisecond()}),N(0,["SSSSSSSS",8],0,function(){return 1e5*this.millisecond()}),N(0,["SSSSSSSSS",9],0,function(){return 1e6*this.millisecond()}),T("millisecond","ms"),O("millisecond",16),E("S",Ee,Le),E("SS",Ee,We),E("SSS",Ee,Ye);var qn;for(qn="SSSS";qn.length<=9;qn+="S")E(qn,qe);for(qn="S";qn.length<=9;qn+="S")G(qn,function(t,e){e[on]=_(1e3*("0."+t))});var Gn=R("Milliseconds",!1);N("z",0,0,"zoneAbbr"),N("zz",0,0,"zoneName");var Zn=y.prototype;Zn.add=Bn,Zn.calendar=function(t,e){var i=t||Nt(),a=qt(i,this).startOf("day"),r=n.calendarFormat(this,a)||"sameElse",o=e&&(D(e[r])?e[r].call(this,i):e[r]);return this.format(o||this.localeData().calendar(r,this,Nt(i)))},Zn.clone=function(){return new y(this)},Zn.diff=function(t,e,n){var i,a,r,o;return this.isValid()&&(i=qt(t,this)).isValid()?(a=6e4*(i.utcOffset()-this.utcOffset()),"year"===(e=A(e))||"month"===e||"quarter"===e?(o=ee(this,i),"quarter"===e?o/=3:"year"===e&&(o/=12)):(r=this-i,o="second"===e?r/1e3:"minute"===e?r/6e4:"hour"===e?r/36e5:"day"===e?(r-a)/864e5:"week"===e?(r-a)/6048e5:r),n?o:x(o)):NaN},Zn.endOf=function(t){return void 0===(t=A(t))||"millisecond"===t?this:("date"===t&&(t="day"),this.startOf(t).add(1,"isoWeek"===t?"week":t).subtract(1,"ms"))},Zn.format=function(t){t||(t=this.isUtc()?n.defaultFormatUtc:n.defaultFormat);var e=V(this,t);return this.localeData().postformat(e)},Zn.from=function(t,e){return this.isValid()&&(b(t)&&t.isValid()||Nt(t).isValid())?Xt({to:this,from:t}).locale(this.locale()).humanize(!e):this.localeData().invalidDate()},Zn.fromNow=function(t){return this.from(Nt(),t)},Zn.to=function(t,e){return this.isValid()&&(b(t)&&t.isValid()||Nt(t).isValid())?Xt({from:this,to:t}).locale(this.locale()).humanize(!e):this.localeData().invalidDate()},Zn.toNow=function(t){return this.to(Nt(),t)},Zn.get=function(t){return t=A(t),D(this[t])?this[t]():this},Zn.invalidAt=function(){return g(this).overflow},Zn.isAfter=function(t,e){var n=b(t)?t:Nt(t);return!(!this.isValid()||!n.isValid())&&("millisecond"===(e=A(o(e)?"millisecond":e))?this.valueOf()>n.valueOf():n.valueOf()9999?V(t,"YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]"):D(Date.prototype.toISOString)?this.toDate().toISOString():V(t,"YYYY-MM-DD[T]HH:mm:ss.SSS[Z]")},Zn.inspect=function(){if(!this.isValid())return"moment.invalid(/* "+this._i+" */)";var t="moment",e="";this.isLocal()||(t=0===this.utcOffset()?"moment.utc":"moment.parseZone",e="Z");var n="["+t+'("]',i=0<=this.year()&&this.year()<=9999?"YYYY":"YYYYYY",a=e+'[")]';return this.format(n+i+"-MM-DD[T]HH:mm:ss.SSS"+a)},Zn.toJSON=function(){return this.isValid()?this.toISOString():null},Zn.toString=function(){return this.clone().locale("en").format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ")},Zn.unix=function(){return Math.floor(this.valueOf()/1e3)},Zn.valueOf=function(){return this._d.valueOf()-6e4*(this._offset||0)},Zn.creationData=function(){return{input:this._i,format:this._f,locale:this._locale,isUTC:this._isUTC,strict:this._strict}},Zn.year=mn,Zn.isLeapYear=function(){return nt(this.year())},Zn.weekYear=function(t){return re.call(this,t,this.week(),this.weekday(),this.localeData()._week.dow,this.localeData()._week.doy)},Zn.isoWeekYear=function(t){return re.call(this,t,this.isoWeek(),this.isoWeekday(),1,4)},Zn.quarter=Zn.quarters=function(t){return null==t?Math.ceil((this.month()+1)/3):this.month(3*(t-1)+this.month()%3)},Zn.month=$,Zn.daysInMonth=function(){return J(this.year(),this.month())},Zn.week=Zn.weeks=function(t){var e=this.localeData().week(this);return null==t?e:this.add(7*(t-e),"d")},Zn.isoWeek=Zn.isoWeeks=function(t){var e=st(this,1,4).week;return null==t?e:this.add(7*(t-e),"d")},Zn.weeksInYear=function(){var t=this.localeData()._week;return lt(this.year(),t.dow,t.doy)},Zn.isoWeeksInYear=function(){return lt(this.year(),1,4)},Zn.date=En,Zn.day=Zn.days=function(t){if(!this.isValid())return null!=t?this:NaN;var e=this._isUTC?this._d.getUTCDay():this._d.getDay();return null!=t?(t=ut(t,this.localeData()),this.add(t-e,"d")):e},Zn.weekday=function(t){if(!this.isValid())return null!=t?this:NaN;var e=(this.day()+7-this.localeData()._week.dow)%7;return null==t?e:this.add(t-e,"d")},Zn.isoWeekday=function(t){if(!this.isValid())return null!=t?this:NaN;if(null!=t){var e=dt(t,this.localeData());return this.day(this.day()%7?e:e-7)}return this.day()||7},Zn.dayOfYear=function(t){var e=Math.round((this.clone().startOf("day")-this.clone().startOf("year"))/864e5)+1;return null==t?e:this.add(t-e,"d")},Zn.hour=Zn.hours=wn,Zn.minute=Zn.minutes=jn,Zn.second=Zn.seconds=Un,Zn.millisecond=Zn.milliseconds=Gn,Zn.utcOffset=function(t,e,i){var a,r=this._offset||0;if(!this.isValid())return null!=t?this:NaN;if(null!=t){if("string"==typeof t){if(null===(t=Ut(Xe,t)))return this}else Math.abs(t)<16&&!i&&(t*=60);return!this._isUTC&&e&&(a=Gt(this)),this._offset=t,this._isUTC=!0,null!=a&&this.add(a,"m"),r!==t&&(!e||this._changeInProgress?te(this,Xt(t-r,"m"),1,!1):this._changeInProgress||(this._changeInProgress=!0,n.updateOffset(this,!0),this._changeInProgress=null)),this}return this._isUTC?r:Gt(this)},Zn.utc=function(t){return this.utcOffset(0,t)},Zn.local=function(t){return this._isUTC&&(this.utcOffset(0,t),this._isUTC=!1,t&&this.subtract(Gt(this),"m")),this},Zn.parseZone=function(){if(null!=this._tzm)this.utcOffset(this._tzm,!1,!0);else if("string"==typeof this._i){var t=Ut(Ze,this._i);null!=t?this.utcOffset(t):this.utcOffset(0,!0)}return this},Zn.hasAlignedHourOffset=function(t){return!!this.isValid()&&(t=t?Nt(t).utcOffset():0,(this.utcOffset()-t)%60==0)},Zn.isDST=function(){return this.utcOffset()>this.clone().month(0).utcOffset()||this.utcOffset()>this.clone().month(5).utcOffset()},Zn.isLocal=function(){return!!this.isValid()&&!this._isUTC},Zn.isUtcOffset=function(){return!!this.isValid()&&this._isUTC},Zn.isUtc=Zt,Zn.isUTC=Zt,Zn.zoneAbbr=function(){return this._isUTC?"UTC":""},Zn.zoneName=function(){return this._isUTC?"Coordinated Universal Time":""},Zn.dates=M("dates accessor is deprecated. Use date instead.",En),Zn.months=M("months accessor is deprecated. Use month instead",$),Zn.years=M("years accessor is deprecated. Use year instead",mn),Zn.zone=M("moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/",function(t,e){return null!=t?("string"!=typeof t&&(t=-t),this.utcOffset(t,e),this):-this.utcOffset()}),Zn.isDSTShifted=M("isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information",function(){if(!o(this._isDSTShifted))return this._isDSTShifted;var t={};if(v(t,this),(t=Lt(t))._a){var e=t._isUTC?h(t._a):Nt(t._a);this._isDSTShifted=this.isValid()&&k(t._a,e.toArray())>0}else this._isDSTShifted=!1;return this._isDSTShifted});var Xn=P.prototype;Xn.calendar=function(t,e,n){var i=this._calendar[t]||this._calendar.sameElse;return D(i)?i.call(e,n):i},Xn.longDateFormat=function(t){var e=this._longDateFormat[t],n=this._longDateFormat[t.toUpperCase()];return e||!n?e:(this._longDateFormat[t]=n.replace(/MMMM|MM|DD|dddd/g,function(t){return t.slice(1)}),this._longDateFormat[t])},Xn.invalidDate=function(){return this._invalidDate},Xn.ordinal=function(t){return this._ordinal.replace("%d",t)},Xn.preparse=se,Xn.postformat=se,Xn.relativeTime=function(t,e,n,i){var a=this._relativeTime[n];return D(a)?a(t,e,n,i):a.replace(/%d/i,t)},Xn.pastFuture=function(t,e){var n=this._relativeTime[t>0?"future":"past"];return D(n)?n(e):n.replace(/%s/i,e)},Xn.set=function(t){var e,n;for(n in t)D(e=t[n])?this[n]=e:this["_"+n]=e;this._config=t,this._dayOfMonthOrdinalParseLenient=new RegExp((this._dayOfMonthOrdinalParse.source||this._ordinalParse.source)+"|"+/\d{1,2}/.source)},Xn.months=function(t,e){return t?i(this._months)?this._months[t.month()]:this._months[(this._months.isFormat||dn).test(e)?"format":"standalone"][t.month()]:i(this._months)?this._months:this._months.standalone},Xn.monthsShort=function(t,e){return t?i(this._monthsShort)?this._monthsShort[t.month()]:this._monthsShort[dn.test(e)?"format":"standalone"][t.month()]:i(this._monthsShort)?this._monthsShort:this._monthsShort.standalone},Xn.monthsParse=function(t,e,n){var i,a,r;if(this._monthsParseExact)return K.call(this,t,e,n);for(this._monthsParse||(this._monthsParse=[],this._longMonthsParse=[],this._shortMonthsParse=[]),i=0;i<12;i++){if(a=h([2e3,i]),n&&!this._longMonthsParse[i]&&(this._longMonthsParse[i]=new RegExp("^"+this.months(a,"").replace(".","")+"$","i"),this._shortMonthsParse[i]=new RegExp("^"+this.monthsShort(a,"").replace(".","")+"$","i")),n||this._monthsParse[i]||(r="^"+this.months(a,"")+"|^"+this.monthsShort(a,""),this._monthsParse[i]=new RegExp(r.replace(".",""),"i")),n&&"MMMM"===e&&this._longMonthsParse[i].test(t))return i;if(n&&"MMM"===e&&this._shortMonthsParse[i].test(t))return i;if(!n&&this._monthsParse[i].test(t))return i}},Xn.monthsRegex=function(t){return this._monthsParseExact?(d(this,"_monthsRegex")||tt.call(this),t?this._monthsStrictRegex:this._monthsRegex):(d(this,"_monthsRegex")||(this._monthsRegex=gn),this._monthsStrictRegex&&t?this._monthsStrictRegex:this._monthsRegex)},Xn.monthsShortRegex=function(t){return this._monthsParseExact?(d(this,"_monthsRegex")||tt.call(this),t?this._monthsShortStrictRegex:this._monthsShortRegex):(d(this,"_monthsShortRegex")||(this._monthsShortRegex=fn),this._monthsShortStrictRegex&&t?this._monthsShortStrictRegex:this._monthsShortRegex)},Xn.week=function(t){return st(t,this._week.dow,this._week.doy).week},Xn.firstDayOfYear=function(){return this._week.doy},Xn.firstDayOfWeek=function(){return this._week.dow},Xn.weekdays=function(t,e){return t?i(this._weekdays)?this._weekdays[t.day()]:this._weekdays[this._weekdays.isFormat.test(e)?"format":"standalone"][t.day()]:i(this._weekdays)?this._weekdays:this._weekdays.standalone},Xn.weekdaysMin=function(t){return t?this._weekdaysMin[t.day()]:this._weekdaysMin},Xn.weekdaysShort=function(t){return t?this._weekdaysShort[t.day()]:this._weekdaysShort},Xn.weekdaysParse=function(t,e,n){var i,a,r;if(this._weekdaysParseExact)return ct.call(this,t,e,n);for(this._weekdaysParse||(this._weekdaysParse=[],this._minWeekdaysParse=[],this._shortWeekdaysParse=[],this._fullWeekdaysParse=[]),i=0;i<7;i++){if(a=h([2e3,1]).day(i),n&&!this._fullWeekdaysParse[i]&&(this._fullWeekdaysParse[i]=new RegExp("^"+this.weekdays(a,"").replace(".",".?")+"$","i"),this._shortWeekdaysParse[i]=new RegExp("^"+this.weekdaysShort(a,"").replace(".",".?")+"$","i"),this._minWeekdaysParse[i]=new RegExp("^"+this.weekdaysMin(a,"").replace(".",".?")+"$","i")),this._weekdaysParse[i]||(r="^"+this.weekdays(a,"")+"|^"+this.weekdaysShort(a,"")+"|^"+this.weekdaysMin(a,""),this._weekdaysParse[i]=new RegExp(r.replace(".",""),"i")),n&&"dddd"===e&&this._fullWeekdaysParse[i].test(t))return i;if(n&&"ddd"===e&&this._shortWeekdaysParse[i].test(t))return i;if(n&&"dd"===e&&this._minWeekdaysParse[i].test(t))return i;if(!n&&this._weekdaysParse[i].test(t))return i}},Xn.weekdaysRegex=function(t){return this._weekdaysParseExact?(d(this,"_weekdaysRegex")||ht.call(this),t?this._weekdaysStrictRegex:this._weekdaysRegex):(d(this,"_weekdaysRegex")||(this._weekdaysRegex=bn),this._weekdaysStrictRegex&&t?this._weekdaysStrictRegex:this._weekdaysRegex)},Xn.weekdaysShortRegex=function(t){return this._weekdaysParseExact?(d(this,"_weekdaysRegex")||ht.call(this),t?this._weekdaysShortStrictRegex:this._weekdaysShortRegex):(d(this,"_weekdaysShortRegex")||(this._weekdaysShortRegex=xn),this._weekdaysShortStrictRegex&&t?this._weekdaysShortStrictRegex:this._weekdaysShortRegex)},Xn.weekdaysMinRegex=function(t){return this._weekdaysParseExact?(d(this,"_weekdaysRegex")||ht.call(this),t?this._weekdaysMinStrictRegex:this._weekdaysMinRegex):(d(this,"_weekdaysMinRegex")||(this._weekdaysMinRegex=_n),this._weekdaysMinStrictRegex&&t?this._weekdaysMinStrictRegex:this._weekdaysMinRegex)},Xn.isPM=function(t){return"p"===(t+"").toLowerCase().charAt(0)},Xn.meridiem=function(t,e,n){return t>11?n?"pm":"PM":n?"am":"AM"},bt("en",{dayOfMonthOrdinalParse:/\d{1,2}(th|st|nd|rd)/,ordinal:function(t){var e=t%10;return t+(1===_(t%100/10)?"th":1===e?"st":2===e?"nd":3===e?"rd":"th")}}),n.lang=M("moment.lang is deprecated. Use moment.locale instead.",bt),n.langData=M("moment.langData is deprecated. Use moment.localeData instead.",_t);var Jn=Math.abs,Kn=me("ms"),Qn=me("s"),$n=me("m"),ti=me("h"),ei=me("d"),ni=me("w"),ii=me("M"),ai=me("y"),ri=pe("milliseconds"),oi=pe("seconds"),si=pe("minutes"),li=pe("hours"),ui=pe("days"),di=pe("months"),ci=pe("years"),hi=Math.round,fi={ss:44,s:45,m:45,h:22,d:26,M:11},gi=Math.abs,mi=Vt.prototype;return mi.isValid=function(){return this._isValid},mi.abs=function(){var t=this._data;return this._milliseconds=Jn(this._milliseconds),this._days=Jn(this._days),this._months=Jn(this._months),t.milliseconds=Jn(t.milliseconds),t.seconds=Jn(t.seconds),t.minutes=Jn(t.minutes),t.hours=Jn(t.hours),t.months=Jn(t.months),t.years=Jn(t.years),this},mi.add=function(t,e){return ce(this,t,e,1)},mi.subtract=function(t,e){return ce(this,t,e,-1)},mi.as=function(t){if(!this.isValid())return NaN;var e,n,i=this._milliseconds;if("month"===(t=A(t))||"year"===t)return e=this._days+i/864e5,n=this._months+fe(e),"month"===t?n:n/12;switch(e=this._days+Math.round(ge(this._months)),t){case"week":return e/7+i/6048e5;case"day":return e+i/864e5;case"hour":return 24*e+i/36e5;case"minute":return 1440*e+i/6e4;case"second":return 86400*e+i/1e3;case"millisecond":return Math.floor(864e5*e)+i;default:throw new Error("Unknown unit "+t)}},mi.asMilliseconds=Kn,mi.asSeconds=Qn,mi.asMinutes=$n,mi.asHours=ti,mi.asDays=ei,mi.asWeeks=ni,mi.asMonths=ii,mi.asYears=ai,mi.valueOf=function(){return this.isValid()?this._milliseconds+864e5*this._days+this._months%12*2592e6+31536e6*_(this._months/12):NaN},mi._bubble=function(){var t,e,n,i,a,r=this._milliseconds,o=this._days,s=this._months,l=this._data;return r>=0&&o>=0&&s>=0||r<=0&&o<=0&&s<=0||(r+=864e5*he(ge(s)+o),o=0,s=0),l.milliseconds=r%1e3,t=x(r/1e3),l.seconds=t%60,e=x(t/60),l.minutes=e%60,n=x(e/60),l.hours=n%24,o+=x(n/24),a=x(fe(o)),s+=a,o-=he(ge(a)),i=x(s/12),s%=12,l.days=o,l.months=s,l.years=i,this},mi.get=function(t){return t=A(t),this.isValid()?this[t+"s"]():NaN},mi.milliseconds=ri,mi.seconds=oi,mi.minutes=si,mi.hours=li,mi.days=ui,mi.weeks=function(){return x(this.days()/7)},mi.months=di,mi.years=ci,mi.humanize=function(t){if(!this.isValid())return this.localeData().invalidDate();var e=this.localeData(),n=ye(this,!t,e);return t&&(n=e.pastFuture(+this,n)),e.postformat(n)},mi.toISOString=be,mi.toString=be,mi.toJSON=be,mi.locale=ne,mi.localeData=ie,mi.toIsoString=M("toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)",be),mi.lang=Hn,N("X",0,0,"unix"),N("x",0,0,"valueOf"),E("x",Ge),E("X",/[+-]?\d+(\.\d{1,3})?/),G("X",function(t,e,n){n._d=new Date(1e3*parseFloat(t,10))}),G("x",function(t,e,n){n._d=new Date(_(t))}),n.version="2.18.1",function(t){xe=t}(Nt),n.fn=Zn,n.min=function(){return zt("isBefore",[].slice.call(arguments,0))},n.max=function(){return zt("isAfter",[].slice.call(arguments,0))},n.now=function(){return Date.now?Date.now():+new Date},n.utc=h,n.unix=function(t){return Nt(1e3*t)},n.months=function(t,e){return ue(t,e,"months")},n.isDate=l,n.locale=bt,n.invalid=p,n.duration=Xt,n.isMoment=b,n.weekdays=function(t,e,n){return de(t,e,n,"weekdays")},n.parseZone=function(){return Nt.apply(null,arguments).parseZone()},n.localeData=_t,n.isDuration=Ht,n.monthsShort=function(t,e){return ue(t,e,"monthsShort")},n.weekdaysMin=function(t,e,n){return de(t,e,n,"weekdaysMin")},n.defineLocale=xt,n.updateLocale=function(t,e){if(null!=e){var n,i=Mn;null!=Sn[t]&&(i=Sn[t]._config),(n=new P(e=C(i,e))).parentLocale=Sn[t],Sn[t]=n,bt(t)}else null!=Sn[t]&&(null!=Sn[t].parentLocale?Sn[t]=Sn[t].parentLocale:null!=Sn[t]&&delete Sn[t]);return Sn[t]},n.locales=function(){return Pe(Sn)},n.weekdaysShort=function(t,e,n){return de(t,e,n,"weekdaysShort")},n.normalizeUnits=A,n.relativeTimeRounding=function(t){return void 0===t?hi:"function"==typeof t&&(hi=t,!0)},n.relativeTimeThreshold=function(t,e){return void 0!==fi[t]&&(void 0===e?fi[t]:(fi[t]=e,"s"===t&&(fi.ss=e-1),!0))},n.calendarFormat=function(t,e){var n=t.diff(e,"days",!0);return n<-6?"sameElse":n<-1?"lastWeek":n<0?"lastDay":n<1?"sameDay":n<2?"nextDay":n<7?"nextWeek":"sameElse"},n.prototype=Zn,n})},{}],7:[function(t,e,n){var i=t(29)();i.helpers=t(45),t(27)(i),i.defaults=t(25),i.Element=t(26),i.elements=t(40),i.Interaction=t(28),i.platform=t(48),t(31)(i),t(22)(i),t(23)(i),t(24)(i),t(30)(i),t(33)(i),t(32)(i),t(35)(i),t(54)(i),t(52)(i),t(53)(i),t(55)(i),t(56)(i),t(57)(i),t(15)(i),t(16)(i),t(17)(i),t(18)(i),t(19)(i),t(20)(i),t(21)(i),t(8)(i),t(9)(i),t(10)(i),t(11)(i),t(12)(i),t(13)(i),t(14)(i);var a=[];a.push(t(49)(i),t(50)(i),t(51)(i)),i.plugins.register(a),i.platform.initialize(),e.exports=i,"undefined"!=typeof window&&(window.Chart=i),i.canvasHelpers=i.helpers.canvas},{10:10,11:11,12:12,13:13,14:14,15:15,16:16,17:17,18:18,19:19,20:20,21:21,22:22,23:23,24:24,25:25,26:26,27:27,28:28,29:29,30:30,31:31,32:32,33:33,35:35,40:40,45:45,48:48,49:49,50:50,51:51,52:52,53:53,54:54,55:55,56:56,57:57,8:8,9:9}],8:[function(t,e,n){"use strict";e.exports=function(t){t.Bar=function(e,n){return n.type="bar",new t(e,n)}}},{}],9:[function(t,e,n){"use strict";e.exports=function(t){t.Bubble=function(e,n){return n.type="bubble",new t(e,n)}}},{}],10:[function(t,e,n){"use strict";e.exports=function(t){t.Doughnut=function(e,n){return n.type="doughnut",new t(e,n)}}},{}],11:[function(t,e,n){"use strict";e.exports=function(t){t.Line=function(e,n){return n.type="line",new t(e,n)}}},{}],12:[function(t,e,n){"use strict";e.exports=function(t){t.PolarArea=function(e,n){return n.type="polarArea",new t(e,n)}}},{}],13:[function(t,e,n){"use strict";e.exports=function(t){t.Radar=function(e,n){return n.type="radar",new t(e,n)}}},{}],14:[function(t,e,n){"use strict";e.exports=function(t){t.Scatter=function(e,n){return n.type="scatter",new t(e,n)}}},{}],15:[function(t,e,n){"use strict";var i=t(25),a=t(40),r=t(45);i._set("bar",{hover:{mode:"label"},scales:{xAxes:[{type:"category",categoryPercentage:.8,barPercentage:.9,offset:!0,gridLines:{offsetGridLines:!0}}],yAxes:[{type:"linear"}]}}),i._set("horizontalBar",{hover:{mode:"index",axis:"y"},scales:{xAxes:[{type:"linear",position:"bottom"}],yAxes:[{position:"left",type:"category",categoryPercentage:.8,barPercentage:.9,offset:!0,gridLines:{offsetGridLines:!0}}]},elements:{rectangle:{borderSkipped:"left"}},tooltips:{callbacks:{title:function(t,e){var n="";return t.length>0&&(t[0].yLabel?n=t[0].yLabel:e.labels.length>0&&t[0].index=0&&a>0)&&(p+=a));return r=c.getPixelForValue(p),o=c.getPixelForValue(p+f),s=(o-r)/2,{size:s,base:r,head:o,center:o+s/2}},calculateBarIndexPixels:function(t,e,n){var i,a,o,s,l,u,d=this,c=n.scale.options,h=d.getStackIndex(t),f=n.pixels,g=f[e],m=f.length,p=n.start,v=n.end;return 1===m?(i=g>p?g-p:v-g,a=g0&&(i=(g-f[e-1])/2,e===m-1&&(a=i)),e');var n=t.data,i=n.datasets,a=n.labels;if(i.length)for(var r=0;r'),a[r]&&e.push(a[r]),e.push("");return e.push(""),e.join("")},legend:{labels:{generateLabels:function(t){var e=t.data;return e.labels.length&&e.datasets.length?e.labels.map(function(n,i){var a=t.getDatasetMeta(0),o=e.datasets[0],s=a.data[i],l=s&&s.custom||{},u=r.valueAtIndexOrDefault,d=t.options.elements.arc;return{text:n,fillStyle:l.backgroundColor?l.backgroundColor:u(o.backgroundColor,i,d.backgroundColor),strokeStyle:l.borderColor?l.borderColor:u(o.borderColor,i,d.borderColor),lineWidth:l.borderWidth?l.borderWidth:u(o.borderWidth,i,d.borderWidth),hidden:isNaN(o.data[i])||a.data[i].hidden,index:i}}):[]}},onClick:function(t,e){var n,i,a,r=e.index,o=this.chart;for(n=0,i=(o.data.datasets||[]).length;n=Math.PI?-1:g<-Math.PI?1:0))+f,p={x:Math.cos(g),y:Math.sin(g)},v={x:Math.cos(m),y:Math.sin(m)},y=g<=0&&m>=0||g<=2*Math.PI&&2*Math.PI<=m,b=g<=.5*Math.PI&&.5*Math.PI<=m||g<=2.5*Math.PI&&2.5*Math.PI<=m,x=g<=-Math.PI&&-Math.PI<=m||g<=Math.PI&&Math.PI<=m,_=g<=.5*-Math.PI&&.5*-Math.PI<=m||g<=1.5*Math.PI&&1.5*Math.PI<=m,k=h/100,w={x:x?-1:Math.min(p.x*(p.x<0?1:k),v.x*(v.x<0?1:k)),y:_?-1:Math.min(p.y*(p.y<0?1:k),v.y*(v.y<0?1:k))},M={x:y?1:Math.max(p.x*(p.x>0?1:k),v.x*(v.x>0?1:k)),y:b?1:Math.max(p.y*(p.y>0?1:k),v.y*(v.y>0?1:k))},S={width:.5*(M.x-w.x),height:.5*(M.y-w.y)};u=Math.min(s/S.width,l/S.height),d={x:-.5*(M.x+w.x),y:-.5*(M.y+w.y)}}n.borderWidth=e.getMaxBorderWidth(c.data),n.outerRadius=Math.max((u-n.borderWidth)/2,0),n.innerRadius=Math.max(h?n.outerRadius/100*h:0,0),n.radiusLength=(n.outerRadius-n.innerRadius)/n.getVisibleDatasetCount(),n.offsetX=d.x*n.outerRadius,n.offsetY=d.y*n.outerRadius,c.total=e.calculateTotal(),e.outerRadius=n.outerRadius-n.radiusLength*e.getRingIndex(e.index),e.innerRadius=Math.max(e.outerRadius-n.radiusLength,0),r.each(c.data,function(n,i){e.updateElement(n,i,t)})},updateElement:function(t,e,n){var i=this,a=i.chart,o=a.chartArea,s=a.options,l=s.animation,u=(o.left+o.right)/2,d=(o.top+o.bottom)/2,c=s.rotation,h=s.rotation,f=i.getDataset(),g=n&&l.animateRotate?0:t.hidden?0:i.calculateCircumference(f.data[e])*(s.circumference/(2*Math.PI)),m=n&&l.animateScale?0:i.innerRadius,p=n&&l.animateScale?0:i.outerRadius,v=r.valueAtIndexOrDefault;r.extend(t,{_datasetIndex:i.index,_index:e,_model:{x:u+a.offsetX,y:d+a.offsetY,startAngle:c,endAngle:h,circumference:g,outerRadius:p,innerRadius:m,label:v(f.label,e,a.data.labels[e])}});var y=t._model;this.removeHoverStyle(t),n&&l.animateRotate||(y.startAngle=0===e?s.rotation:i.getMeta().data[e-1]._model.endAngle,y.endAngle=y.startAngle+y.circumference),t.pivot()},removeHoverStyle:function(e){t.DatasetController.prototype.removeHoverStyle.call(this,e,this.chart.options.elements.arc)},calculateTotal:function(){var t,e=this.getDataset(),n=this.getMeta(),i=0;return r.each(n.data,function(n,a){t=e.data[a],isNaN(t)||n.hidden||(i+=Math.abs(t))}),i},calculateCircumference:function(t){var e=this.getMeta().total;return e>0&&!isNaN(t)?2*Math.PI*(t/e):0},getMaxBorderWidth:function(t){for(var e,n,i=0,a=this.index,r=t.length,o=0;o(i=e>i?e:i)?n:i;return i}})}},{25:25,40:40,45:45}],18:[function(t,e,n){"use strict";var i=t(25),a=t(40),r=t(45);i._set("line",{showLines:!0,spanGaps:!1,hover:{mode:"label"},scales:{xAxes:[{type:"category",id:"x-axis-0"}],yAxes:[{type:"linear",id:"y-axis-0"}]}}),e.exports=function(t){function e(t,e){return r.valueOrDefault(t.showLine,e.showLines)}t.controllers.line=t.DatasetController.extend({datasetElementType:a.Line,dataElementType:a.Point,update:function(t){var n,i,a,o=this,s=o.getMeta(),l=s.dataset,u=s.data||[],d=o.chart.options,c=d.elements.line,h=o.getScaleForId(s.yAxisID),f=o.getDataset(),g=e(f,d);for(g&&(a=l.custom||{},void 0!==f.tension&&void 0===f.lineTension&&(f.lineTension=f.tension),l._scale=h,l._datasetIndex=o.index,l._children=u,l._model={spanGaps:f.spanGaps?f.spanGaps:d.spanGaps,tension:a.tension?a.tension:r.valueOrDefault(f.lineTension,c.tension),backgroundColor:a.backgroundColor?a.backgroundColor:f.backgroundColor||c.backgroundColor,borderWidth:a.borderWidth?a.borderWidth:f.borderWidth||c.borderWidth,borderColor:a.borderColor?a.borderColor:f.borderColor||c.borderColor,borderCapStyle:a.borderCapStyle?a.borderCapStyle:f.borderCapStyle||c.borderCapStyle,borderDash:a.borderDash?a.borderDash:f.borderDash||c.borderDash,borderDashOffset:a.borderDashOffset?a.borderDashOffset:f.borderDashOffset||c.borderDashOffset,borderJoinStyle:a.borderJoinStyle?a.borderJoinStyle:f.borderJoinStyle||c.borderJoinStyle,fill:a.fill?a.fill:void 0!==f.fill?f.fill:c.fill,steppedLine:a.steppedLine?a.steppedLine:r.valueOrDefault(f.steppedLine,c.stepped),cubicInterpolationMode:a.cubicInterpolationMode?a.cubicInterpolationMode:r.valueOrDefault(f.cubicInterpolationMode,c.cubicInterpolationMode)},l.pivot()),n=0,i=u.length;n');var n=t.data,i=n.datasets,a=n.labels;if(i.length)for(var r=0;r'),a[r]&&e.push(a[r]),e.push("");return e.push(""),e.join("")},legend:{labels:{generateLabels:function(t){var e=t.data;return e.labels.length&&e.datasets.length?e.labels.map(function(n,i){var a=t.getDatasetMeta(0),o=e.datasets[0],s=a.data[i].custom||{},l=r.valueAtIndexOrDefault,u=t.options.elements.arc;return{text:n,fillStyle:s.backgroundColor?s.backgroundColor:l(o.backgroundColor,i,u.backgroundColor),strokeStyle:s.borderColor?s.borderColor:l(o.borderColor,i,u.borderColor),lineWidth:s.borderWidth?s.borderWidth:l(o.borderWidth,i,u.borderWidth),hidden:isNaN(o.data[i])||a.data[i].hidden,index:i}}):[]}},onClick:function(t,e){var n,i,a,r=e.index,o=this.chart;for(n=0,i=(o.data.datasets||[]).length;n0&&!isNaN(t)?2*Math.PI/e:0}})}},{25:25,40:40,45:45}],20:[function(t,e,n){"use strict";var i=t(25),a=t(40),r=t(45);i._set("radar",{scale:{type:"radialLinear"},elements:{line:{tension:0}}}),e.exports=function(t){t.controllers.radar=t.DatasetController.extend({datasetElementType:a.Line,dataElementType:a.Point,linkScales:r.noop,update:function(t){var e=this,n=e.getMeta(),i=n.dataset,a=n.data,o=i.custom||{},s=e.getDataset(),l=e.chart.options.elements.line,u=e.chart.scale;void 0!==s.tension&&void 0===s.lineTension&&(s.lineTension=s.tension),r.extend(n.dataset,{_datasetIndex:e.index,_scale:u,_children:a,_loop:!0,_model:{tension:o.tension?o.tension:r.valueOrDefault(s.lineTension,l.tension),backgroundColor:o.backgroundColor?o.backgroundColor:s.backgroundColor||l.backgroundColor,borderWidth:o.borderWidth?o.borderWidth:s.borderWidth||l.borderWidth,borderColor:o.borderColor?o.borderColor:s.borderColor||l.borderColor,fill:o.fill?o.fill:void 0!==s.fill?s.fill:l.fill,borderCapStyle:o.borderCapStyle?o.borderCapStyle:s.borderCapStyle||l.borderCapStyle,borderDash:o.borderDash?o.borderDash:s.borderDash||l.borderDash,borderDashOffset:o.borderDashOffset?o.borderDashOffset:s.borderDashOffset||l.borderDashOffset,borderJoinStyle:o.borderJoinStyle?o.borderJoinStyle:s.borderJoinStyle||l.borderJoinStyle}}),n.dataset.pivot(),r.each(a,function(n,i){e.updateElement(n,i,t)},e),e.updateBezierControlPoints()},updateElement:function(t,e,n){var i=this,a=t.custom||{},o=i.getDataset(),s=i.chart.scale,l=i.chart.options.elements.point,u=s.getPointPositionForValue(e,o.data[e]);void 0!==o.radius&&void 0===o.pointRadius&&(o.pointRadius=o.radius),void 0!==o.hitRadius&&void 0===o.pointHitRadius&&(o.pointHitRadius=o.hitRadius),r.extend(t,{_datasetIndex:i.index,_index:e,_scale:s,_model:{x:n?s.xCenter:u.x,y:n?s.yCenter:u.y,tension:a.tension?a.tension:r.valueOrDefault(o.lineTension,i.chart.options.elements.line.tension),radius:a.radius?a.radius:r.valueAtIndexOrDefault(o.pointRadius,e,l.radius),backgroundColor:a.backgroundColor?a.backgroundColor:r.valueAtIndexOrDefault(o.pointBackgroundColor,e,l.backgroundColor),borderColor:a.borderColor?a.borderColor:r.valueAtIndexOrDefault(o.pointBorderColor,e,l.borderColor),borderWidth:a.borderWidth?a.borderWidth:r.valueAtIndexOrDefault(o.pointBorderWidth,e,l.borderWidth),pointStyle:a.pointStyle?a.pointStyle:r.valueAtIndexOrDefault(o.pointStyle,e,l.pointStyle),hitRadius:a.hitRadius?a.hitRadius:r.valueAtIndexOrDefault(o.pointHitRadius,e,l.hitRadius)}}),t._model.skip=a.skip?a.skip:isNaN(t._model.x)||isNaN(t._model.y)},updateBezierControlPoints:function(){var t=this.chart.chartArea,e=this.getMeta();r.each(e.data,function(n,i){var a=n._model,o=r.splineCurve(r.previousItem(e.data,i,!0)._model,a,r.nextItem(e.data,i,!0)._model,a.tension);a.controlPointPreviousX=Math.max(Math.min(o.previous.x,t.right),t.left),a.controlPointPreviousY=Math.max(Math.min(o.previous.y,t.bottom),t.top),a.controlPointNextX=Math.max(Math.min(o.next.x,t.right),t.left),a.controlPointNextY=Math.max(Math.min(o.next.y,t.bottom),t.top),n.pivot()})},setHoverStyle:function(t){var e=this.chart.data.datasets[t._datasetIndex],n=t.custom||{},i=t._index,a=t._model;a.radius=n.hoverRadius?n.hoverRadius:r.valueAtIndexOrDefault(e.pointHoverRadius,i,this.chart.options.elements.point.hoverRadius),a.backgroundColor=n.hoverBackgroundColor?n.hoverBackgroundColor:r.valueAtIndexOrDefault(e.pointHoverBackgroundColor,i,r.getHoverColor(a.backgroundColor)),a.borderColor=n.hoverBorderColor?n.hoverBorderColor:r.valueAtIndexOrDefault(e.pointHoverBorderColor,i,r.getHoverColor(a.borderColor)),a.borderWidth=n.hoverBorderWidth?n.hoverBorderWidth:r.valueAtIndexOrDefault(e.pointHoverBorderWidth,i,a.borderWidth)},removeHoverStyle:function(t){var e=this.chart.data.datasets[t._datasetIndex],n=t.custom||{},i=t._index,a=t._model,o=this.chart.options.elements.point;a.radius=n.radius?n.radius:r.valueAtIndexOrDefault(e.pointRadius,i,o.radius),a.backgroundColor=n.backgroundColor?n.backgroundColor:r.valueAtIndexOrDefault(e.pointBackgroundColor,i,o.backgroundColor),a.borderColor=n.borderColor?n.borderColor:r.valueAtIndexOrDefault(e.pointBorderColor,i,o.borderColor),a.borderWidth=n.borderWidth?n.borderWidth:r.valueAtIndexOrDefault(e.pointBorderWidth,i,o.borderWidth)}})}},{25:25,40:40,45:45}],21:[function(t,e,n){"use strict";t(25)._set("scatter",{hover:{mode:"single"},scales:{xAxes:[{id:"x-axis-1",type:"linear",position:"bottom"}],yAxes:[{id:"y-axis-1",type:"linear",position:"left"}]},showLines:!1,tooltips:{callbacks:{title:function(){return""},label:function(t){return"("+t.xLabel+", "+t.yLabel+")"}}}}),e.exports=function(t){t.controllers.scatter=t.controllers.line}},{25:25}],22:[function(t,e,n){"use strict";var i=t(25),a=t(26),r=t(45);i._set("global",{animation:{duration:1e3,easing:"easeOutQuart",onProgress:r.noop,onComplete:r.noop}}),e.exports=function(t){t.Animation=a.extend({chart:null,currentStep:0,numSteps:60,easing:"",render:null,onAnimationProgress:null,onAnimationComplete:null}),t.animationService={frameDuration:17,animations:[],dropFrames:0,request:null,addAnimation:function(t,e,n,i){var a,r,o=this.animations;for(e.chart=t,i||(t.animating=!0),a=0,r=o.length;a1&&(n=Math.floor(t.dropFrames),t.dropFrames=t.dropFrames%1),t.advance(1+n);var i=Date.now();t.dropFrames+=(i-e)/t.frameDuration,t.animations.length>0&&t.requestAnimationFrame()},advance:function(t){for(var e,n,i=this.animations,a=0;a=e.numSteps?(r.callback(e.onAnimationComplete,[e],n),n.animating=!1,i.splice(a,1)):++a}},Object.defineProperty(t.Animation.prototype,"animationObject",{get:function(){return this}}),Object.defineProperty(t.Animation.prototype,"chartInstance",{get:function(){return this.chart},set:function(t){this.chart=t}})}},{25:25,26:26,45:45}],23:[function(t,e,n){"use strict";var i=t(25),a=t(45),r=t(28),o=t(48);e.exports=function(t){function e(t){var e=(t=t||{}).data=t.data||{};return e.datasets=e.datasets||[],e.labels=e.labels||[],t.options=a.configMerge(i.global,i[t.type],t.options||{}),t}function n(t){var e=t.options;e.scale?t.scale.options=e.scale:e.scales&&e.scales.xAxes.concat(e.scales.yAxes).forEach(function(e){t.scales[e.id].options=e}),t.tooltip._options=e.tooltips}function s(t){return"top"===t||"bottom"===t}var l=t.plugins;t.types={},t.instances={},t.controllers={},a.extend(t.prototype,{construct:function(n,i){var r=this;i=e(i);var s=o.acquireContext(n,i),l=s&&s.canvas,u=l&&l.height,d=l&&l.width;r.id=a.uid(),r.ctx=s,r.canvas=l,r.config=i,r.width=d,r.height=u,r.aspectRatio=u?d/u:null,r.options=i.options,r._bufferedRender=!1,r.chart=r,r.controller=r,t.instances[r.id]=r,Object.defineProperty(r,"data",{get:function(){return r.config.data},set:function(t){r.config.data=t}}),s&&l?(r.initialize(),r.update()):console.error("Failed to create chart: can't acquire context from the given item")},initialize:function(){var t=this;return l.notify(t,"beforeInit"),a.retinaScale(t,t.options.devicePixelRatio),t.bindEvents(),t.options.responsive&&t.resize(!0),t.ensureScalesHaveIDs(),t.buildScales(),t.initToolTip(),l.notify(t,"afterInit"),t},clear:function(){return a.canvas.clear(this),this},stop:function(){return t.animationService.cancelAnimation(this),this},resize:function(t){var e=this,n=e.options,i=e.canvas,r=n.maintainAspectRatio&&e.aspectRatio||null,o=Math.max(0,Math.floor(a.getMaximumWidth(i))),s=Math.max(0,Math.floor(r?o/r:a.getMaximumHeight(i)));if((e.width!==o||e.height!==s)&&(i.width=e.width=o,i.height=e.height=s,i.style.width=o+"px",i.style.height=s+"px",a.retinaScale(e,n.devicePixelRatio),!t)){var u={width:o,height:s};l.notify(e,"resize",[u]),e.options.onResize&&e.options.onResize(e,u),e.stop(),e.update(e.options.responsiveAnimationDuration)}},ensureScalesHaveIDs:function(){var t=this.options,e=t.scales||{},n=t.scale;a.each(e.xAxes,function(t,e){t.id=t.id||"x-axis-"+e}),a.each(e.yAxes,function(t,e){t.id=t.id||"y-axis-"+e}),n&&(n.id=n.id||"scale")},buildScales:function(){var e=this,n=e.options,i=e.scales={},r=[];n.scales&&(r=r.concat((n.scales.xAxes||[]).map(function(t){return{options:t,dtype:"category",dposition:"bottom"}}),(n.scales.yAxes||[]).map(function(t){return{options:t,dtype:"linear",dposition:"left"}}))),n.scale&&r.push({options:n.scale,dtype:"radialLinear",isDefault:!0,dposition:"chartArea"}),a.each(r,function(n){var r=n.options,o=a.valueOrDefault(r.type,n.dtype),l=t.scaleService.getScaleConstructor(o);if(l){s(r.position)!==s(n.dposition)&&(r.position=n.dposition);var u=new l({id:r.id,options:r,ctx:e.ctx,chart:e});i[u.id]=u,u.mergeTicksOptions(),n.isDefault&&(e.scale=u)}}),t.scaleService.addScalesToLayout(this)},buildOrUpdateControllers:function(){var e=this,n=[],i=[];return a.each(e.data.datasets,function(a,r){var o=e.getDatasetMeta(r),s=a.type||e.config.type;if(o.type&&o.type!==s&&(e.destroyDatasetMeta(r),o=e.getDatasetMeta(r)),o.type=s,n.push(o.type),o.controller)o.controller.updateIndex(r);else{var l=t.controllers[o.type];if(void 0===l)throw new Error('"'+o.type+'" is not a chart type.');o.controller=new l(e,r),i.push(o.controller)}},e),i},resetElements:function(){var t=this;a.each(t.data.datasets,function(e,n){t.getDatasetMeta(n).controller.reset()},t)},reset:function(){this.resetElements(),this.tooltip.initialize()},update:function(t){var e=this;if(t&&"object"==typeof t||(t={duration:t,lazy:arguments[1]}),n(e),!1!==l.notify(e,"beforeUpdate")){e.tooltip._data=e.data;var i=e.buildOrUpdateControllers();a.each(e.data.datasets,function(t,n){e.getDatasetMeta(n).controller.buildOrUpdateElements()},e),e.updateLayout(),a.each(i,function(t){t.reset()}),e.updateDatasets(),e.tooltip.initialize(),e.lastActive=[],l.notify(e,"afterUpdate"),e._bufferedRender?e._bufferedRequest={duration:t.duration,easing:t.easing,lazy:t.lazy}:e.render(t)}},updateLayout:function(){var e=this;!1!==l.notify(e,"beforeLayout")&&(t.layoutService.update(this,this.width,this.height),l.notify(e,"afterScaleUpdate"),l.notify(e,"afterLayout"))},updateDatasets:function(){var t=this;if(!1!==l.notify(t,"beforeDatasetsUpdate")){for(var e=0,n=t.data.datasets.length;e=0;--n)e.isDatasetVisible(n)&&e.drawDataset(n,t);l.notify(e,"afterDatasetsDraw",[t])}},drawDataset:function(t,e){var n=this,i=n.getDatasetMeta(t),a={meta:i,index:t,easingValue:e};!1!==l.notify(n,"beforeDatasetDraw",[a])&&(i.controller.draw(e),l.notify(n,"afterDatasetDraw",[a]))},_drawTooltip:function(t){var e=this,n=e.tooltip,i={tooltip:n,easingValue:t};!1!==l.notify(e,"beforeTooltipDraw",[i])&&(n.draw(),l.notify(e,"afterTooltipDraw",[i]))},getElementAtEvent:function(t){return r.modes.single(this,t)},getElementsAtEvent:function(t){return r.modes.label(this,t,{intersect:!0})},getElementsAtXAxis:function(t){return r.modes["x-axis"](this,t,{intersect:!0})},getElementsAtEventForMode:function(t,e,n){var i=r.modes[e];return"function"==typeof i?i(this,t,n):[]},getDatasetAtEvent:function(t){return r.modes.dataset(this,t,{intersect:!0})},getDatasetMeta:function(t){var e=this,n=e.data.datasets[t];n._meta||(n._meta={});var i=n._meta[e.id];return i||(i=n._meta[e.id]={type:null,data:[],dataset:null,controller:null,hidden:null,xAxisID:null,yAxisID:null}),i},getVisibleDatasetCount:function(){for(var t=0,e=0,n=this.data.datasets.length;e0||(a.forEach(function(e){delete t[e]}),delete t._chartjs)}}var a=["push","pop","shift","splice","unshift"];t.DatasetController=function(t,e){this.initialize(t,e)},i.extend(t.DatasetController.prototype,{datasetElementType:null,dataElementType:null,initialize:function(t,e){var n=this;n.chart=t,n.index=e,n.linkScales(),n.addElements()},updateIndex:function(t){this.index=t},linkScales:function(){var t=this,e=t.getMeta(),n=t.getDataset();null===e.xAxisID&&(e.xAxisID=n.xAxisID||t.chart.options.scales.xAxes[0].id),null===e.yAxisID&&(e.yAxisID=n.yAxisID||t.chart.options.scales.yAxes[0].id)},getDataset:function(){return this.chart.data.datasets[this.index]},getMeta:function(){return this.chart.getDatasetMeta(this.index)},getScaleForId:function(t){return this.chart.scales[t]},reset:function(){this.update(!0)},destroy:function(){this._data&&n(this._data,this)},createMetaDataset:function(){var t=this,e=t.datasetElementType;return e&&new e({_chart:t.chart,_datasetIndex:t.index})},createMetaData:function(t){var e=this,n=e.dataElementType;return n&&new n({_chart:e.chart,_datasetIndex:e.index,_index:t})},addElements:function(){var t,e,n=this,i=n.getMeta(),a=n.getDataset().data||[],r=i.data;for(t=0,e=a.length;ti&&t.insertElements(i,a-i)},insertElements:function(t,e){for(var n=0;n=n[e].length&&n[e].push({}),!n[e][o].type||l.type&&l.type!==n[e][o].type?r.merge(n[e][o],[t.scaleService.getScaleDefaults(s),l]):r.merge(n[e][o],l)}else r._merger(e,n,i,a)}})},r.where=function(t,e){if(r.isArray(t)&&Array.prototype.filter)return t.filter(e);var n=[];return r.each(t,function(t){e(t)&&n.push(t)}),n},r.findIndex=Array.prototype.findIndex?function(t,e,n){return t.findIndex(e,n)}:function(t,e,n){n=void 0===n?t:n;for(var i=0,a=t.length;i=0;i--){var a=t[i];if(e(a))return a}},r.isNumber=function(t){return!isNaN(parseFloat(t))&&isFinite(t)},r.almostEquals=function(t,e,n){return Math.abs(t-e)t},r.max=function(t){return t.reduce(function(t,e){return isNaN(e)?t:Math.max(t,e)},Number.NEGATIVE_INFINITY)},r.min=function(t){return t.reduce(function(t,e){return isNaN(e)?t:Math.min(t,e)},Number.POSITIVE_INFINITY)},r.sign=Math.sign?function(t){return Math.sign(t)}:function(t){return 0==(t=+t)||isNaN(t)?t:t>0?1:-1},r.log10=Math.log10?function(t){return Math.log10(t)}:function(t){return Math.log(t)/Math.LN10},r.toRadians=function(t){return t*(Math.PI/180)},r.toDegrees=function(t){return t*(180/Math.PI)},r.getAngleFromPoint=function(t,e){var n=e.x-t.x,i=e.y-t.y,a=Math.sqrt(n*n+i*i),r=Math.atan2(i,n);return r<-.5*Math.PI&&(r+=2*Math.PI),{angle:r,distance:a}},r.distanceBetweenPoints=function(t,e){return Math.sqrt(Math.pow(e.x-t.x,2)+Math.pow(e.y-t.y,2))},r.aliasPixel=function(t){return t%2==0?0:.5},r.splineCurve=function(t,e,n,i){var a=t.skip?e:t,r=e,o=n.skip?e:n,s=Math.sqrt(Math.pow(r.x-a.x,2)+Math.pow(r.y-a.y,2)),l=Math.sqrt(Math.pow(o.x-r.x,2)+Math.pow(o.y-r.y,2)),u=s/(s+l),d=l/(s+l),c=i*(u=isNaN(u)?0:u),h=i*(d=isNaN(d)?0:d);return{previous:{x:r.x-c*(o.x-a.x),y:r.y-c*(o.y-a.y)},next:{x:r.x+h*(o.x-a.x),y:r.y+h*(o.y-a.y)}}},r.EPSILON=Number.EPSILON||1e-14,r.splineCurveMonotone=function(t){var e,n,i,a,o=(t||[]).map(function(t){return{model:t._model,deltaK:0,mK:0}}),s=o.length;for(e=0;e0?o[e-1]:null,(a=e0?o[e-1]:null,a=e=t.length-1?t[0]:t[e+1]:e>=t.length-1?t[t.length-1]:t[e+1]},r.previousItem=function(t,e,n){return n?e<=0?t[t.length-1]:t[e-1]:e<=0?t[0]:t[e-1]},r.niceNum=function(t,e){var n=Math.floor(r.log10(t)),i=t/Math.pow(10,n);return(e?i<1.5?1:i<3?2:i<7?5:10:i<=1?1:i<=2?2:i<=5?5:10)*Math.pow(10,n)},r.requestAnimFrame="undefined"==typeof window?function(t){t()}:window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(t){return window.setTimeout(t,1e3/60)},r.getRelativePosition=function(t,e){var n,i,a=t.originalEvent||t,o=t.currentTarget||t.srcElement,s=o.getBoundingClientRect(),l=a.touches;l&&l.length>0?(n=l[0].clientX,i=l[0].clientY):(n=a.clientX,i=a.clientY);var u=parseFloat(r.getStyle(o,"padding-left")),d=parseFloat(r.getStyle(o,"padding-top")),c=parseFloat(r.getStyle(o,"padding-right")),h=parseFloat(r.getStyle(o,"padding-bottom")),f=s.right-s.left-u-c,g=s.bottom-s.top-d-h;return n=Math.round((n-s.left-u)/f*o.width/e.currentDevicePixelRatio),i=Math.round((i-s.top-d)/g*o.height/e.currentDevicePixelRatio),{x:n,y:i}},r.getConstraintWidth=function(t){return o(t,"max-width","clientWidth")},r.getConstraintHeight=function(t){return o(t,"max-height","clientHeight")},r.getMaximumWidth=function(t){var e=t.parentNode;if(!e)return t.clientWidth;var n=parseInt(r.getStyle(e,"padding-left"),10),i=parseInt(r.getStyle(e,"padding-right"),10),a=e.clientWidth-n-i,o=r.getConstraintWidth(t);return isNaN(o)?a:Math.min(a,o)},r.getMaximumHeight=function(t){var e=t.parentNode;if(!e)return t.clientHeight;var n=parseInt(r.getStyle(e,"padding-top"),10),i=parseInt(r.getStyle(e,"padding-bottom"),10),a=e.clientHeight-n-i,o=r.getConstraintHeight(t);return isNaN(o)?a:Math.min(a,o)},r.getStyle=function(t,e){return t.currentStyle?t.currentStyle[e]:document.defaultView.getComputedStyle(t,null).getPropertyValue(e)},r.retinaScale=function(t,e){var n=t.currentDevicePixelRatio=e||window.devicePixelRatio||1;if(1!==n){var i=t.canvas,a=t.height,r=t.width;i.height=a*n,i.width=r*n,t.ctx.scale(n,n),i.style.height=a+"px",i.style.width=r+"px"}},r.fontString=function(t,e,n){return e+" "+t+"px "+n},r.longestText=function(t,e,n,i){var a=(i=i||{}).data=i.data||{},o=i.garbageCollect=i.garbageCollect||[];i.font!==e&&(a=i.data={},o=i.garbageCollect=[],i.font=e),t.font=e;var s=0;r.each(n,function(e){void 0!==e&&null!==e&&!0!==r.isArray(e)?s=r.measureText(t,a,o,s,e):r.isArray(e)&&r.each(e,function(e){void 0===e||null===e||r.isArray(e)||(s=r.measureText(t,a,o,s,e))})});var l=o.length/2;if(l>n.length){for(var u=0;ui&&(i=r),i},r.numberOfLabelLines=function(t){var e=1;return r.each(t,function(t){r.isArray(t)&&t.length>e&&(e=t.length)}),e},r.color=i?function(t){return t instanceof CanvasGradient&&(t=a.global.defaultColor),i(t)}:function(t){return console.error("Color.js not found!"),t},r.getHoverColor=function(t){return t instanceof CanvasPattern?t:r.color(t).saturate(.5).darken(.1).rgbString()}}},{2:2,25:25,45:45}],28:[function(t,e,n){"use strict";function i(t,e){return t.native?{x:t.x,y:t.y}:u.getRelativePosition(t,e)}function a(t,e){var n,i,a,r,o;for(i=0,r=t.data.datasets.length;i0&&(u=t.getDatasetMeta(u[0]._datasetIndex).data),u},"x-axis":function(t,e){return l(t,e,{intersect:!1})},point:function(t,e){return r(t,i(e,t))},nearest:function(t,e,n){var a=i(e,t);n.axis=n.axis||"xy";var r=s(n.axis),l=o(t,a,n.intersect,r);return l.length>1&&l.sort(function(t,e){var n=t.getArea()-e.getArea();return 0===n&&(n=t._datasetIndex-e._datasetIndex),n}),l.slice(0,1)},x:function(t,e,n){var r=i(e,t),o=[],s=!1;return a(t,function(t){t.inXRange(r.x)&&o.push(t),t.inRange(r.x,r.y)&&(s=!0)}),n.intersect&&!s&&(o=[]),o},y:function(t,e,n){var r=i(e,t),o=[],s=!1;return a(t,function(t){t.inYRange(r.y)&&o.push(t),t.inRange(r.x,r.y)&&(s=!0)}),n.intersect&&!s&&(o=[]),o}}}},{45:45}],29:[function(t,e,n){"use strict";t(25)._set("global",{responsive:!0,responsiveAnimationDuration:0,maintainAspectRatio:!0,events:["mousemove","mouseout","click","touchstart","touchmove"],hover:{onHover:null,mode:"nearest",intersect:!0,animationDuration:400},onClick:null,defaultColor:"rgba(0,0,0,0.1)",defaultFontColor:"#666",defaultFontFamily:"'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",defaultFontSize:12,defaultFontStyle:"normal",showLines:!0,elements:{},layout:{padding:{top:0,right:0,bottom:0,left:0}}}),e.exports=function(){var t=function(t,e){return this.construct(t,e),this};return t.Chart=t,t}},{25:25}],30:[function(t,e,n){"use strict";var i=t(45);e.exports=function(t){function e(t,e){return i.where(t,function(t){return t.position===e})}function n(t,e){t.forEach(function(t,e){return t._tmpIndex_=e,t}),t.sort(function(t,n){var i=e?n:t,a=e?t:n;return i.weight===a.weight?i._tmpIndex_-a._tmpIndex_:i.weight-a.weight}),t.forEach(function(t){delete t._tmpIndex_})}t.layoutService={defaults:{},addBox:function(t,e){t.boxes||(t.boxes=[]),e.fullWidth=e.fullWidth||!1,e.position=e.position||"top",e.weight=e.weight||0,t.boxes.push(e)},removeBox:function(t,e){var n=t.boxes?t.boxes.indexOf(e):-1;-1!==n&&t.boxes.splice(n,1)},configure:function(t,e,n){for(var i,a=["fullWidth","position","weight"],r=a.length,o=0;oh&&lt.maxHeight){l--;break}l++,c=u*d}t.labelRotation=l},afterCalculateTickRotation:function(){s.callback(this.options.afterCalculateTickRotation,[this])},beforeFit:function(){s.callback(this.options.beforeFit,[this])},fit:function(){var t=this,a=t.minSize={width:0,height:0},r=i(t._ticks),o=t.options,u=o.ticks,d=o.scaleLabel,c=o.gridLines,h=o.display,f=t.isHorizontal(),g=n(u),m=o.gridLines.tickMarkLength;if(a.width=f?t.isFullWidth()?t.maxWidth-t.margins.left-t.margins.right:t.maxWidth:h&&c.drawTicks?m:0,a.height=f?h&&c.drawTicks?m:0:t.maxHeight,d.display&&h){var p=l(d)+s.options.toPadding(d.padding).height;f?a.height+=p:a.width+=p}if(u.display&&h){var v=s.longestText(t.ctx,g.font,r,t.longestTextCache),y=s.numberOfLabelLines(r),b=.5*g.size,x=t.options.ticks.padding;if(f){t.longestLabelWidth=v;var _=s.toRadians(t.labelRotation),k=Math.cos(_),w=Math.sin(_)*v+g.size*y+b*(y-1)+b;a.height=Math.min(t.maxHeight,a.height+w+x),t.ctx.font=g.font;var M=e(t.ctx,r[0],g.font),S=e(t.ctx,r[r.length-1],g.font);0!==t.labelRotation?(t.paddingLeft="bottom"===o.position?k*M+3:k*b+3,t.paddingRight="bottom"===o.position?k*b+3:k*S+3):(t.paddingLeft=M/2+3,t.paddingRight=S/2+3)}else u.mirror?v=0:v+=x+b,a.width=Math.min(t.maxWidth,a.width+v),t.paddingTop=g.size/2,t.paddingBottom=g.size/2}t.handleMargins(),t.width=a.width,t.height=a.height},handleMargins:function(){var t=this;t.margins&&(t.paddingLeft=Math.max(t.paddingLeft-t.margins.left,0),t.paddingTop=Math.max(t.paddingTop-t.margins.top,0),t.paddingRight=Math.max(t.paddingRight-t.margins.right,0),t.paddingBottom=Math.max(t.paddingBottom-t.margins.bottom,0))},afterFit:function(){s.callback(this.options.afterFit,[this])},isHorizontal:function(){return"top"===this.options.position||"bottom"===this.options.position},isFullWidth:function(){return this.options.fullWidth},getRightValue:function(t){if(s.isNullOrUndef(t))return NaN;if("number"==typeof t&&!isFinite(t))return NaN;if(t)if(this.isHorizontal()){if(void 0!==t.x)return this.getRightValue(t.x)}else if(void 0!==t.y)return this.getRightValue(t.y);return t},getLabelForIndex:s.noop,getPixelForValue:s.noop,getValueForPixel:s.noop,getPixelForTick:function(t){var e=this,n=e.options.offset;if(e.isHorizontal()){var i=(e.width-(e.paddingLeft+e.paddingRight))/Math.max(e._ticks.length-(n?0:1),1),a=i*t+e.paddingLeft;n&&(a+=i/2);var r=e.left+Math.round(a);return r+=e.isFullWidth()?e.margins.left:0}var o=e.height-(e.paddingTop+e.paddingBottom);return e.top+t*(o/(e._ticks.length-1))},getPixelForDecimal:function(t){var e=this;if(e.isHorizontal()){var n=(e.width-(e.paddingLeft+e.paddingRight))*t+e.paddingLeft,i=e.left+Math.round(n);return i+=e.isFullWidth()?e.margins.left:0}return e.top+t*e.height},getBasePixel:function(){return this.getPixelForValue(this.getBaseValue())},getBaseValue:function(){var t=this,e=t.min,n=t.max;return t.beginAtZero?0:e<0&&n<0?n:e>0&&n>0?e:0},_autoSkip:function(t){var e,n,i,a,r=this,o=r.isHorizontal(),l=r.options.ticks.minor,u=t.length,d=s.toRadians(r.labelRotation),c=Math.cos(d),h=r.longestLabelWidth*c,f=[];for(l.maxTicksLimit&&(a=l.maxTicksLimit),o&&(e=!1,(h+l.autoSkipPadding)*u>r.width-(r.paddingLeft+r.paddingRight)&&(e=1+Math.floor((h+l.autoSkipPadding)*u/(r.width-(r.paddingLeft+r.paddingRight)))),a&&u>a&&(e=Math.max(e,Math.floor(u/a)))),n=0;n1&&n%e>0||n%e==0&&n+e>=u)&&n!==u-1&&delete i.label,f.push(i);return f},draw:function(t){var e=this,i=e.options;if(i.display){var o=e.ctx,u=r.global,d=i.ticks.minor,c=i.ticks.major||d,h=i.gridLines,f=i.scaleLabel,g=0!==e.labelRotation,m=e.isHorizontal(),p=d.autoSkip?e._autoSkip(e.getTicks()):e.getTicks(),v=s.valueOrDefault(d.fontColor,u.defaultFontColor),y=n(d),b=s.valueOrDefault(c.fontColor,u.defaultFontColor),x=n(c),_=h.drawTicks?h.tickMarkLength:0,k=s.valueOrDefault(f.fontColor,u.defaultFontColor),w=n(f),M=s.options.toPadding(f.padding),S=s.toRadians(e.labelRotation),D=[],C="right"===i.position?e.left:e.right-_,P="right"===i.position?e.left+_:e.right,T="bottom"===i.position?e.top:e.bottom-_,A="bottom"===i.position?e.top+_:e.bottom;if(s.each(p,function(n,r){if(!s.isNullOrUndef(n.label)){var o,l,c,f,v=n.label;r===e.zeroLineIndex&&i.offset===h.offsetGridLines?(o=h.zeroLineWidth,l=h.zeroLineColor,c=h.zeroLineBorderDash,f=h.zeroLineBorderDashOffset):(o=s.valueAtIndexOrDefault(h.lineWidth,r),l=s.valueAtIndexOrDefault(h.color,r),c=s.valueOrDefault(h.borderDash,u.borderDash),f=s.valueOrDefault(h.borderDashOffset,u.borderDashOffset));var y,b,x,k,w,M,I,O,F,R,L="middle",W="middle",Y=d.padding;if(m){var N=_+Y;"bottom"===i.position?(W=g?"middle":"top",L=g?"right":"center",R=e.top+N):(W=g?"middle":"bottom",L=g?"left":"center",R=e.bottom-N);var z=a(e,r,h.offsetGridLines&&p.length>1);z1);H0)n=t.stepSize;else{var r=i.niceNum(e.max-e.min,!1);n=i.niceNum(r/(t.maxTicks-1),!0)}var o=Math.floor(e.min/n)*n,s=Math.ceil(e.max/n)*n;t.min&&t.max&&t.stepSize&&i.almostWhole((t.max-t.min)/t.stepSize,n/1e3)&&(o=t.min,s=t.max);var l=(s-o)/n;l=i.almostEquals(l,Math.round(l),n/1e3)?Math.round(l):Math.ceil(l),a.push(void 0!==t.min?t.min:o);for(var u=1;u3?n[2]-n[1]:n[1]-n[0];Math.abs(a)>1&&t!==Math.floor(t)&&(a=t-Math.floor(t));var r=i.log10(Math.abs(a)),o="";if(0!==t){var s=-1*Math.floor(r);s=Math.max(Math.min(s,20),0),o=t.toFixed(s)}else o="0";return o},logarithmic:function(t,e,n){var a=t/Math.pow(10,Math.floor(i.log10(t)));return 0===t?"0":1===a||2===a||5===a||0===e||e===n.length-1?t.toExponential():""}}}},{45:45}],35:[function(t,e,n){"use strict";var i=t(25),a=t(26),r=t(45);i._set("global",{tooltips:{enabled:!0,custom:null,mode:"nearest",position:"average",intersect:!0,backgroundColor:"rgba(0,0,0,0.8)",titleFontStyle:"bold",titleSpacing:2,titleMarginBottom:6,titleFontColor:"#fff",titleAlign:"left",bodySpacing:2,bodyFontColor:"#fff",bodyAlign:"left",footerFontStyle:"bold",footerSpacing:2,footerMarginTop:6,footerFontColor:"#fff",footerAlign:"left",yPadding:6,xPadding:6,caretPadding:2,caretSize:5,cornerRadius:6,multiKeyBackground:"#fff",displayColors:!0,borderColor:"rgba(0,0,0,0)",borderWidth:0,callbacks:{beforeTitle:r.noop,title:function(t,e){var n="",i=e.labels,a=i?i.length:0;if(t.length>0){var r=t[0];r.xLabel?n=r.xLabel:a>0&&r.indexi.height-e.height&&(o="bottom");var s,l,u,d,c,h=(a.left+a.right)/2,f=(a.top+a.bottom)/2;"center"===o?(s=function(t){return t<=h},l=function(t){return t>h}):(s=function(t){return t<=e.width/2},l=function(t){return t>=i.width-e.width/2}),u=function(t){return t+e.width>i.width},d=function(t){return t-e.width<0},c=function(t){return t<=f?"top":"bottom"},s(n.x)?(r="left",u(n.x)&&(r="center",o=c(n.y))):l(n.x)&&(r="right",d(n.x)&&(r="center",o=c(n.y)));var g=t._options;return{xAlign:g.xAlign?g.xAlign:r,yAlign:g.yAlign?g.yAlign:o}}function d(t,e,n){var i=t.x,a=t.y,r=t.caretSize,o=t.caretPadding,s=t.cornerRadius,l=n.xAlign,u=n.yAlign,d=r+o,c=s+o;return"right"===l?i-=e.width:"center"===l&&(i-=e.width/2),"top"===u?a+=d:a-="bottom"===u?e.height+d:e.height/2,"center"===u?"left"===l?i+=d:"right"===l&&(i-=d):"left"===l?i-=c:"right"===l&&(i+=c),{x:i,y:a}}t.Tooltip=a.extend({initialize:function(){this._model=s(this._options),this._lastActive=[]},getTitle:function(){var t=this,e=t._options.callbacks,i=e.beforeTitle.apply(t,arguments),a=e.title.apply(t,arguments),r=e.afterTitle.apply(t,arguments),o=[];return o=n(o,i),o=n(o,a),o=n(o,r)},getBeforeBody:function(){var t=this._options.callbacks.beforeBody.apply(this,arguments);return r.isArray(t)?t:void 0!==t?[t]:[]},getBody:function(t,e){var i=this,a=i._options.callbacks,o=[];return r.each(t,function(t){var r={before:[],lines:[],after:[]};n(r.before,a.beforeLabel.call(i,t,e)),n(r.lines,a.label.call(i,t,e)),n(r.after,a.afterLabel.call(i,t,e)),o.push(r)}),o},getAfterBody:function(){var t=this._options.callbacks.afterBody.apply(this,arguments);return r.isArray(t)?t:void 0!==t?[t]:[]},getFooter:function(){var t=this,e=t._options.callbacks,i=e.beforeFooter.apply(t,arguments),a=e.footer.apply(t,arguments),r=e.afterFooter.apply(t,arguments),o=[];return o=n(o,i),o=n(o,a),o=n(o,r)},update:function(e){var n,i,a=this,c=a._options,h=a._model,f=a._model=s(c),g=a._active,m=a._data,p={xAlign:h.xAlign,yAlign:h.yAlign},v={x:h.x,y:h.y},y={width:h.width,height:h.height},b={x:h.caretX,y:h.caretY};if(g.length){f.opacity=1;var x=[],_=[];b=t.Tooltip.positioners[c.position].call(a,g,a._eventPosition);var k=[];for(n=0,i=g.length;n0&&i.stroke()},draw:function(){var t=this._chart.ctx,e=this._view;if(0!==e.opacity){var n={width:e.width,height:e.height},i={x:e.x,y:e.y},a=Math.abs(e.opacity<.001)?0:e.opacity,r=e.title.length||e.beforeBody.length||e.body.length||e.afterBody.length||e.footer.length;this._options.enabled&&r&&(this.drawBackground(i,e,t,n,a),i.x+=e.xPadding,i.y+=e.yPadding,this.drawTitle(i,e,t,a),this.drawBody(i,e,t,a),this.drawFooter(i,e,t,a))}},handleEvent:function(t){var e=this,n=e._options,i=!1;if(e._lastActive=e._lastActive||[],"mouseout"===t.type?e._active=[]:e._active=e._chart.getElementsAtEventForMode(t,n.mode,n),!(i=!r.arrayEquals(e._active,e._lastActive)))return!1;if(e._lastActive=e._active,n.enabled||n.custom){e._eventPosition={x:t.x,y:t.y};var a=e._model;e.update(!0),e.pivot(),i|=a.x!==e._model.x||a.y!==e._model.y}return i}}),t.Tooltip.positioners={average:function(t){if(!t.length)return!1;var e,n,i=0,a=0,r=0;for(e=0,n=t.length;el;)a-=2*Math.PI;for(;a=s&&a<=l,d=o>=n.innerRadius&&o<=n.outerRadius;return u&&d}return!1},getCenterPoint:function(){var t=this._view,e=(t.startAngle+t.endAngle)/2,n=(t.innerRadius+t.outerRadius)/2;return{x:t.x+Math.cos(e)*n,y:t.y+Math.sin(e)*n}},getArea:function(){var t=this._view;return Math.PI*((t.endAngle-t.startAngle)/(2*Math.PI))*(Math.pow(t.outerRadius,2)-Math.pow(t.innerRadius,2))},tooltipPosition:function(){var t=this._view,e=t.startAngle+(t.endAngle-t.startAngle)/2,n=(t.outerRadius-t.innerRadius)/2+t.innerRadius;return{x:t.x+Math.cos(e)*n,y:t.y+Math.sin(e)*n}},draw:function(){var t=this._chart.ctx,e=this._view,n=e.startAngle,i=e.endAngle;t.beginPath(),t.arc(e.x,e.y,e.outerRadius,n,i),t.arc(e.x,e.y,e.innerRadius,i,n,!0),t.closePath(),t.strokeStyle=e.borderColor,t.lineWidth=e.borderWidth,t.fillStyle=e.backgroundColor,t.fill(),t.lineJoin="bevel",e.borderWidth&&t.stroke()}})},{25:25,26:26,45:45}],37:[function(t,e,n){"use strict";var i=t(25),a=t(26),r=t(45),o=i.global;i._set("global",{elements:{line:{tension:.4,backgroundColor:o.defaultColor,borderWidth:3,borderColor:o.defaultColor,borderCapStyle:"butt",borderDash:[],borderDashOffset:0,borderJoinStyle:"miter",capBezierPoints:!0,fill:!0}}}),e.exports=a.extend({draw:function(){var t,e,n,i,a=this,s=a._view,l=a._chart.ctx,u=s.spanGaps,d=a._children.slice(),c=o.elements.line,h=-1;for(a._loop&&d.length&&d.push(d[0]),l.save(),l.lineCap=s.borderCapStyle||c.borderCapStyle,l.setLineDash&&l.setLineDash(s.borderDash||c.borderDash),l.lineDashOffset=s.borderDashOffset||c.borderDashOffset,l.lineJoin=s.borderJoinStyle||c.borderJoinStyle,l.lineWidth=s.borderWidth||c.borderWidth,l.strokeStyle=s.borderColor||o.defaultColor,l.beginPath(),h=-1,t=0;te?1:-1,o=1,s=u.borderSkipped||"left"):(e=u.x-u.width/2,n=u.x+u.width/2,i=u.y,r=1,o=(a=u.base)>i?1:-1,s=u.borderSkipped||"bottom"),d){var c=Math.min(Math.abs(e-n),Math.abs(i-a)),h=(d=d>c?c:d)/2,f=e+("left"!==s?h*r:0),g=n+("right"!==s?-h*r:0),m=i+("top"!==s?h*o:0),p=a+("bottom"!==s?-h*o:0);f!==g&&(i=m,a=p),m!==p&&(e=f,n=g)}l.beginPath(),l.fillStyle=u.backgroundColor,l.strokeStyle=u.borderColor,l.lineWidth=d;var v=[[e,a],[e,i],[n,i],[n,a]],y=["bottom","left","top","right"].indexOf(s,0);-1===y&&(y=0);var b=t(0);l.moveTo(b[0],b[1]);for(var x=1;x<4;x++)b=t(x),l.lineTo(b[0],b[1]);l.fill(),d&&l.stroke()},height:function(){var t=this._view;return t.base-t.y},inRange:function(t,e){var n=!1;if(this._view){var i=a(this);n=t>=i.left&&t<=i.right&&e>=i.top&&e<=i.bottom}return n},inLabelRange:function(t,e){var n=this;if(!n._view)return!1;var r=a(n);return i(n)?t>=r.left&&t<=r.right:e>=r.top&&e<=r.bottom},inXRange:function(t){var e=a(this);return t>=e.left&&t<=e.right},inYRange:function(t){var e=a(this);return t>=e.top&&t<=e.bottom},getCenterPoint:function(){var t,e,n=this._view;return i(this)?(t=n.x,e=(n.y+n.base)/2):(t=(n.x+n.base)/2,e=n.y),{x:t,y:e}},getArea:function(){var t=this._view;return t.width*Math.abs(t.y-t.base)},tooltipPosition:function(){var t=this._view;return{x:t.x,y:t.y}}})},{25:25,26:26}],40:[function(t,e,n){"use strict";e.exports={},e.exports.Arc=t(36),e.exports.Line=t(37),e.exports.Point=t(38),e.exports.Rectangle=t(39)},{36:36,37:37,38:38,39:39}],41:[function(t,e,n){"use strict";var i=t(42),n=e.exports={clear:function(t){t.ctx.clearRect(0,0,t.width,t.height)},roundedRect:function(t,e,n,i,a,r){if(r){var o=Math.min(r,i/2),s=Math.min(r,a/2);t.moveTo(e+o,n),t.lineTo(e+i-o,n),t.quadraticCurveTo(e+i,n,e+i,n+s),t.lineTo(e+i,n+a-s),t.quadraticCurveTo(e+i,n+a,e+i-o,n+a),t.lineTo(e+o,n+a),t.quadraticCurveTo(e,n+a,e,n+a-s),t.lineTo(e,n+s),t.quadraticCurveTo(e,n,e+o,n)}else t.rect(e,n,i,a)},drawPoint:function(t,e,n,i,a){var r,o,s,l,u,d;if(!e||"object"!=typeof e||"[object HTMLImageElement]"!==(r=e.toString())&&"[object HTMLCanvasElement]"!==r){if(!(isNaN(n)||n<=0)){switch(e){default:t.beginPath(),t.arc(i,a,n,0,2*Math.PI),t.closePath(),t.fill();break;case"triangle":t.beginPath(),u=(o=3*n/Math.sqrt(3))*Math.sqrt(3)/2,t.moveTo(i-o/2,a+u/3),t.lineTo(i+o/2,a+u/3),t.lineTo(i,a-2*u/3),t.closePath(),t.fill();break;case"rect":d=1/Math.SQRT2*n,t.beginPath(),t.fillRect(i-d,a-d,2*d,2*d),t.strokeRect(i-d,a-d,2*d,2*d);break;case"rectRounded":var c=n/Math.SQRT2,h=i-c,f=a-c,g=Math.SQRT2*n;t.beginPath(),this.roundedRect(t,h,f,g,g,n/2),t.closePath(),t.fill();break;case"rectRot":d=1/Math.SQRT2*n,t.beginPath(),t.moveTo(i-d,a),t.lineTo(i,a+d),t.lineTo(i+d,a),t.lineTo(i,a-d),t.closePath(),t.fill();break;case"cross":t.beginPath(),t.moveTo(i,a+n),t.lineTo(i,a-n),t.moveTo(i-n,a),t.lineTo(i+n,a),t.closePath();break;case"crossRot":t.beginPath(),s=Math.cos(Math.PI/4)*n,l=Math.sin(Math.PI/4)*n,t.moveTo(i-s,a-l),t.lineTo(i+s,a+l),t.moveTo(i-s,a+l),t.lineTo(i+s,a-l),t.closePath();break;case"star":t.beginPath(),t.moveTo(i,a+n),t.lineTo(i,a-n),t.moveTo(i-n,a),t.lineTo(i+n,a),s=Math.cos(Math.PI/4)*n,l=Math.sin(Math.PI/4)*n,t.moveTo(i-s,a-l),t.lineTo(i+s,a+l),t.moveTo(i-s,a+l),t.lineTo(i+s,a-l),t.closePath();break;case"line":t.beginPath(),t.moveTo(i-n,a),t.lineTo(i+n,a),t.closePath();break;case"dash":t.beginPath(),t.moveTo(i,a),t.lineTo(i+n,a),t.closePath()}t.stroke()}}else t.drawImage(e,i-e.width/2,a-e.height/2,e.width,e.height)},clipArea:function(t,e){t.save(),t.beginPath(),t.rect(e.left,e.top,e.right-e.left,e.bottom-e.top),t.clip()},unclipArea:function(t){t.restore()},lineTo:function(t,e,n,i){if(n.steppedLine)return"after"===n.steppedLine&&!i||"after"!==n.steppedLine&&i?t.lineTo(e.x,n.y):t.lineTo(n.x,e.y),void t.lineTo(n.x,n.y);n.tension?t.bezierCurveTo(i?e.controlPointPreviousX:e.controlPointNextX,i?e.controlPointPreviousY:e.controlPointNextY,i?n.controlPointNextX:n.controlPointPreviousX,i?n.controlPointNextY:n.controlPointPreviousY,n.x,n.y):t.lineTo(n.x,n.y)}};i.clear=n.clear,i.drawRoundedRectangle=function(t){t.beginPath(),n.roundedRect.apply(n,arguments),t.closePath()}},{42:42}],42:[function(t,e,n){"use strict";var i={noop:function(){},uid:function(){var t=0;return function(){return t++}}(),isNullOrUndef:function(t){return null===t||void 0===t},isArray:Array.isArray?Array.isArray:function(t){return"[object Array]"===Object.prototype.toString.call(t)},isObject:function(t){return null!==t&&"[object Object]"===Object.prototype.toString.call(t)},valueOrDefault:function(t,e){return void 0===t?e:t},valueAtIndexOrDefault:function(t,e,n){return i.valueOrDefault(i.isArray(t)?t[e]:t,n)},callback:function(t,e,n){if(t&&"function"==typeof t.call)return t.apply(n,e)},each:function(t,e,n,a){var r,o,s;if(i.isArray(t))if(o=t.length,a)for(r=o-1;r>=0;r--)e.call(n,t[r],r);else for(r=0;r=1?t:-(Math.sqrt(1-t*t)-1)},easeOutCirc:function(t){return Math.sqrt(1-(t-=1)*t)},easeInOutCirc:function(t){return(t/=.5)<1?-.5*(Math.sqrt(1-t*t)-1):.5*(Math.sqrt(1-(t-=2)*t)+1)},easeInElastic:function(t){var e=1.70158,n=0,i=1;return 0===t?0:1===t?1:(n||(n=.3),i<1?(i=1,e=n/4):e=n/(2*Math.PI)*Math.asin(1/i),-i*Math.pow(2,10*(t-=1))*Math.sin((t-e)*(2*Math.PI)/n))},easeOutElastic:function(t){var e=1.70158,n=0,i=1;return 0===t?0:1===t?1:(n||(n=.3),i<1?(i=1,e=n/4):e=n/(2*Math.PI)*Math.asin(1/i),i*Math.pow(2,-10*t)*Math.sin((t-e)*(2*Math.PI)/n)+1)},easeInOutElastic:function(t){var e=1.70158,n=0,i=1;return 0===t?0:2==(t/=.5)?1:(n||(n=.45),i<1?(i=1,e=n/4):e=n/(2*Math.PI)*Math.asin(1/i),t<1?i*Math.pow(2,10*(t-=1))*Math.sin((t-e)*(2*Math.PI)/n)*-.5:i*Math.pow(2,-10*(t-=1))*Math.sin((t-e)*(2*Math.PI)/n)*.5+1)},easeInBack:function(t){var e=1.70158;return t*t*((e+1)*t-e)},easeOutBack:function(t){var e=1.70158;return(t-=1)*t*((e+1)*t+e)+1},easeInOutBack:function(t){var e=1.70158;return(t/=.5)<1?t*t*((1+(e*=1.525))*t-e)*.5:.5*((t-=2)*t*((1+(e*=1.525))*t+e)+2)},easeInBounce:function(t){return 1-a.easeOutBounce(1-t)},easeOutBounce:function(t){return t<1/2.75?7.5625*t*t:t<2/2.75?7.5625*(t-=1.5/2.75)*t+.75:t<2.5/2.75?7.5625*(t-=2.25/2.75)*t+.9375:7.5625*(t-=2.625/2.75)*t+.984375},easeInOutBounce:function(t){return t<.5?.5*a.easeInBounce(2*t):.5*a.easeOutBounce(2*t-1)+.5}};e.exports={effects:a},i.easingEffects=a},{42:42}],44:[function(t,e,n){"use strict";var i=t(42);e.exports={toLineHeight:function(t,e){var n=(""+t).match(/^(normal|(\d+(?:\.\d+)?)(px|em|%)?)$/);if(!n||"normal"===n[1])return 1.2*e;switch(t=+n[2],n[3]){case"px":return t;case"%":t/=100}return e*t},toPadding:function(t){var e,n,a,r;return i.isObject(t)?(e=+t.top||0,n=+t.right||0,a=+t.bottom||0,r=+t.left||0):e=n=a=r=+t||0,{top:e,right:n,bottom:a,left:r,height:e+a,width:r+n}},resolve:function(t,e,n){var a,r,o;for(a=0,r=t.length;a
';var a=e.childNodes[0],o=e.childNodes[1];e._reset=function(){a.scrollLeft=1e6,a.scrollTop=1e6,o.scrollLeft=1e6,o.scrollTop=1e6};var s=function(){e._reset(),t()};return r(a,"scroll",s.bind(a,"expand")),r(o,"scroll",s.bind(o,"shrink")),e}function c(t,e){var n=t[v]||(t[v]={}),i=n.renderProxy=function(t){t.animationName===x&&e()};p.each(_,function(e){r(t,e,i)}),n.reflow=!!t.offsetParent,t.classList.add(b)}function h(t){var e=t[v]||{},n=e.renderProxy;n&&(p.each(_,function(e){o(t,e,n)}),delete e.renderProxy),t.classList.remove(b)}function f(t,e,n){var i=t[v]||(t[v]={}),a=i.resizer=d(u(function(){if(i.resizer)return e(s("resize",n))}));c(t,function(){if(i.resizer){var e=t.parentNode;e&&e!==a.parentNode&&e.insertBefore(a,e.firstChild),a._reset()}})}function g(t){var e=t[v]||{},n=e.resizer;delete e.resizer,h(t),n&&n.parentNode&&n.parentNode.removeChild(n)}function m(t,e){var n=t._style||document.createElement("style");t._style||(t._style=n,e="/* Chart.js */\n"+e,n.setAttribute("type","text/css"),document.getElementsByTagName("head")[0].appendChild(n)),n.appendChild(document.createTextNode(e))}var p=t(45),v="$chartjs",y="chartjs-",b=y+"render-monitor",x=y+"render-animation",_=["animationstart","webkitAnimationStart"],k={touchstart:"mousedown",touchmove:"mousemove",touchend:"mouseup",pointerenter:"mouseenter",pointerdown:"mousedown",pointermove:"mousemove",pointerup:"mouseup",pointerleave:"mouseout",pointerout:"mouseout"},w=!!function(){var t=!1;try{var e=Object.defineProperty({},"passive",{get:function(){t=!0}});window.addEventListener("e",null,e)}catch(t){}return t}()&&{passive:!0};e.exports={_enabled:"undefined"!=typeof window&&"undefined"!=typeof document,initialize:function(){var t="from{opacity:0.99}to{opacity:1}";m(this,"@-webkit-keyframes "+x+"{"+t+"}@keyframes "+x+"{"+t+"}."+b+"{-webkit-animation:"+x+" 0.001s;animation:"+x+" 0.001s;}")},acquireContext:function(t,e){"string"==typeof t?t=document.getElementById(t):t.length&&(t=t[0]),t&&t.canvas&&(t=t.canvas);var n=t&&t.getContext&&t.getContext("2d");return n&&n.canvas===t?(a(t,e),n):null},releaseContext:function(t){var e=t.canvas;if(e[v]){var n=e[v].initial;["height","width"].forEach(function(t){var i=n[t];p.isNullOrUndef(i)?e.removeAttribute(t):e.setAttribute(t,i)}),p.each(n.style||{},function(t,n){e.style[n]=t}),e.width=e.width,delete e[v]}},addEventListener:function(t,e,n){var i=t.canvas;if("resize"!==e){var a=n[v]||(n[v]={});r(i,e,(a.proxies||(a.proxies={}))[t.id+"_"+e]=function(e){n(l(e,t))})}else f(i,n,t)},removeEventListener:function(t,e,n){var i=t.canvas;if("resize"!==e){var a=((n[v]||{}).proxies||{})[t.id+"_"+e];a&&o(i,e,a)}else g(i)}},p.addEvent=r,p.removeEvent=o},{45:45}],48:[function(t,e,n){"use strict";var i=t(45),a=t(46),r=t(47),o=r._enabled?r:a;e.exports=i.extend({initialize:function(){},acquireContext:function(){},releaseContext:function(){},addEventListener:function(){},removeEventListener:function(){}},o)},{45:45,46:46,47:47}],49:[function(t,e,n){"use strict";var i=t(25),a=t(40),r=t(45);i._set("global",{plugins:{filler:{propagate:!0}}}),e.exports=function(){function t(t,e,n){var i,a=t._model||{},r=a.fill;if(void 0===r&&(r=!!a.backgroundColor),!1===r||null===r)return!1;if(!0===r)return"origin";if(i=parseFloat(r,10),isFinite(i)&&Math.floor(i)===i)return"-"!==r[0]&&"+"!==r[0]||(i=e+i),!(i===e||i<0||i>=n)&&i;switch(r){case"bottom":return"start";case"top":return"end";case"zero":return"origin";case"origin":case"start":case"end":return r;default:return!1}}function e(t){var e,n=t.el._model||{},i=t.el._scale||{},a=t.fill,r=null;if(isFinite(a))return null;if("start"===a?r=void 0===n.scaleBottom?i.bottom:n.scaleBottom:"end"===a?r=void 0===n.scaleTop?i.top:n.scaleTop:void 0!==n.scaleZero?r=n.scaleZero:i.getBasePosition?r=i.getBasePosition():i.getBasePixel&&(r=i.getBasePixel()),void 0!==r&&null!==r){if(void 0!==r.x&&void 0!==r.y)return r;if("number"==typeof r&&isFinite(r))return e=i.isHorizontal(),{x:e?r:null,y:e?null:r}}return null}function n(t,e,n){var i,a=t[e].fill,r=[e];if(!n)return a;for(;!1!==a&&-1===r.indexOf(a);){if(!isFinite(a))return a;if(!(i=t[a]))return!1;if(i.visible)return a;r.push(a),a=i.fill}return!1}function o(t){var e=t.fill,n="dataset";return!1===e?null:(isFinite(e)||(n="boundary"),d[n](t))}function s(t){return t&&!t.skip}function l(t,e,n,i,a){var o;if(i&&a){for(t.moveTo(e[0].x,e[0].y),o=1;o0;--o)r.canvas.lineTo(t,n[o],n[o-1],!0)}}function u(t,e,n,i,a,r){var o,u,d,c,h,f,g,m=e.length,p=i.spanGaps,v=[],y=[],b=0,x=0;for(t.beginPath(),o=0,u=m+!!r;o');for(var n=0;n'),t.data.datasets[n].label&&e.push(t.data.datasets[n].label),e.push("");return e.push(""),e.join("")}}),e.exports=function(t){function e(t,e){return t.usePointStyle?e*Math.SQRT2:t.boxWidth}function n(e,n){var i=new t.Legend({ctx:e.ctx,options:n,chart:e});o.configure(e,i,n),o.addBox(e,i),e.legend=i}var o=t.layoutService,s=r.noop;return t.Legend=a.extend({initialize:function(t){r.extend(this,t),this.legendHitBoxes=[],this.doughnutMode=!1},beforeUpdate:s,update:function(t,e,n){var i=this;return i.beforeUpdate(),i.maxWidth=t,i.maxHeight=e,i.margins=n,i.beforeSetDimensions(),i.setDimensions(),i.afterSetDimensions(),i.beforeBuildLabels(),i.buildLabels(),i.afterBuildLabels(),i.beforeFit(),i.fit(),i.afterFit(),i.afterUpdate(),i.minSize},afterUpdate:s,beforeSetDimensions:s,setDimensions:function(){var t=this;t.isHorizontal()?(t.width=t.maxWidth,t.left=0,t.right=t.width):(t.height=t.maxHeight,t.top=0,t.bottom=t.height),t.paddingLeft=0,t.paddingTop=0,t.paddingRight=0,t.paddingBottom=0,t.minSize={width:0,height:0}},afterSetDimensions:s,beforeBuildLabels:s,buildLabels:function(){var t=this,e=t.options.labels||{},n=r.callback(e.generateLabels,[t.chart],t)||[];e.filter&&(n=n.filter(function(n){return e.filter(n,t.chart.data)})),t.options.reverse&&n.reverse(),t.legendItems=n},afterBuildLabels:s,beforeFit:s,fit:function(){var t=this,n=t.options,a=n.labels,o=n.display,s=t.ctx,l=i.global,u=r.valueOrDefault,d=u(a.fontSize,l.defaultFontSize),c=u(a.fontStyle,l.defaultFontStyle),h=u(a.fontFamily,l.defaultFontFamily),f=r.fontString(d,c,h),g=t.legendHitBoxes=[],m=t.minSize,p=t.isHorizontal();if(p?(m.width=t.maxWidth,m.height=o?10:0):(m.width=o?10:0,m.height=t.maxHeight),o)if(s.font=f,p){var v=t.lineWidths=[0],y=t.legendItems.length?d+a.padding:0;s.textAlign="left",s.textBaseline="top",r.each(t.legendItems,function(n,i){var r=e(a,d)+d/2+s.measureText(n.text).width;v[v.length-1]+r+a.padding>=t.width&&(y+=d+a.padding,v[v.length]=t.left),g[i]={left:0,top:0,width:r,height:d},v[v.length-1]+=r+a.padding}),m.height+=y}else{var b=a.padding,x=t.columnWidths=[],_=a.padding,k=0,w=0,M=d+b;r.each(t.legendItems,function(t,n){var i=e(a,d)+d/2+s.measureText(t.text).width;w+M>m.height&&(_+=k+a.padding,x.push(k),k=0,w=0),k=Math.max(k,i),w+=M,g[n]={left:0,top:0,width:i,height:d}}),_+=k,x.push(k),m.width+=_}t.width=m.width,t.height=m.height},afterFit:s,isHorizontal:function(){return"top"===this.options.position||"bottom"===this.options.position},draw:function(){var t=this,n=t.options,a=n.labels,o=i.global,s=o.elements.line,l=t.width,u=t.lineWidths;if(n.display){var d,c=t.ctx,h=r.valueOrDefault,f=h(a.fontColor,o.defaultFontColor),g=h(a.fontSize,o.defaultFontSize),m=h(a.fontStyle,o.defaultFontStyle),p=h(a.fontFamily,o.defaultFontFamily),v=r.fontString(g,m,p);c.textAlign="left",c.textBaseline="middle",c.lineWidth=.5,c.strokeStyle=f,c.fillStyle=f,c.font=v;var y=e(a,g),b=t.legendHitBoxes,x=function(t,e,i){if(!(isNaN(y)||y<=0)){c.save(),c.fillStyle=h(i.fillStyle,o.defaultColor),c.lineCap=h(i.lineCap,s.borderCapStyle),c.lineDashOffset=h(i.lineDashOffset,s.borderDashOffset),c.lineJoin=h(i.lineJoin,s.borderJoinStyle),c.lineWidth=h(i.lineWidth,s.borderWidth),c.strokeStyle=h(i.strokeStyle,o.defaultColor);var a=0===h(i.lineWidth,s.borderWidth);if(c.setLineDash&&c.setLineDash(h(i.lineDash,s.borderDash)),n.labels&&n.labels.usePointStyle){var l=g*Math.SQRT2/2,u=l/Math.SQRT2,d=t+u,f=e+u;r.canvas.drawPoint(c,i.pointStyle,l,d,f)}else a||c.strokeRect(t,e,y,g),c.fillRect(t,e,y,g);c.restore()}},_=function(t,e,n,i){var a=g/2,r=y+a+t,o=e+a;c.fillText(n.text,r,o),n.hidden&&(c.beginPath(),c.lineWidth=2,c.moveTo(r,o),c.lineTo(r+i,o),c.stroke())},k=t.isHorizontal();d=k?{x:t.left+(l-u[0])/2,y:t.top+a.padding,line:0}:{x:t.left+a.padding,y:t.top+a.padding,line:0};var w=g+a.padding;r.each(t.legendItems,function(e,n){var i=c.measureText(e.text).width,r=y+g/2+i,o=d.x,s=d.y;k?o+r>=l&&(s=d.y+=w,d.line++,o=d.x=t.left+(l-u[d.line])/2):s+w>t.bottom&&(o=d.x=o+t.columnWidths[d.line]+a.padding,s=d.y=t.top+a.padding,d.line++),x(o,s,e),b[n].left=o,b[n].top=s,_(o,s,e,i),k?d.x+=r+a.padding:d.y+=w})}},handleEvent:function(t){var e=this,n=e.options,i="mouseup"===t.type?"click":t.type,a=!1;if("mousemove"===i){if(!n.onHover)return}else{if("click"!==i)return;if(!n.onClick)return}var r=t.x,o=t.y;if(r>=e.left&&r<=e.right&&o>=e.top&&o<=e.bottom)for(var s=e.legendHitBoxes,l=0;l=u.left&&r<=u.left+u.width&&o>=u.top&&o<=u.top+u.height){if("click"===i){n.onClick.call(e,t.native,e.legendItems[l]),a=!0;break}if("mousemove"===i){n.onHover.call(e,t.native,e.legendItems[l]),a=!0;break}}}return a}}),{id:"legend",beforeInit:function(t){var e=t.options.legend;e&&n(t,e)},beforeUpdate:function(t){var e=t.options.legend,a=t.legend;e?(r.mergeIf(e,i.global.legend),a?(o.configure(t,a,e),a.options=e):n(t,e)):a&&(o.removeBox(t,a),delete t.legend)},afterEvent:function(t,e){var n=t.legend;n&&n.handleEvent(e)}}}},{25:25,26:26,45:45}],51:[function(t,e,n){"use strict";var i=t(25),a=t(26),r=t(45);i._set("global",{title:{display:!1,fontStyle:"bold",fullWidth:!0,lineHeight:1.2,padding:10,position:"top",text:"",weight:2e3}}),e.exports=function(t){function e(e,i){var a=new t.Title({ctx:e.ctx,options:i,chart:e});n.configure(e,a,i),n.addBox(e,a),e.titleBlock=a}var n=t.layoutService,o=r.noop;return t.Title=a.extend({initialize:function(t){var e=this;r.extend(e,t),e.legendHitBoxes=[]},beforeUpdate:o,update:function(t,e,n){var i=this;return i.beforeUpdate(),i.maxWidth=t,i.maxHeight=e,i.margins=n,i.beforeSetDimensions(),i.setDimensions(),i.afterSetDimensions(),i.beforeBuildLabels(),i.buildLabels(),i.afterBuildLabels(),i.beforeFit(),i.fit(),i.afterFit(),i.afterUpdate(),i.minSize},afterUpdate:o,beforeSetDimensions:o,setDimensions:function(){var t=this;t.isHorizontal()?(t.width=t.maxWidth,t.left=0,t.right=t.width):(t.height=t.maxHeight,t.top=0,t.bottom=t.height),t.paddingLeft=0,t.paddingTop=0,t.paddingRight=0,t.paddingBottom=0,t.minSize={width:0,height:0}},afterSetDimensions:o,beforeBuildLabels:o,buildLabels:o,afterBuildLabels:o,beforeFit:o,fit:function(){var t=this,e=r.valueOrDefault,n=t.options,a=n.display,o=e(n.fontSize,i.global.defaultFontSize),s=t.minSize,l=r.isArray(n.text)?n.text.length:1,u=r.options.toLineHeight(n.lineHeight,o),d=a?l*u+2*n.padding:0;t.isHorizontal()?(s.width=t.maxWidth,s.height=d):(s.width=d,s.height=t.maxHeight),t.width=s.width,t.height=s.height},afterFit:o,isHorizontal:function(){var t=this.options.position;return"top"===t||"bottom"===t},draw:function(){var t=this,e=t.ctx,n=r.valueOrDefault,a=t.options,o=i.global;if(a.display){var s,l,u,d=n(a.fontSize,o.defaultFontSize),c=n(a.fontStyle,o.defaultFontStyle),h=n(a.fontFamily,o.defaultFontFamily),f=r.fontString(d,c,h),g=r.options.toLineHeight(a.lineHeight,d),m=g/2+a.padding,p=0,v=t.top,y=t.left,b=t.bottom,x=t.right;e.fillStyle=n(a.fontColor,o.defaultFontColor),e.font=f,t.isHorizontal()?(l=y+(x-y)/2,u=v+m,s=x-y):(l="left"===a.position?y+m:x-m,u=v+(b-v)/2,s=b-v,p=Math.PI*("left"===a.position?-.5:.5)),e.save(),e.translate(l,u),e.rotate(p),e.textAlign="center",e.textBaseline="middle";var _=a.text;if(r.isArray(_))for(var k=0,w=0;w<_.length;++w)e.fillText(_[w],0,k,s),k+=g;else e.fillText(_,0,0,s);e.restore()}}}),{id:"title",beforeInit:function(t){var n=t.options.title;n&&e(t,n)},beforeUpdate:function(a){var o=a.options.title,s=a.titleBlock;o?(r.mergeIf(o,i.global.title),s?(n.configure(a,s,o),s.options=o):e(a,o)):s&&(t.layoutService.removeBox(a,s),delete a.titleBlock)}}}},{25:25,26:26,45:45}],52:[function(t,e,n){"use strict";e.exports=function(t){var e=t.Scale.extend({getLabels:function(){var t=this.chart.data;return this.options.labels||(this.isHorizontal()?t.xLabels:t.yLabels)||t.labels},determineDataLimits:function(){var t=this,e=t.getLabels();t.minIndex=0,t.maxIndex=e.length-1;var n;void 0!==t.options.ticks.min&&(n=e.indexOf(t.options.ticks.min),t.minIndex=-1!==n?n:t.minIndex),void 0!==t.options.ticks.max&&(n=e.indexOf(t.options.ticks.max),t.maxIndex=-1!==n?n:t.maxIndex),t.min=e[t.minIndex],t.max=e[t.maxIndex]},buildTicks:function(){var t=this,e=t.getLabels();t.ticks=0===t.minIndex&&t.maxIndex===e.length-1?e:e.slice(t.minIndex,t.maxIndex+1)},getLabelForIndex:function(t,e){var n=this,i=n.chart.data,a=n.isHorizontal();return i.yLabels&&!a?n.getRightValue(i.datasets[e].data[t]):n.ticks[t-n.minIndex]},getPixelForValue:function(t,e){var n,i=this,a=i.options.offset,r=Math.max(i.maxIndex+1-i.minIndex-(a?0:1),1);if(void 0!==t&&null!==t&&(n=i.isHorizontal()?t.x:t.y),void 0!==n||void 0!==t&&isNaN(e)){var o=i.getLabels();t=n||t;var s=o.indexOf(t);e=-1!==s?s:e}if(i.isHorizontal()){var l=i.width/r,u=l*(e-i.minIndex);return a&&(u+=l/2),i.left+Math.round(u)}var d=i.height/r,c=d*(e-i.minIndex);return a&&(c+=d/2),i.top+Math.round(c)},getPixelForTick:function(t){return this.getPixelForValue(this.ticks[t],t+this.minIndex,null)},getValueForPixel:function(t){var e=this,n=e.options.offset,i=Math.max(e._ticks.length-(n?0:1),1),a=e.isHorizontal(),r=(a?e.width:e.height)/i;return t-=a?e.left:e.top,n&&(t-=r/2),(t<=0?0:Math.round(t/r))+e.minIndex},getBasePixel:function(){return this.bottom}});t.scaleService.registerScaleType("category",e,{position:"bottom"})}},{}],53:[function(t,e,n){"use strict";var i=t(25),a=t(45),r=t(34);e.exports=function(t){var e={position:"left",ticks:{callback:r.formatters.linear}},n=t.LinearScaleBase.extend({determineDataLimits:function(){function t(t){return o?t.xAxisID===e.id:t.yAxisID===e.id}var e=this,n=e.options,i=e.chart,r=i.data.datasets,o=e.isHorizontal();e.min=null,e.max=null;var s=n.stacked;if(void 0===s&&a.each(r,function(e,n){if(!s){var a=i.getDatasetMeta(n);i.isDatasetVisible(n)&&t(a)&&void 0!==a.stack&&(s=!0)}}),n.stacked||s){var l={};a.each(r,function(r,o){var s=i.getDatasetMeta(o),u=[s.type,void 0===n.stacked&&void 0===s.stack?o:"",s.stack].join(".");void 0===l[u]&&(l[u]={positiveValues:[],negativeValues:[]});var d=l[u].positiveValues,c=l[u].negativeValues;i.isDatasetVisible(o)&&t(s)&&a.each(r.data,function(t,i){var a=+e.getRightValue(t);isNaN(a)||s.data[i].hidden||(d[i]=d[i]||0,c[i]=c[i]||0,n.relativePoints?d[i]=100:a<0?c[i]+=a:d[i]+=a)})}),a.each(l,function(t){var n=t.positiveValues.concat(t.negativeValues),i=a.min(n),r=a.max(n);e.min=null===e.min?i:Math.min(e.min,i),e.max=null===e.max?r:Math.max(e.max,r)})}else a.each(r,function(n,r){var o=i.getDatasetMeta(r);i.isDatasetVisible(r)&&t(o)&&a.each(n.data,function(t,n){var i=+e.getRightValue(t);isNaN(i)||o.data[n].hidden||(null===e.min?e.min=i:ie.max&&(e.max=i))})});e.min=isFinite(e.min)&&!isNaN(e.min)?e.min:0,e.max=isFinite(e.max)&&!isNaN(e.max)?e.max:1,this.handleTickRangeOptions()},getTickLimit:function(){var t,e=this,n=e.options.ticks;if(e.isHorizontal())t=Math.min(n.maxTicksLimit?n.maxTicksLimit:11,Math.ceil(e.width/50));else{var r=a.valueOrDefault(n.fontSize,i.global.defaultFontSize);t=Math.min(n.maxTicksLimit?n.maxTicksLimit:11,Math.ceil(e.height/(2*r)))}return t},handleDirectionalChanges:function(){this.isHorizontal()||this.ticks.reverse()},getLabelForIndex:function(t,e){return+this.getRightValue(this.chart.data.datasets[e].data[t])},getPixelForValue:function(t){var e,n=this,i=n.start,a=+n.getRightValue(t),r=n.end-i;return n.isHorizontal()?(e=n.left+n.width/r*(a-i),Math.round(e)):(e=n.bottom-n.height/r*(a-i),Math.round(e))},getValueForPixel:function(t){var e=this,n=e.isHorizontal(),i=n?e.width:e.height,a=(n?t-e.left:e.bottom-t)/i;return e.start+(e.end-e.start)*a},getPixelForTick:function(t){return this.getPixelForValue(this.ticksAsNumbers[t])}});t.scaleService.registerScaleType("linear",n,e)}},{25:25,34:34,45:45}],54:[function(t,e,n){"use strict";var i=t(45),a=t(34);e.exports=function(t){var e=i.noop;t.LinearScaleBase=t.Scale.extend({getRightValue:function(e){return"string"==typeof e?+e:t.Scale.prototype.getRightValue.call(this,e)},handleTickRangeOptions:function(){var t=this,e=t.options.ticks;if(e.beginAtZero){var n=i.sign(t.min),a=i.sign(t.max);n<0&&a<0?t.max=0:n>0&&a>0&&(t.min=0)}var r=void 0!==e.min||void 0!==e.suggestedMin,o=void 0!==e.max||void 0!==e.suggestedMax;void 0!==e.min?t.min=e.min:void 0!==e.suggestedMin&&(null===t.min?t.min=e.suggestedMin:t.min=Math.min(t.min,e.suggestedMin)),void 0!==e.max?t.max=e.max:void 0!==e.suggestedMax&&(null===t.max?t.max=e.suggestedMax:t.max=Math.max(t.max,e.suggestedMax)),r!==o&&t.min>=t.max&&(r?t.max=t.min+1:t.min=t.max-1),t.min===t.max&&(t.max++,e.beginAtZero||t.min--)},getTickLimit:e,handleDirectionalChanges:e,buildTicks:function(){var t=this,e=t.options.ticks,n=t.getTickLimit(),r={maxTicks:n=Math.max(2,n),min:e.min,max:e.max,stepSize:i.valueOrDefault(e.fixedStepSize,e.stepSize)},o=t.ticks=a.generators.linear(r,t);t.handleDirectionalChanges(),t.max=i.max(o),t.min=i.min(o),e.reverse?(o.reverse(),t.start=t.max,t.end=t.min):(t.start=t.min,t.end=t.max)},convertTicksToLabels:function(){var e=this;e.ticksAsNumbers=e.ticks.slice(),e.zeroLineIndex=e.ticks.indexOf(0),t.Scale.prototype.convertTicksToLabels.call(e)}})}},{34:34,45:45}],55:[function(t,e,n){"use strict";var i=t(45),a=t(34);e.exports=function(t){var e={position:"left",ticks:{callback:a.formatters.logarithmic}},n=t.Scale.extend({determineDataLimits:function(){function t(t){return l?t.xAxisID===e.id:t.yAxisID===e.id}var e=this,n=e.options,a=n.ticks,r=e.chart,o=r.data.datasets,s=i.valueOrDefault,l=e.isHorizontal();e.min=null,e.max=null,e.minNotZero=null;var u=n.stacked;if(void 0===u&&i.each(o,function(e,n){if(!u){var i=r.getDatasetMeta(n);r.isDatasetVisible(n)&&t(i)&&void 0!==i.stack&&(u=!0)}}),n.stacked||u){var d={};i.each(o,function(a,o){var s=r.getDatasetMeta(o),l=[s.type,void 0===n.stacked&&void 0===s.stack?o:"",s.stack].join(".");r.isDatasetVisible(o)&&t(s)&&(void 0===d[l]&&(d[l]=[]),i.each(a.data,function(t,i){var a=d[l],r=+e.getRightValue(t);isNaN(r)||s.data[i].hidden||(a[i]=a[i]||0,n.relativePoints?a[i]=100:a[i]+=r)}))}),i.each(d,function(t){var n=i.min(t),a=i.max(t);e.min=null===e.min?n:Math.min(e.min,n),e.max=null===e.max?a:Math.max(e.max,a)})}else i.each(o,function(n,a){var o=r.getDatasetMeta(a);r.isDatasetVisible(a)&&t(o)&&i.each(n.data,function(t,n){var i=+e.getRightValue(t);isNaN(i)||o.data[n].hidden||(null===e.min?e.min=i:ie.max&&(e.max=i),0!==i&&(null===e.minNotZero||ia?{start:e-n-5,end:e}:{start:e,end:e+n+5}}function l(t){var i,r,l,u=n(t),d=Math.min(t.height/2,t.width/2),c={r:t.width,l:0,t:t.height,b:0},h={};t.ctx.font=u.font,t._pointLabelSizes=[];var f=e(t);for(i=0;ic.r&&(c.r=p.end,h.r=g),v.startc.b&&(c.b=v.end,h.b=g)}t.setReductions(d,c,h)}function u(t){var e=Math.min(t.height/2,t.width/2);t.drawingArea=Math.round(e),t.setCenterPoint(0,0,0,0)}function d(t){return 0===t||180===t?"center":t<180?"left":"right"}function c(t,e,n,i){if(a.isArray(e))for(var r=n.y,o=1.5*i,s=0;s270||t<90)&&(n.y-=e.h)}function f(t){var i=t.ctx,r=a.valueOrDefault,o=t.options,s=o.angleLines,l=o.pointLabels;i.lineWidth=s.lineWidth,i.strokeStyle=s.color;var u=t.getDistanceFromCenterForValue(o.ticks.reverse?t.min:t.max),f=n(t);i.textBaseline="top";for(var g=e(t)-1;g>=0;g--){if(s.display){var m=t.getPointPosition(g,u);i.beginPath(),i.moveTo(t.xCenter,t.yCenter),i.lineTo(m.x,m.y),i.stroke(),i.closePath()}if(l.display){var v=t.getPointPosition(g,u+5),y=r(l.fontColor,p.defaultFontColor);i.font=f.font,i.fillStyle=y;var b=t.getIndexAngle(g),x=a.toDegrees(b);i.textAlign=d(x),h(x,t._pointLabelSizes[g],v),c(i,t.pointLabels[g]||"",v,f.size)}}}function g(t,n,i,r){var o=t.ctx;if(o.strokeStyle=a.valueAtIndexOrDefault(n.color,r-1),o.lineWidth=a.valueAtIndexOrDefault(n.lineWidth,r-1),t.options.gridLines.circular)o.beginPath(),o.arc(t.xCenter,t.yCenter,i,0,2*Math.PI),o.closePath(),o.stroke();else{var s=e(t);if(0===s)return;o.beginPath();var l=t.getPointPosition(0,i);o.moveTo(l.x,l.y);for(var u=1;u0&&n>0?e:0)},draw:function(){var t=this,e=t.options,n=e.gridLines,i=e.ticks,r=a.valueOrDefault;if(e.display){var o=t.ctx,s=this.getIndexAngle(0),l=r(i.fontSize,p.defaultFontSize),u=r(i.fontStyle,p.defaultFontStyle),d=r(i.fontFamily,p.defaultFontFamily),c=a.fontString(l,u,d);a.each(t.ticks,function(e,a){if(a>0||i.reverse){var u=t.getDistanceFromCenterForValue(t.ticksAsNumbers[a]);if(n.display&&0!==a&&g(t,n,u,a),i.display){var d=r(i.fontColor,p.defaultFontColor);if(o.font=c,o.save(),o.translate(t.xCenter,t.yCenter),o.rotate(s),i.showLabelBackdrop){var h=o.measureText(e).width;o.fillStyle=i.backdropColor,o.fillRect(-h/2-i.backdropPaddingX,-u-l/2-i.backdropPaddingY,h+2*i.backdropPaddingX,l+2*i.backdropPaddingY)}o.textAlign="center",o.textBaseline="middle",o.fillStyle=d,o.fillText(e,0,-u),o.restore()}}}),(e.angleLines.display||e.pointLabels.display)&&f(t)}}});t.scaleService.registerScaleType("radialLinear",y,v)}},{25:25,34:34,45:45}],57:[function(t,e,n){"use strict";function i(t,e){return t-e}function a(t){var e,n,i,a={},r=[];for(e=0,n=t.length;ee&&s=0&&o<=s;){if(i=o+s>>1,a=t[i-1]||null,r=t[i],!a)return{lo:null,hi:r};if(r[e]n))return{lo:a,hi:r};s=i-1}}return{lo:r,hi:null}}function s(t,e,n,i){var a=o(t,e,n),r=a.lo?a.hi?a.lo:t[t.length-2]:t[0],s=a.lo?a.hi?a.hi:t[t.length-1]:t[1],l=s[e]-r[e],u=l?(n-r[e])/l:0,d=(s[i]-r[i])*u;return r[i]+d}function l(t,e){var n=e.parser,i=e.parser||e.format;return"function"==typeof n?n(t):"string"==typeof t&&"string"==typeof i?v(t,i):(t instanceof v||(t=v(t)),t.isValid()?t:"function"==typeof i?i(t):t)}function u(t,e){if(b.isNullOrUndef(t))return null;var n=e.options.time,i=l(e.getRightValue(t),n);return i.isValid()?(n.round&&i.startOf(n.round),i.valueOf()):null}function d(t,e,n,i){var a,r,o,s=e-t,l=k[n],u=l.size,d=l.steps;if(!d)return Math.ceil(s/((i||1)*u));for(a=0,r=d.length;a=w.indexOf(e);a--)if(r=w[a],k[r].common&&o.as(r)>=t.length)return r;return w[e?w.indexOf(e):0]}function f(t){for(var e=w.indexOf(t)+1,n=w.length;e1?e[1]:i,o=e[0],l=(s(t,"time",r,"pos")-s(t,"time",o,"pos"))/2),a.time.max||(r=e[e.length-1],o=e.length>1?e[e.length-2]:n,u=(s(t,"time",r,"pos")-s(t,"time",o,"pos"))/2)),{left:l,right:u}}function p(t,e){var n,i,a,r,o=[];for(n=0,i=t.length;n=a&&n<=o&&c.push(n);return i.min=a,i.max=o,i._unit=l.unit||h(c,l.minUnit,i.min,i.max),i._majorUnit=f(i._unit),i._table=r(i._timestamps.data,a,o,s.distribution),i._offsets=m(i._table,c,a,o,s),p(c,i._majorUnit)},getLabelForIndex:function(t,e){var n=this,i=n.chart.data,a=n.options.time,r=i.labels&&t=0&&t lum2) { return (lum1 + 0.05) / (lum2 + 0.05); } return (lum2 + 0.05) / (lum1 + 0.05); }, level: function (color2) { var contrastRatio = this.contrast(color2); if (contrastRatio >= 7.1) { return 'AAA'; } return (contrastRatio >= 4.5) ? 'AA' : ''; }, dark: function () { // YIQ equation from http://24ways.org/2010/calculating-color-contrast var rgb = this.values.rgb; var yiq = (rgb[0] * 299 + rgb[1] * 587 + rgb[2] * 114) / 1000; return yiq < 128; }, light: function () { return !this.dark(); }, negate: function () { var rgb = []; for (var i = 0; i < 3; i++) { rgb[i] = 255 - this.values.rgb[i]; } this.setValues('rgb', rgb); return this; }, lighten: function (ratio) { var hsl = this.values.hsl; hsl[2] += hsl[2] * ratio; this.setValues('hsl', hsl); return this; }, darken: function (ratio) { var hsl = this.values.hsl; hsl[2] -= hsl[2] * ratio; this.setValues('hsl', hsl); return this; }, saturate: function (ratio) { var hsl = this.values.hsl; hsl[1] += hsl[1] * ratio; this.setValues('hsl', hsl); return this; }, desaturate: function (ratio) { var hsl = this.values.hsl; hsl[1] -= hsl[1] * ratio; this.setValues('hsl', hsl); return this; }, whiten: function (ratio) { var hwb = this.values.hwb; hwb[1] += hwb[1] * ratio; this.setValues('hwb', hwb); return this; }, blacken: function (ratio) { var hwb = this.values.hwb; hwb[2] += hwb[2] * ratio; this.setValues('hwb', hwb); return this; }, greyscale: function () { var rgb = this.values.rgb; // http://en.wikipedia.org/wiki/Grayscale#Converting_color_to_grayscale var val = rgb[0] * 0.3 + rgb[1] * 0.59 + rgb[2] * 0.11; this.setValues('rgb', [val, val, val]); return this; }, clearer: function (ratio) { var alpha = this.values.alpha; this.setValues('alpha', alpha - (alpha * ratio)); return this; }, opaquer: function (ratio) { var alpha = this.values.alpha; this.setValues('alpha', alpha + (alpha * ratio)); return this; }, rotate: function (degrees) { var hsl = this.values.hsl; var hue = (hsl[0] + degrees) % 360; hsl[0] = hue < 0 ? 360 + hue : hue; this.setValues('hsl', hsl); return this; }, /** * Ported from sass implementation in C * https://github.com/sass/libsass/blob/0e6b4a2850092356aa3ece07c6b249f0221caced/functions.cpp#L209 */ mix: function (mixinColor, weight) { var color1 = this; var color2 = mixinColor; var p = weight === undefined ? 0.5 : weight; var w = 2 * p - 1; var a = color1.alpha() - color2.alpha(); var w1 = (((w * a === -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0; var w2 = 1 - w1; return this .rgb( w1 * color1.red() + w2 * color2.red(), w1 * color1.green() + w2 * color2.green(), w1 * color1.blue() + w2 * color2.blue() ) .alpha(color1.alpha() * p + color2.alpha() * (1 - p)); }, toJSON: function () { return this.rgb(); }, clone: function () { // NOTE(SB): using node-clone creates a dependency to Buffer when using browserify, // making the final build way to big to embed in Chart.js. So let's do it manually, // assuming that values to clone are 1 dimension arrays containing only numbers, // except 'alpha' which is a number. var result = new Color(); var source = this.values; var target = result.values; var value, type; for (var prop in source) { if (source.hasOwnProperty(prop)) { value = source[prop]; type = ({}).toString.call(value); if (type === '[object Array]') { target[prop] = value.slice(0); } else if (type === '[object Number]') { target[prop] = value; } else { console.error('unexpected color value:', value); } } } return result; } }; Color.prototype.spaces = { rgb: ['red', 'green', 'blue'], hsl: ['hue', 'saturation', 'lightness'], hsv: ['hue', 'saturation', 'value'], hwb: ['hue', 'whiteness', 'blackness'], cmyk: ['cyan', 'magenta', 'yellow', 'black'] }; Color.prototype.maxes = { rgb: [255, 255, 255], hsl: [360, 100, 100], hsv: [360, 100, 100], hwb: [360, 100, 100], cmyk: [100, 100, 100, 100] }; Color.prototype.getValues = function (space) { var values = this.values; var vals = {}; for (var i = 0; i < space.length; i++) { vals[space.charAt(i)] = values[space][i]; } if (values.alpha !== 1) { vals.a = values.alpha; } // {r: 255, g: 255, b: 255, a: 0.4} return vals; }; Color.prototype.setValues = function (space, vals) { var values = this.values; var spaces = this.spaces; var maxes = this.maxes; var alpha = 1; var i; this.valid = true; if (space === 'alpha') { alpha = vals; } else if (vals.length) { // [10, 10, 10] values[space] = vals.slice(0, space.length); alpha = vals[space.length]; } else if (vals[space.charAt(0)] !== undefined) { // {r: 10, g: 10, b: 10} for (i = 0; i < space.length; i++) { values[space][i] = vals[space.charAt(i)]; } alpha = vals.a; } else if (vals[spaces[space][0]] !== undefined) { // {red: 10, green: 10, blue: 10} var chans = spaces[space]; for (i = 0; i < space.length; i++) { values[space][i] = vals[chans[i]]; } alpha = vals.alpha; } values.alpha = Math.max(0, Math.min(1, (alpha === undefined ? values.alpha : alpha))); if (space === 'alpha') { return false; } var capped; // cap values of the space prior converting all values for (i = 0; i < space.length; i++) { capped = Math.max(0, Math.min(maxes[space][i], values[space][i])); values[space][i] = Math.round(capped); } // convert to all the other color spaces for (var sname in spaces) { if (sname !== space) { values[sname] = convert[space][sname](values[space]); } } return true; }; Color.prototype.setSpace = function (space, args) { var vals = args[0]; if (vals === undefined) { // color.rgb() return this.getValues(space); } // color.rgb(10, 10, 10) if (typeof vals === 'number') { vals = Array.prototype.slice.call(args); } this.setValues(space, vals); return this; }; Color.prototype.setChannel = function (space, index, val) { var svalues = this.values[space]; if (val === undefined) { // color.red() return svalues[index]; } else if (val === svalues[index]) { // color.red(color.red()) return this; } // color.red(100) svalues[index] = val; this.setValues(space, svalues); return this; }; if (typeof window !== 'undefined') { window.Color = Color; } module.exports = Color; },{"1":1,"4":4}],3:[function(require,module,exports){ /* MIT license */ module.exports = { rgb2hsl: rgb2hsl, rgb2hsv: rgb2hsv, rgb2hwb: rgb2hwb, rgb2cmyk: rgb2cmyk, rgb2keyword: rgb2keyword, rgb2xyz: rgb2xyz, rgb2lab: rgb2lab, rgb2lch: rgb2lch, hsl2rgb: hsl2rgb, hsl2hsv: hsl2hsv, hsl2hwb: hsl2hwb, hsl2cmyk: hsl2cmyk, hsl2keyword: hsl2keyword, hsv2rgb: hsv2rgb, hsv2hsl: hsv2hsl, hsv2hwb: hsv2hwb, hsv2cmyk: hsv2cmyk, hsv2keyword: hsv2keyword, hwb2rgb: hwb2rgb, hwb2hsl: hwb2hsl, hwb2hsv: hwb2hsv, hwb2cmyk: hwb2cmyk, hwb2keyword: hwb2keyword, cmyk2rgb: cmyk2rgb, cmyk2hsl: cmyk2hsl, cmyk2hsv: cmyk2hsv, cmyk2hwb: cmyk2hwb, cmyk2keyword: cmyk2keyword, keyword2rgb: keyword2rgb, keyword2hsl: keyword2hsl, keyword2hsv: keyword2hsv, keyword2hwb: keyword2hwb, keyword2cmyk: keyword2cmyk, keyword2lab: keyword2lab, keyword2xyz: keyword2xyz, xyz2rgb: xyz2rgb, xyz2lab: xyz2lab, xyz2lch: xyz2lch, lab2xyz: lab2xyz, lab2rgb: lab2rgb, lab2lch: lab2lch, lch2lab: lch2lab, lch2xyz: lch2xyz, lch2rgb: lch2rgb } function rgb2hsl(rgb) { var r = rgb[0]/255, g = rgb[1]/255, b = rgb[2]/255, min = Math.min(r, g, b), max = Math.max(r, g, b), delta = max - min, h, s, l; if (max == min) h = 0; else if (r == max) h = (g - b) / delta; else if (g == max) h = 2 + (b - r) / delta; else if (b == max) h = 4 + (r - g)/ delta; h = Math.min(h * 60, 360); if (h < 0) h += 360; l = (min + max) / 2; if (max == min) s = 0; else if (l <= 0.5) s = delta / (max + min); else s = delta / (2 - max - min); return [h, s * 100, l * 100]; } function rgb2hsv(rgb) { var r = rgb[0], g = rgb[1], b = rgb[2], min = Math.min(r, g, b), max = Math.max(r, g, b), delta = max - min, h, s, v; if (max == 0) s = 0; else s = (delta/max * 1000)/10; if (max == min) h = 0; else if (r == max) h = (g - b) / delta; else if (g == max) h = 2 + (b - r) / delta; else if (b == max) h = 4 + (r - g) / delta; h = Math.min(h * 60, 360); if (h < 0) h += 360; v = ((max / 255) * 1000) / 10; return [h, s, v]; } function rgb2hwb(rgb) { var r = rgb[0], g = rgb[1], b = rgb[2], h = rgb2hsl(rgb)[0], w = 1/255 * Math.min(r, Math.min(g, b)), b = 1 - 1/255 * Math.max(r, Math.max(g, b)); return [h, w * 100, b * 100]; } function rgb2cmyk(rgb) { var r = rgb[0] / 255, g = rgb[1] / 255, b = rgb[2] / 255, c, m, y, k; k = Math.min(1 - r, 1 - g, 1 - b); c = (1 - r - k) / (1 - k) || 0; m = (1 - g - k) / (1 - k) || 0; y = (1 - b - k) / (1 - k) || 0; return [c * 100, m * 100, y * 100, k * 100]; } function rgb2keyword(rgb) { return reverseKeywords[JSON.stringify(rgb)]; } function rgb2xyz(rgb) { var r = rgb[0] / 255, g = rgb[1] / 255, b = rgb[2] / 255; // assume sRGB r = r > 0.04045 ? Math.pow(((r + 0.055) / 1.055), 2.4) : (r / 12.92); g = g > 0.04045 ? Math.pow(((g + 0.055) / 1.055), 2.4) : (g / 12.92); b = b > 0.04045 ? Math.pow(((b + 0.055) / 1.055), 2.4) : (b / 12.92); var x = (r * 0.4124) + (g * 0.3576) + (b * 0.1805); var y = (r * 0.2126) + (g * 0.7152) + (b * 0.0722); var z = (r * 0.0193) + (g * 0.1192) + (b * 0.9505); return [x * 100, y *100, z * 100]; } function rgb2lab(rgb) { var xyz = rgb2xyz(rgb), x = xyz[0], y = xyz[1], z = xyz[2], l, a, b; x /= 95.047; y /= 100; z /= 108.883; x = x > 0.008856 ? Math.pow(x, 1/3) : (7.787 * x) + (16 / 116); y = y > 0.008856 ? Math.pow(y, 1/3) : (7.787 * y) + (16 / 116); z = z > 0.008856 ? Math.pow(z, 1/3) : (7.787 * z) + (16 / 116); l = (116 * y) - 16; a = 500 * (x - y); b = 200 * (y - z); return [l, a, b]; } function rgb2lch(args) { return lab2lch(rgb2lab(args)); } function hsl2rgb(hsl) { var h = hsl[0] / 360, s = hsl[1] / 100, l = hsl[2] / 100, t1, t2, t3, rgb, val; if (s == 0) { val = l * 255; return [val, val, val]; } if (l < 0.5) t2 = l * (1 + s); else t2 = l + s - l * s; t1 = 2 * l - t2; rgb = [0, 0, 0]; for (var i = 0; i < 3; i++) { t3 = h + 1 / 3 * - (i - 1); t3 < 0 && t3++; t3 > 1 && t3--; if (6 * t3 < 1) val = t1 + (t2 - t1) * 6 * t3; else if (2 * t3 < 1) val = t2; else if (3 * t3 < 2) val = t1 + (t2 - t1) * (2 / 3 - t3) * 6; else val = t1; rgb[i] = val * 255; } return rgb; } function hsl2hsv(hsl) { var h = hsl[0], s = hsl[1] / 100, l = hsl[2] / 100, sv, v; if(l === 0) { // no need to do calc on black // also avoids divide by 0 error return [0, 0, 0]; } l *= 2; s *= (l <= 1) ? l : 2 - l; v = (l + s) / 2; sv = (2 * s) / (l + s); return [h, sv * 100, v * 100]; } function hsl2hwb(args) { return rgb2hwb(hsl2rgb(args)); } function hsl2cmyk(args) { return rgb2cmyk(hsl2rgb(args)); } function hsl2keyword(args) { return rgb2keyword(hsl2rgb(args)); } function hsv2rgb(hsv) { var h = hsv[0] / 60, s = hsv[1] / 100, v = hsv[2] / 100, hi = Math.floor(h) % 6; var f = h - Math.floor(h), p = 255 * v * (1 - s), q = 255 * v * (1 - (s * f)), t = 255 * v * (1 - (s * (1 - f))), v = 255 * v; switch(hi) { case 0: return [v, t, p]; case 1: return [q, v, p]; case 2: return [p, v, t]; case 3: return [p, q, v]; case 4: return [t, p, v]; case 5: return [v, p, q]; } } function hsv2hsl(hsv) { var h = hsv[0], s = hsv[1] / 100, v = hsv[2] / 100, sl, l; l = (2 - s) * v; sl = s * v; sl /= (l <= 1) ? l : 2 - l; sl = sl || 0; l /= 2; return [h, sl * 100, l * 100]; } function hsv2hwb(args) { return rgb2hwb(hsv2rgb(args)) } function hsv2cmyk(args) { return rgb2cmyk(hsv2rgb(args)); } function hsv2keyword(args) { return rgb2keyword(hsv2rgb(args)); } // http://dev.w3.org/csswg/css-color/#hwb-to-rgb function hwb2rgb(hwb) { var h = hwb[0] / 360, wh = hwb[1] / 100, bl = hwb[2] / 100, ratio = wh + bl, i, v, f, n; // wh + bl cant be > 1 if (ratio > 1) { wh /= ratio; bl /= ratio; } i = Math.floor(6 * h); v = 1 - bl; f = 6 * h - i; if ((i & 0x01) != 0) { f = 1 - f; } n = wh + f * (v - wh); // linear interpolation switch (i) { default: case 6: case 0: r = v; g = n; b = wh; break; case 1: r = n; g = v; b = wh; break; case 2: r = wh; g = v; b = n; break; case 3: r = wh; g = n; b = v; break; case 4: r = n; g = wh; b = v; break; case 5: r = v; g = wh; b = n; break; } return [r * 255, g * 255, b * 255]; } function hwb2hsl(args) { return rgb2hsl(hwb2rgb(args)); } function hwb2hsv(args) { return rgb2hsv(hwb2rgb(args)); } function hwb2cmyk(args) { return rgb2cmyk(hwb2rgb(args)); } function hwb2keyword(args) { return rgb2keyword(hwb2rgb(args)); } function cmyk2rgb(cmyk) { var c = cmyk[0] / 100, m = cmyk[1] / 100, y = cmyk[2] / 100, k = cmyk[3] / 100, r, g, b; r = 1 - Math.min(1, c * (1 - k) + k); g = 1 - Math.min(1, m * (1 - k) + k); b = 1 - Math.min(1, y * (1 - k) + k); return [r * 255, g * 255, b * 255]; } function cmyk2hsl(args) { return rgb2hsl(cmyk2rgb(args)); } function cmyk2hsv(args) { return rgb2hsv(cmyk2rgb(args)); } function cmyk2hwb(args) { return rgb2hwb(cmyk2rgb(args)); } function cmyk2keyword(args) { return rgb2keyword(cmyk2rgb(args)); } function xyz2rgb(xyz) { var x = xyz[0] / 100, y = xyz[1] / 100, z = xyz[2] / 100, r, g, b; r = (x * 3.2406) + (y * -1.5372) + (z * -0.4986); g = (x * -0.9689) + (y * 1.8758) + (z * 0.0415); b = (x * 0.0557) + (y * -0.2040) + (z * 1.0570); // assume sRGB r = r > 0.0031308 ? ((1.055 * Math.pow(r, 1.0 / 2.4)) - 0.055) : r = (r * 12.92); g = g > 0.0031308 ? ((1.055 * Math.pow(g, 1.0 / 2.4)) - 0.055) : g = (g * 12.92); b = b > 0.0031308 ? ((1.055 * Math.pow(b, 1.0 / 2.4)) - 0.055) : b = (b * 12.92); r = Math.min(Math.max(0, r), 1); g = Math.min(Math.max(0, g), 1); b = Math.min(Math.max(0, b), 1); return [r * 255, g * 255, b * 255]; } function xyz2lab(xyz) { var x = xyz[0], y = xyz[1], z = xyz[2], l, a, b; x /= 95.047; y /= 100; z /= 108.883; x = x > 0.008856 ? Math.pow(x, 1/3) : (7.787 * x) + (16 / 116); y = y > 0.008856 ? Math.pow(y, 1/3) : (7.787 * y) + (16 / 116); z = z > 0.008856 ? Math.pow(z, 1/3) : (7.787 * z) + (16 / 116); l = (116 * y) - 16; a = 500 * (x - y); b = 200 * (y - z); return [l, a, b]; } function xyz2lch(args) { return lab2lch(xyz2lab(args)); } function lab2xyz(lab) { var l = lab[0], a = lab[1], b = lab[2], x, y, z, y2; if (l <= 8) { y = (l * 100) / 903.3; y2 = (7.787 * (y / 100)) + (16 / 116); } else { y = 100 * Math.pow((l + 16) / 116, 3); y2 = Math.pow(y / 100, 1/3); } x = x / 95.047 <= 0.008856 ? x = (95.047 * ((a / 500) + y2 - (16 / 116))) / 7.787 : 95.047 * Math.pow((a / 500) + y2, 3); z = z / 108.883 <= 0.008859 ? z = (108.883 * (y2 - (b / 200) - (16 / 116))) / 7.787 : 108.883 * Math.pow(y2 - (b / 200), 3); return [x, y, z]; } function lab2lch(lab) { var l = lab[0], a = lab[1], b = lab[2], hr, h, c; hr = Math.atan2(b, a); h = hr * 360 / 2 / Math.PI; if (h < 0) { h += 360; } c = Math.sqrt(a * a + b * b); return [l, c, h]; } function lab2rgb(args) { return xyz2rgb(lab2xyz(args)); } function lch2lab(lch) { var l = lch[0], c = lch[1], h = lch[2], a, b, hr; hr = h / 360 * 2 * Math.PI; a = c * Math.cos(hr); b = c * Math.sin(hr); return [l, a, b]; } function lch2xyz(args) { return lab2xyz(lch2lab(args)); } function lch2rgb(args) { return lab2rgb(lch2lab(args)); } function keyword2rgb(keyword) { return cssKeywords[keyword]; } function keyword2hsl(args) { return rgb2hsl(keyword2rgb(args)); } function keyword2hsv(args) { return rgb2hsv(keyword2rgb(args)); } function keyword2hwb(args) { return rgb2hwb(keyword2rgb(args)); } function keyword2cmyk(args) { return rgb2cmyk(keyword2rgb(args)); } function keyword2lab(args) { return rgb2lab(keyword2rgb(args)); } function keyword2xyz(args) { return rgb2xyz(keyword2rgb(args)); } var cssKeywords = { aliceblue: [240,248,255], antiquewhite: [250,235,215], aqua: [0,255,255], aquamarine: [127,255,212], azure: [240,255,255], beige: [245,245,220], bisque: [255,228,196], black: [0,0,0], blanchedalmond: [255,235,205], blue: [0,0,255], blueviolet: [138,43,226], brown: [165,42,42], burlywood: [222,184,135], cadetblue: [95,158,160], chartreuse: [127,255,0], chocolate: [210,105,30], coral: [255,127,80], cornflowerblue: [100,149,237], cornsilk: [255,248,220], crimson: [220,20,60], cyan: [0,255,255], darkblue: [0,0,139], darkcyan: [0,139,139], darkgoldenrod: [184,134,11], darkgray: [169,169,169], darkgreen: [0,100,0], darkgrey: [169,169,169], darkkhaki: [189,183,107], darkmagenta: [139,0,139], darkolivegreen: [85,107,47], darkorange: [255,140,0], darkorchid: [153,50,204], darkred: [139,0,0], darksalmon: [233,150,122], darkseagreen: [143,188,143], darkslateblue: [72,61,139], darkslategray: [47,79,79], darkslategrey: [47,79,79], darkturquoise: [0,206,209], darkviolet: [148,0,211], deeppink: [255,20,147], deepskyblue: [0,191,255], dimgray: [105,105,105], dimgrey: [105,105,105], dodgerblue: [30,144,255], firebrick: [178,34,34], floralwhite: [255,250,240], forestgreen: [34,139,34], fuchsia: [255,0,255], gainsboro: [220,220,220], ghostwhite: [248,248,255], gold: [255,215,0], goldenrod: [218,165,32], gray: [128,128,128], green: [0,128,0], greenyellow: [173,255,47], grey: [128,128,128], honeydew: [240,255,240], hotpink: [255,105,180], indianred: [205,92,92], indigo: [75,0,130], ivory: [255,255,240], khaki: [240,230,140], lavender: [230,230,250], lavenderblush: [255,240,245], lawngreen: [124,252,0], lemonchiffon: [255,250,205], lightblue: [173,216,230], lightcoral: [240,128,128], lightcyan: [224,255,255], lightgoldenrodyellow: [250,250,210], lightgray: [211,211,211], lightgreen: [144,238,144], lightgrey: [211,211,211], lightpink: [255,182,193], lightsalmon: [255,160,122], lightseagreen: [32,178,170], lightskyblue: [135,206,250], lightslategray: [119,136,153], lightslategrey: [119,136,153], lightsteelblue: [176,196,222], lightyellow: [255,255,224], lime: [0,255,0], limegreen: [50,205,50], linen: [250,240,230], magenta: [255,0,255], maroon: [128,0,0], mediumaquamarine: [102,205,170], mediumblue: [0,0,205], mediumorchid: [186,85,211], mediumpurple: [147,112,219], mediumseagreen: [60,179,113], mediumslateblue: [123,104,238], mediumspringgreen: [0,250,154], mediumturquoise: [72,209,204], mediumvioletred: [199,21,133], midnightblue: [25,25,112], mintcream: [245,255,250], mistyrose: [255,228,225], moccasin: [255,228,181], navajowhite: [255,222,173], navy: [0,0,128], oldlace: [253,245,230], olive: [128,128,0], olivedrab: [107,142,35], orange: [255,165,0], orangered: [255,69,0], orchid: [218,112,214], palegoldenrod: [238,232,170], palegreen: [152,251,152], paleturquoise: [175,238,238], palevioletred: [219,112,147], papayawhip: [255,239,213], peachpuff: [255,218,185], peru: [205,133,63], pink: [255,192,203], plum: [221,160,221], powderblue: [176,224,230], purple: [128,0,128], rebeccapurple: [102, 51, 153], red: [255,0,0], rosybrown: [188,143,143], royalblue: [65,105,225], saddlebrown: [139,69,19], salmon: [250,128,114], sandybrown: [244,164,96], seagreen: [46,139,87], seashell: [255,245,238], sienna: [160,82,45], silver: [192,192,192], skyblue: [135,206,235], slateblue: [106,90,205], slategray: [112,128,144], slategrey: [112,128,144], snow: [255,250,250], springgreen: [0,255,127], steelblue: [70,130,180], tan: [210,180,140], teal: [0,128,128], thistle: [216,191,216], tomato: [255,99,71], turquoise: [64,224,208], violet: [238,130,238], wheat: [245,222,179], white: [255,255,255], whitesmoke: [245,245,245], yellow: [255,255,0], yellowgreen: [154,205,50] }; var reverseKeywords = {}; for (var key in cssKeywords) { reverseKeywords[JSON.stringify(cssKeywords[key])] = key; } },{}],4:[function(require,module,exports){ var conversions = require(3); var convert = function() { return new Converter(); } for (var func in conversions) { // export Raw versions convert[func + "Raw"] = (function(func) { // accept array or plain args return function(arg) { if (typeof arg == "number") arg = Array.prototype.slice.call(arguments); return conversions[func](arg); } })(func); var pair = /(\w+)2(\w+)/.exec(func), from = pair[1], to = pair[2]; // export rgb2hsl and ["rgb"]["hsl"] convert[from] = convert[from] || {}; convert[from][to] = convert[func] = (function(func) { return function(arg) { if (typeof arg == "number") arg = Array.prototype.slice.call(arguments); var val = conversions[func](arg); if (typeof val == "string" || val === undefined) return val; // keyword for (var i = 0; i < val.length; i++) val[i] = Math.round(val[i]); return val; } })(func); } /* Converter does lazy conversion and caching */ var Converter = function() { this.convs = {}; }; /* Either get the values for a space or set the values for a space, depending on args */ Converter.prototype.routeSpace = function(space, args) { var values = args[0]; if (values === undefined) { // color.rgb() return this.getValues(space); } // color.rgb(10, 10, 10) if (typeof values == "number") { values = Array.prototype.slice.call(args); } return this.setValues(space, values); }; /* Set the values for a space, invalidating cache */ Converter.prototype.setValues = function(space, values) { this.space = space; this.convs = {}; this.convs[space] = values; return this; }; /* Get the values for a space. If there's already a conversion for the space, fetch it, otherwise compute it */ Converter.prototype.getValues = function(space) { var vals = this.convs[space]; if (!vals) { var fspace = this.space, from = this.convs[fspace]; vals = convert[fspace][space](from); this.convs[space] = vals; } return vals; }; ["rgb", "hsl", "hsv", "cmyk", "keyword"].forEach(function(space) { Converter.prototype[space] = function(vals) { return this.routeSpace(space, arguments); } }); module.exports = convert; },{"3":3}],5:[function(require,module,exports){ 'use strict' module.exports = { "aliceblue": [240, 248, 255], "antiquewhite": [250, 235, 215], "aqua": [0, 255, 255], "aquamarine": [127, 255, 212], "azure": [240, 255, 255], "beige": [245, 245, 220], "bisque": [255, 228, 196], "black": [0, 0, 0], "blanchedalmond": [255, 235, 205], "blue": [0, 0, 255], "blueviolet": [138, 43, 226], "brown": [165, 42, 42], "burlywood": [222, 184, 135], "cadetblue": [95, 158, 160], "chartreuse": [127, 255, 0], "chocolate": [210, 105, 30], "coral": [255, 127, 80], "cornflowerblue": [100, 149, 237], "cornsilk": [255, 248, 220], "crimson": [220, 20, 60], "cyan": [0, 255, 255], "darkblue": [0, 0, 139], "darkcyan": [0, 139, 139], "darkgoldenrod": [184, 134, 11], "darkgray": [169, 169, 169], "darkgreen": [0, 100, 0], "darkgrey": [169, 169, 169], "darkkhaki": [189, 183, 107], "darkmagenta": [139, 0, 139], "darkolivegreen": [85, 107, 47], "darkorange": [255, 140, 0], "darkorchid": [153, 50, 204], "darkred": [139, 0, 0], "darksalmon": [233, 150, 122], "darkseagreen": [143, 188, 143], "darkslateblue": [72, 61, 139], "darkslategray": [47, 79, 79], "darkslategrey": [47, 79, 79], "darkturquoise": [0, 206, 209], "darkviolet": [148, 0, 211], "deeppink": [255, 20, 147], "deepskyblue": [0, 191, 255], "dimgray": [105, 105, 105], "dimgrey": [105, 105, 105], "dodgerblue": [30, 144, 255], "firebrick": [178, 34, 34], "floralwhite": [255, 250, 240], "forestgreen": [34, 139, 34], "fuchsia": [255, 0, 255], "gainsboro": [220, 220, 220], "ghostwhite": [248, 248, 255], "gold": [255, 215, 0], "goldenrod": [218, 165, 32], "gray": [128, 128, 128], "green": [0, 128, 0], "greenyellow": [173, 255, 47], "grey": [128, 128, 128], "honeydew": [240, 255, 240], "hotpink": [255, 105, 180], "indianred": [205, 92, 92], "indigo": [75, 0, 130], "ivory": [255, 255, 240], "khaki": [240, 230, 140], "lavender": [230, 230, 250], "lavenderblush": [255, 240, 245], "lawngreen": [124, 252, 0], "lemonchiffon": [255, 250, 205], "lightblue": [173, 216, 230], "lightcoral": [240, 128, 128], "lightcyan": [224, 255, 255], "lightgoldenrodyellow": [250, 250, 210], "lightgray": [211, 211, 211], "lightgreen": [144, 238, 144], "lightgrey": [211, 211, 211], "lightpink": [255, 182, 193], "lightsalmon": [255, 160, 122], "lightseagreen": [32, 178, 170], "lightskyblue": [135, 206, 250], "lightslategray": [119, 136, 153], "lightslategrey": [119, 136, 153], "lightsteelblue": [176, 196, 222], "lightyellow": [255, 255, 224], "lime": [0, 255, 0], "limegreen": [50, 205, 50], "linen": [250, 240, 230], "magenta": [255, 0, 255], "maroon": [128, 0, 0], "mediumaquamarine": [102, 205, 170], "mediumblue": [0, 0, 205], "mediumorchid": [186, 85, 211], "mediumpurple": [147, 112, 219], "mediumseagreen": [60, 179, 113], "mediumslateblue": [123, 104, 238], "mediumspringgreen": [0, 250, 154], "mediumturquoise": [72, 209, 204], "mediumvioletred": [199, 21, 133], "midnightblue": [25, 25, 112], "mintcream": [245, 255, 250], "mistyrose": [255, 228, 225], "moccasin": [255, 228, 181], "navajowhite": [255, 222, 173], "navy": [0, 0, 128], "oldlace": [253, 245, 230], "olive": [128, 128, 0], "olivedrab": [107, 142, 35], "orange": [255, 165, 0], "orangered": [255, 69, 0], "orchid": [218, 112, 214], "palegoldenrod": [238, 232, 170], "palegreen": [152, 251, 152], "paleturquoise": [175, 238, 238], "palevioletred": [219, 112, 147], "papayawhip": [255, 239, 213], "peachpuff": [255, 218, 185], "peru": [205, 133, 63], "pink": [255, 192, 203], "plum": [221, 160, 221], "powderblue": [176, 224, 230], "purple": [128, 0, 128], "rebeccapurple": [102, 51, 153], "red": [255, 0, 0], "rosybrown": [188, 143, 143], "royalblue": [65, 105, 225], "saddlebrown": [139, 69, 19], "salmon": [250, 128, 114], "sandybrown": [244, 164, 96], "seagreen": [46, 139, 87], "seashell": [255, 245, 238], "sienna": [160, 82, 45], "silver": [192, 192, 192], "skyblue": [135, 206, 235], "slateblue": [106, 90, 205], "slategray": [112, 128, 144], "slategrey": [112, 128, 144], "snow": [255, 250, 250], "springgreen": [0, 255, 127], "steelblue": [70, 130, 180], "tan": [210, 180, 140], "teal": [0, 128, 128], "thistle": [216, 191, 216], "tomato": [255, 99, 71], "turquoise": [64, 224, 208], "violet": [238, 130, 238], "wheat": [245, 222, 179], "white": [255, 255, 255], "whitesmoke": [245, 245, 245], "yellow": [255, 255, 0], "yellowgreen": [154, 205, 50] }; },{}],6:[function(require,module,exports){ //! moment.js //! version : 2.18.1 //! authors : Tim Wood, Iskren Chernev, Moment.js contributors //! license : MIT //! momentjs.com ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof define === 'function' && define.amd ? define(factory) : global.moment = factory() }(this, (function () { 'use strict'; var hookCallback; function hooks () { return hookCallback.apply(null, arguments); } // This is done to register the method called with moment() // without creating circular dependencies. function setHookCallback (callback) { hookCallback = callback; } function isArray(input) { return input instanceof Array || Object.prototype.toString.call(input) === '[object Array]'; } function isObject(input) { // IE8 will treat undefined and null as object if it wasn't for // input != null return input != null && Object.prototype.toString.call(input) === '[object Object]'; } function isObjectEmpty(obj) { var k; for (k in obj) { // even if its not own property I'd still call it non-empty return false; } return true; } function isUndefined(input) { return input === void 0; } function isNumber(input) { return typeof input === 'number' || Object.prototype.toString.call(input) === '[object Number]'; } function isDate(input) { return input instanceof Date || Object.prototype.toString.call(input) === '[object Date]'; } function map(arr, fn) { var res = [], i; for (i = 0; i < arr.length; ++i) { res.push(fn(arr[i], i)); } return res; } function hasOwnProp(a, b) { return Object.prototype.hasOwnProperty.call(a, b); } function extend(a, b) { for (var i in b) { if (hasOwnProp(b, i)) { a[i] = b[i]; } } if (hasOwnProp(b, 'toString')) { a.toString = b.toString; } if (hasOwnProp(b, 'valueOf')) { a.valueOf = b.valueOf; } return a; } function createUTC (input, format, locale, strict) { return createLocalOrUTC(input, format, locale, strict, true).utc(); } function defaultParsingFlags() { // We need to deep clone this object. return { empty : false, unusedTokens : [], unusedInput : [], overflow : -2, charsLeftOver : 0, nullInput : false, invalidMonth : null, invalidFormat : false, userInvalidated : false, iso : false, parsedDateParts : [], meridiem : null, rfc2822 : false, weekdayMismatch : false }; } function getParsingFlags(m) { if (m._pf == null) { m._pf = defaultParsingFlags(); } return m._pf; } var some; if (Array.prototype.some) { some = Array.prototype.some; } else { some = function (fun) { var t = Object(this); var len = t.length >>> 0; for (var i = 0; i < len; i++) { if (i in t && fun.call(this, t[i], i, t)) { return true; } } return false; }; } var some$1 = some; function isValid(m) { if (m._isValid == null) { var flags = getParsingFlags(m); var parsedParts = some$1.call(flags.parsedDateParts, function (i) { return i != null; }); var isNowValid = !isNaN(m._d.getTime()) && flags.overflow < 0 && !flags.empty && !flags.invalidMonth && !flags.invalidWeekday && !flags.nullInput && !flags.invalidFormat && !flags.userInvalidated && (!flags.meridiem || (flags.meridiem && parsedParts)); if (m._strict) { isNowValid = isNowValid && flags.charsLeftOver === 0 && flags.unusedTokens.length === 0 && flags.bigHour === undefined; } if (Object.isFrozen == null || !Object.isFrozen(m)) { m._isValid = isNowValid; } else { return isNowValid; } } return m._isValid; } function createInvalid (flags) { var m = createUTC(NaN); if (flags != null) { extend(getParsingFlags(m), flags); } else { getParsingFlags(m).userInvalidated = true; } return m; } // Plugins that add properties should also add the key here (null value), // so we can properly clone ourselves. var momentProperties = hooks.momentProperties = []; function copyConfig(to, from) { var i, prop, val; if (!isUndefined(from._isAMomentObject)) { to._isAMomentObject = from._isAMomentObject; } if (!isUndefined(from._i)) { to._i = from._i; } if (!isUndefined(from._f)) { to._f = from._f; } if (!isUndefined(from._l)) { to._l = from._l; } if (!isUndefined(from._strict)) { to._strict = from._strict; } if (!isUndefined(from._tzm)) { to._tzm = from._tzm; } if (!isUndefined(from._isUTC)) { to._isUTC = from._isUTC; } if (!isUndefined(from._offset)) { to._offset = from._offset; } if (!isUndefined(from._pf)) { to._pf = getParsingFlags(from); } if (!isUndefined(from._locale)) { to._locale = from._locale; } if (momentProperties.length > 0) { for (i = 0; i < momentProperties.length; i++) { prop = momentProperties[i]; val = from[prop]; if (!isUndefined(val)) { to[prop] = val; } } } return to; } var updateInProgress = false; // Moment prototype object function Moment(config) { copyConfig(this, config); this._d = new Date(config._d != null ? config._d.getTime() : NaN); if (!this.isValid()) { this._d = new Date(NaN); } // Prevent infinite loop in case updateOffset creates new moment // objects. if (updateInProgress === false) { updateInProgress = true; hooks.updateOffset(this); updateInProgress = false; } } function isMoment (obj) { return obj instanceof Moment || (obj != null && obj._isAMomentObject != null); } function absFloor (number) { if (number < 0) { // -0 -> 0 return Math.ceil(number) || 0; } else { return Math.floor(number); } } function toInt(argumentForCoercion) { var coercedNumber = +argumentForCoercion, value = 0; if (coercedNumber !== 0 && isFinite(coercedNumber)) { value = absFloor(coercedNumber); } return value; } // compare two arrays, return the number of differences function compareArrays(array1, array2, dontConvert) { var len = Math.min(array1.length, array2.length), lengthDiff = Math.abs(array1.length - array2.length), diffs = 0, i; for (i = 0; i < len; i++) { if ((dontConvert && array1[i] !== array2[i]) || (!dontConvert && toInt(array1[i]) !== toInt(array2[i]))) { diffs++; } } return diffs + lengthDiff; } function warn(msg) { if (hooks.suppressDeprecationWarnings === false && (typeof console !== 'undefined') && console.warn) { console.warn('Deprecation warning: ' + msg); } } function deprecate(msg, fn) { var firstTime = true; return extend(function () { if (hooks.deprecationHandler != null) { hooks.deprecationHandler(null, msg); } if (firstTime) { var args = []; var arg; for (var i = 0; i < arguments.length; i++) { arg = ''; if (typeof arguments[i] === 'object') { arg += '\n[' + i + '] '; for (var key in arguments[0]) { arg += key + ': ' + arguments[0][key] + ', '; } arg = arg.slice(0, -2); // Remove trailing comma and space } else { arg = arguments[i]; } args.push(arg); } warn(msg + '\nArguments: ' + Array.prototype.slice.call(args).join('') + '\n' + (new Error()).stack); firstTime = false; } return fn.apply(this, arguments); }, fn); } var deprecations = {}; function deprecateSimple(name, msg) { if (hooks.deprecationHandler != null) { hooks.deprecationHandler(name, msg); } if (!deprecations[name]) { warn(msg); deprecations[name] = true; } } hooks.suppressDeprecationWarnings = false; hooks.deprecationHandler = null; function isFunction(input) { return input instanceof Function || Object.prototype.toString.call(input) === '[object Function]'; } function set (config) { var prop, i; for (i in config) { prop = config[i]; if (isFunction(prop)) { this[i] = prop; } else { this['_' + i] = prop; } } this._config = config; // Lenient ordinal parsing accepts just a number in addition to // number + (possibly) stuff coming from _dayOfMonthOrdinalParse. // TODO: Remove "ordinalParse" fallback in next major release. this._dayOfMonthOrdinalParseLenient = new RegExp( (this._dayOfMonthOrdinalParse.source || this._ordinalParse.source) + '|' + (/\d{1,2}/).source); } function mergeConfigs(parentConfig, childConfig) { var res = extend({}, parentConfig), prop; for (prop in childConfig) { if (hasOwnProp(childConfig, prop)) { if (isObject(parentConfig[prop]) && isObject(childConfig[prop])) { res[prop] = {}; extend(res[prop], parentConfig[prop]); extend(res[prop], childConfig[prop]); } else if (childConfig[prop] != null) { res[prop] = childConfig[prop]; } else { delete res[prop]; } } } for (prop in parentConfig) { if (hasOwnProp(parentConfig, prop) && !hasOwnProp(childConfig, prop) && isObject(parentConfig[prop])) { // make sure changes to properties don't modify parent config res[prop] = extend({}, res[prop]); } } return res; } function Locale(config) { if (config != null) { this.set(config); } } var keys; if (Object.keys) { keys = Object.keys; } else { keys = function (obj) { var i, res = []; for (i in obj) { if (hasOwnProp(obj, i)) { res.push(i); } } return res; }; } var keys$1 = keys; var defaultCalendar = { sameDay : '[Today at] LT', nextDay : '[Tomorrow at] LT', nextWeek : 'dddd [at] LT', lastDay : '[Yesterday at] LT', lastWeek : '[Last] dddd [at] LT', sameElse : 'L' }; function calendar (key, mom, now) { var output = this._calendar[key] || this._calendar['sameElse']; return isFunction(output) ? output.call(mom, now) : output; } var defaultLongDateFormat = { LTS : 'h:mm:ss A', LT : 'h:mm A', L : 'MM/DD/YYYY', LL : 'MMMM D, YYYY', LLL : 'MMMM D, YYYY h:mm A', LLLL : 'dddd, MMMM D, YYYY h:mm A' }; function longDateFormat (key) { var format = this._longDateFormat[key], formatUpper = this._longDateFormat[key.toUpperCase()]; if (format || !formatUpper) { return format; } this._longDateFormat[key] = formatUpper.replace(/MMMM|MM|DD|dddd/g, function (val) { return val.slice(1); }); return this._longDateFormat[key]; } var defaultInvalidDate = 'Invalid date'; function invalidDate () { return this._invalidDate; } var defaultOrdinal = '%d'; var defaultDayOfMonthOrdinalParse = /\d{1,2}/; function ordinal (number) { return this._ordinal.replace('%d', number); } var defaultRelativeTime = { future : 'in %s', past : '%s ago', s : 'a few seconds', ss : '%d seconds', m : 'a minute', mm : '%d minutes', h : 'an hour', hh : '%d hours', d : 'a day', dd : '%d days', M : 'a month', MM : '%d months', y : 'a year', yy : '%d years' }; function relativeTime (number, withoutSuffix, string, isFuture) { var output = this._relativeTime[string]; return (isFunction(output)) ? output(number, withoutSuffix, string, isFuture) : output.replace(/%d/i, number); } function pastFuture (diff, output) { var format = this._relativeTime[diff > 0 ? 'future' : 'past']; return isFunction(format) ? format(output) : format.replace(/%s/i, output); } var aliases = {}; function addUnitAlias (unit, shorthand) { var lowerCase = unit.toLowerCase(); aliases[lowerCase] = aliases[lowerCase + 's'] = aliases[shorthand] = unit; } function normalizeUnits(units) { return typeof units === 'string' ? aliases[units] || aliases[units.toLowerCase()] : undefined; } function normalizeObjectUnits(inputObject) { var normalizedInput = {}, normalizedProp, prop; for (prop in inputObject) { if (hasOwnProp(inputObject, prop)) { normalizedProp = normalizeUnits(prop); if (normalizedProp) { normalizedInput[normalizedProp] = inputObject[prop]; } } } return normalizedInput; } var priorities = {}; function addUnitPriority(unit, priority) { priorities[unit] = priority; } function getPrioritizedUnits(unitsObj) { var units = []; for (var u in unitsObj) { units.push({unit: u, priority: priorities[u]}); } units.sort(function (a, b) { return a.priority - b.priority; }); return units; } function makeGetSet (unit, keepTime) { return function (value) { if (value != null) { set$1(this, unit, value); hooks.updateOffset(this, keepTime); return this; } else { return get(this, unit); } }; } function get (mom, unit) { return mom.isValid() ? mom._d['get' + (mom._isUTC ? 'UTC' : '') + unit]() : NaN; } function set$1 (mom, unit, value) { if (mom.isValid()) { mom._d['set' + (mom._isUTC ? 'UTC' : '') + unit](value); } } // MOMENTS function stringGet (units) { units = normalizeUnits(units); if (isFunction(this[units])) { return this[units](); } return this; } function stringSet (units, value) { if (typeof units === 'object') { units = normalizeObjectUnits(units); var prioritized = getPrioritizedUnits(units); for (var i = 0; i < prioritized.length; i++) { this[prioritized[i].unit](units[prioritized[i].unit]); } } else { units = normalizeUnits(units); if (isFunction(this[units])) { return this[units](value); } } return this; } function zeroFill(number, targetLength, forceSign) { var absNumber = '' + Math.abs(number), zerosToFill = targetLength - absNumber.length, sign = number >= 0; return (sign ? (forceSign ? '+' : '') : '-') + Math.pow(10, Math.max(0, zerosToFill)).toString().substr(1) + absNumber; } var formattingTokens = /(\[[^\[]*\])|(\\)?([Hh]mm(ss)?|Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Qo?|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|kk?|mm?|ss?|S{1,9}|x|X|zz?|ZZ?|.)/g; var localFormattingTokens = /(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g; var formatFunctions = {}; var formatTokenFunctions = {}; // token: 'M' // padded: ['MM', 2] // ordinal: 'Mo' // callback: function () { this.month() + 1 } function addFormatToken (token, padded, ordinal, callback) { var func = callback; if (typeof callback === 'string') { func = function () { return this[callback](); }; } if (token) { formatTokenFunctions[token] = func; } if (padded) { formatTokenFunctions[padded[0]] = function () { return zeroFill(func.apply(this, arguments), padded[1], padded[2]); }; } if (ordinal) { formatTokenFunctions[ordinal] = function () { return this.localeData().ordinal(func.apply(this, arguments), token); }; } } function removeFormattingTokens(input) { if (input.match(/\[[\s\S]/)) { return input.replace(/^\[|\]$/g, ''); } return input.replace(/\\/g, ''); } function makeFormatFunction(format) { var array = format.match(formattingTokens), i, length; for (i = 0, length = array.length; i < length; i++) { if (formatTokenFunctions[array[i]]) { array[i] = formatTokenFunctions[array[i]]; } else { array[i] = removeFormattingTokens(array[i]); } } return function (mom) { var output = '', i; for (i = 0; i < length; i++) { output += isFunction(array[i]) ? array[i].call(mom, format) : array[i]; } return output; }; } // format date using native date object function formatMoment(m, format) { if (!m.isValid()) { return m.localeData().invalidDate(); } format = expandFormat(format, m.localeData()); formatFunctions[format] = formatFunctions[format] || makeFormatFunction(format); return formatFunctions[format](m); } function expandFormat(format, locale) { var i = 5; function replaceLongDateFormatTokens(input) { return locale.longDateFormat(input) || input; } localFormattingTokens.lastIndex = 0; while (i >= 0 && localFormattingTokens.test(format)) { format = format.replace(localFormattingTokens, replaceLongDateFormatTokens); localFormattingTokens.lastIndex = 0; i -= 1; } return format; } var match1 = /\d/; // 0 - 9 var match2 = /\d\d/; // 00 - 99 var match3 = /\d{3}/; // 000 - 999 var match4 = /\d{4}/; // 0000 - 9999 var match6 = /[+-]?\d{6}/; // -999999 - 999999 var match1to2 = /\d\d?/; // 0 - 99 var match3to4 = /\d\d\d\d?/; // 999 - 9999 var match5to6 = /\d\d\d\d\d\d?/; // 99999 - 999999 var match1to3 = /\d{1,3}/; // 0 - 999 var match1to4 = /\d{1,4}/; // 0 - 9999 var match1to6 = /[+-]?\d{1,6}/; // -999999 - 999999 var matchUnsigned = /\d+/; // 0 - inf var matchSigned = /[+-]?\d+/; // -inf - inf var matchOffset = /Z|[+-]\d\d:?\d\d/gi; // +00:00 -00:00 +0000 -0000 or Z var matchShortOffset = /Z|[+-]\d\d(?::?\d\d)?/gi; // +00 -00 +00:00 -00:00 +0000 -0000 or Z var matchTimestamp = /[+-]?\d+(\.\d{1,3})?/; // 123456789 123456789.123 // any word (or two) characters or numbers including two/three word month in arabic. // includes scottish gaelic two word and hyphenated months var matchWord = /[0-9]*['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+|[\u0600-\u06FF\/]+(\s*?[\u0600-\u06FF]+){1,2}/i; var regexes = {}; function addRegexToken (token, regex, strictRegex) { regexes[token] = isFunction(regex) ? regex : function (isStrict, localeData) { return (isStrict && strictRegex) ? strictRegex : regex; }; } function getParseRegexForToken (token, config) { if (!hasOwnProp(regexes, token)) { return new RegExp(unescapeFormat(token)); } return regexes[token](config._strict, config._locale); } // Code from http://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript function unescapeFormat(s) { return regexEscape(s.replace('\\', '').replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g, function (matched, p1, p2, p3, p4) { return p1 || p2 || p3 || p4; })); } function regexEscape(s) { return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); } var tokens = {}; function addParseToken (token, callback) { var i, func = callback; if (typeof token === 'string') { token = [token]; } if (isNumber(callback)) { func = function (input, array) { array[callback] = toInt(input); }; } for (i = 0; i < token.length; i++) { tokens[token[i]] = func; } } function addWeekParseToken (token, callback) { addParseToken(token, function (input, array, config, token) { config._w = config._w || {}; callback(input, config._w, config, token); }); } function addTimeToArrayFromToken(token, input, config) { if (input != null && hasOwnProp(tokens, token)) { tokens[token](input, config._a, config, token); } } var YEAR = 0; var MONTH = 1; var DATE = 2; var HOUR = 3; var MINUTE = 4; var SECOND = 5; var MILLISECOND = 6; var WEEK = 7; var WEEKDAY = 8; var indexOf; if (Array.prototype.indexOf) { indexOf = Array.prototype.indexOf; } else { indexOf = function (o) { // I know var i; for (i = 0; i < this.length; ++i) { if (this[i] === o) { return i; } } return -1; }; } var indexOf$1 = indexOf; function daysInMonth(year, month) { return new Date(Date.UTC(year, month + 1, 0)).getUTCDate(); } // FORMATTING addFormatToken('M', ['MM', 2], 'Mo', function () { return this.month() + 1; }); addFormatToken('MMM', 0, 0, function (format) { return this.localeData().monthsShort(this, format); }); addFormatToken('MMMM', 0, 0, function (format) { return this.localeData().months(this, format); }); // ALIASES addUnitAlias('month', 'M'); // PRIORITY addUnitPriority('month', 8); // PARSING addRegexToken('M', match1to2); addRegexToken('MM', match1to2, match2); addRegexToken('MMM', function (isStrict, locale) { return locale.monthsShortRegex(isStrict); }); addRegexToken('MMMM', function (isStrict, locale) { return locale.monthsRegex(isStrict); }); addParseToken(['M', 'MM'], function (input, array) { array[MONTH] = toInt(input) - 1; }); addParseToken(['MMM', 'MMMM'], function (input, array, config, token) { var month = config._locale.monthsParse(input, token, config._strict); // if we didn't find a month name, mark the date as invalid. if (month != null) { array[MONTH] = month; } else { getParsingFlags(config).invalidMonth = input; } }); // LOCALES var MONTHS_IN_FORMAT = /D[oD]?(\[[^\[\]]*\]|\s)+MMMM?/; var defaultLocaleMonths = 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_'); function localeMonths (m, format) { if (!m) { return isArray(this._months) ? this._months : this._months['standalone']; } return isArray(this._months) ? this._months[m.month()] : this._months[(this._months.isFormat || MONTHS_IN_FORMAT).test(format) ? 'format' : 'standalone'][m.month()]; } var defaultLocaleMonthsShort = 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split('_'); function localeMonthsShort (m, format) { if (!m) { return isArray(this._monthsShort) ? this._monthsShort : this._monthsShort['standalone']; } return isArray(this._monthsShort) ? this._monthsShort[m.month()] : this._monthsShort[MONTHS_IN_FORMAT.test(format) ? 'format' : 'standalone'][m.month()]; } function handleStrictParse(monthName, format, strict) { var i, ii, mom, llc = monthName.toLocaleLowerCase(); if (!this._monthsParse) { // this is not used this._monthsParse = []; this._longMonthsParse = []; this._shortMonthsParse = []; for (i = 0; i < 12; ++i) { mom = createUTC([2000, i]); this._shortMonthsParse[i] = this.monthsShort(mom, '').toLocaleLowerCase(); this._longMonthsParse[i] = this.months(mom, '').toLocaleLowerCase(); } } if (strict) { if (format === 'MMM') { ii = indexOf$1.call(this._shortMonthsParse, llc); return ii !== -1 ? ii : null; } else { ii = indexOf$1.call(this._longMonthsParse, llc); return ii !== -1 ? ii : null; } } else { if (format === 'MMM') { ii = indexOf$1.call(this._shortMonthsParse, llc); if (ii !== -1) { return ii; } ii = indexOf$1.call(this._longMonthsParse, llc); return ii !== -1 ? ii : null; } else { ii = indexOf$1.call(this._longMonthsParse, llc); if (ii !== -1) { return ii; } ii = indexOf$1.call(this._shortMonthsParse, llc); return ii !== -1 ? ii : null; } } } function localeMonthsParse (monthName, format, strict) { var i, mom, regex; if (this._monthsParseExact) { return handleStrictParse.call(this, monthName, format, strict); } if (!this._monthsParse) { this._monthsParse = []; this._longMonthsParse = []; this._shortMonthsParse = []; } // TODO: add sorting // Sorting makes sure if one month (or abbr) is a prefix of another // see sorting in computeMonthsParse for (i = 0; i < 12; i++) { // make the regex if we don't have it already mom = createUTC([2000, i]); if (strict && !this._longMonthsParse[i]) { this._longMonthsParse[i] = new RegExp('^' + this.months(mom, '').replace('.', '') + '$', 'i'); this._shortMonthsParse[i] = new RegExp('^' + this.monthsShort(mom, '').replace('.', '') + '$', 'i'); } if (!strict && !this._monthsParse[i]) { regex = '^' + this.months(mom, '') + '|^' + this.monthsShort(mom, ''); this._monthsParse[i] = new RegExp(regex.replace('.', ''), 'i'); } // test the regex if (strict && format === 'MMMM' && this._longMonthsParse[i].test(monthName)) { return i; } else if (strict && format === 'MMM' && this._shortMonthsParse[i].test(monthName)) { return i; } else if (!strict && this._monthsParse[i].test(monthName)) { return i; } } } // MOMENTS function setMonth (mom, value) { var dayOfMonth; if (!mom.isValid()) { // No op return mom; } if (typeof value === 'string') { if (/^\d+$/.test(value)) { value = toInt(value); } else { value = mom.localeData().monthsParse(value); // TODO: Another silent failure? if (!isNumber(value)) { return mom; } } } dayOfMonth = Math.min(mom.date(), daysInMonth(mom.year(), value)); mom._d['set' + (mom._isUTC ? 'UTC' : '') + 'Month'](value, dayOfMonth); return mom; } function getSetMonth (value) { if (value != null) { setMonth(this, value); hooks.updateOffset(this, true); return this; } else { return get(this, 'Month'); } } function getDaysInMonth () { return daysInMonth(this.year(), this.month()); } var defaultMonthsShortRegex = matchWord; function monthsShortRegex (isStrict) { if (this._monthsParseExact) { if (!hasOwnProp(this, '_monthsRegex')) { computeMonthsParse.call(this); } if (isStrict) { return this._monthsShortStrictRegex; } else { return this._monthsShortRegex; } } else { if (!hasOwnProp(this, '_monthsShortRegex')) { this._monthsShortRegex = defaultMonthsShortRegex; } return this._monthsShortStrictRegex && isStrict ? this._monthsShortStrictRegex : this._monthsShortRegex; } } var defaultMonthsRegex = matchWord; function monthsRegex (isStrict) { if (this._monthsParseExact) { if (!hasOwnProp(this, '_monthsRegex')) { computeMonthsParse.call(this); } if (isStrict) { return this._monthsStrictRegex; } else { return this._monthsRegex; } } else { if (!hasOwnProp(this, '_monthsRegex')) { this._monthsRegex = defaultMonthsRegex; } return this._monthsStrictRegex && isStrict ? this._monthsStrictRegex : this._monthsRegex; } } function computeMonthsParse () { function cmpLenRev(a, b) { return b.length - a.length; } var shortPieces = [], longPieces = [], mixedPieces = [], i, mom; for (i = 0; i < 12; i++) { // make the regex if we don't have it already mom = createUTC([2000, i]); shortPieces.push(this.monthsShort(mom, '')); longPieces.push(this.months(mom, '')); mixedPieces.push(this.months(mom, '')); mixedPieces.push(this.monthsShort(mom, '')); } // Sorting makes sure if one month (or abbr) is a prefix of another it // will match the longer piece. shortPieces.sort(cmpLenRev); longPieces.sort(cmpLenRev); mixedPieces.sort(cmpLenRev); for (i = 0; i < 12; i++) { shortPieces[i] = regexEscape(shortPieces[i]); longPieces[i] = regexEscape(longPieces[i]); } for (i = 0; i < 24; i++) { mixedPieces[i] = regexEscape(mixedPieces[i]); } this._monthsRegex = new RegExp('^(' + mixedPieces.join('|') + ')', 'i'); this._monthsShortRegex = this._monthsRegex; this._monthsStrictRegex = new RegExp('^(' + longPieces.join('|') + ')', 'i'); this._monthsShortStrictRegex = new RegExp('^(' + shortPieces.join('|') + ')', 'i'); } // FORMATTING addFormatToken('Y', 0, 0, function () { var y = this.year(); return y <= 9999 ? '' + y : '+' + y; }); addFormatToken(0, ['YY', 2], 0, function () { return this.year() % 100; }); addFormatToken(0, ['YYYY', 4], 0, 'year'); addFormatToken(0, ['YYYYY', 5], 0, 'year'); addFormatToken(0, ['YYYYYY', 6, true], 0, 'year'); // ALIASES addUnitAlias('year', 'y'); // PRIORITIES addUnitPriority('year', 1); // PARSING addRegexToken('Y', matchSigned); addRegexToken('YY', match1to2, match2); addRegexToken('YYYY', match1to4, match4); addRegexToken('YYYYY', match1to6, match6); addRegexToken('YYYYYY', match1to6, match6); addParseToken(['YYYYY', 'YYYYYY'], YEAR); addParseToken('YYYY', function (input, array) { array[YEAR] = input.length === 2 ? hooks.parseTwoDigitYear(input) : toInt(input); }); addParseToken('YY', function (input, array) { array[YEAR] = hooks.parseTwoDigitYear(input); }); addParseToken('Y', function (input, array) { array[YEAR] = parseInt(input, 10); }); // HELPERS function daysInYear(year) { return isLeapYear(year) ? 366 : 365; } function isLeapYear(year) { return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0; } // HOOKS hooks.parseTwoDigitYear = function (input) { return toInt(input) + (toInt(input) > 68 ? 1900 : 2000); }; // MOMENTS var getSetYear = makeGetSet('FullYear', true); function getIsLeapYear () { return isLeapYear(this.year()); } function createDate (y, m, d, h, M, s, ms) { // can't just apply() to create a date: // https://stackoverflow.com/q/181348 var date = new Date(y, m, d, h, M, s, ms); // the date constructor remaps years 0-99 to 1900-1999 if (y < 100 && y >= 0 && isFinite(date.getFullYear())) { date.setFullYear(y); } return date; } function createUTCDate (y) { var date = new Date(Date.UTC.apply(null, arguments)); // the Date.UTC function remaps years 0-99 to 1900-1999 if (y < 100 && y >= 0 && isFinite(date.getUTCFullYear())) { date.setUTCFullYear(y); } return date; } // start-of-first-week - start-of-year function firstWeekOffset(year, dow, doy) { var // first-week day -- which january is always in the first week (4 for iso, 1 for other) fwd = 7 + dow - doy, // first-week day local weekday -- which local weekday is fwd fwdlw = (7 + createUTCDate(year, 0, fwd).getUTCDay() - dow) % 7; return -fwdlw + fwd - 1; } // https://en.wikipedia.org/wiki/ISO_week_date#Calculating_a_date_given_the_year.2C_week_number_and_weekday function dayOfYearFromWeeks(year, week, weekday, dow, doy) { var localWeekday = (7 + weekday - dow) % 7, weekOffset = firstWeekOffset(year, dow, doy), dayOfYear = 1 + 7 * (week - 1) + localWeekday + weekOffset, resYear, resDayOfYear; if (dayOfYear <= 0) { resYear = year - 1; resDayOfYear = daysInYear(resYear) + dayOfYear; } else if (dayOfYear > daysInYear(year)) { resYear = year + 1; resDayOfYear = dayOfYear - daysInYear(year); } else { resYear = year; resDayOfYear = dayOfYear; } return { year: resYear, dayOfYear: resDayOfYear }; } function weekOfYear(mom, dow, doy) { var weekOffset = firstWeekOffset(mom.year(), dow, doy), week = Math.floor((mom.dayOfYear() - weekOffset - 1) / 7) + 1, resWeek, resYear; if (week < 1) { resYear = mom.year() - 1; resWeek = week + weeksInYear(resYear, dow, doy); } else if (week > weeksInYear(mom.year(), dow, doy)) { resWeek = week - weeksInYear(mom.year(), dow, doy); resYear = mom.year() + 1; } else { resYear = mom.year(); resWeek = week; } return { week: resWeek, year: resYear }; } function weeksInYear(year, dow, doy) { var weekOffset = firstWeekOffset(year, dow, doy), weekOffsetNext = firstWeekOffset(year + 1, dow, doy); return (daysInYear(year) - weekOffset + weekOffsetNext) / 7; } // FORMATTING addFormatToken('w', ['ww', 2], 'wo', 'week'); addFormatToken('W', ['WW', 2], 'Wo', 'isoWeek'); // ALIASES addUnitAlias('week', 'w'); addUnitAlias('isoWeek', 'W'); // PRIORITIES addUnitPriority('week', 5); addUnitPriority('isoWeek', 5); // PARSING addRegexToken('w', match1to2); addRegexToken('ww', match1to2, match2); addRegexToken('W', match1to2); addRegexToken('WW', match1to2, match2); addWeekParseToken(['w', 'ww', 'W', 'WW'], function (input, week, config, token) { week[token.substr(0, 1)] = toInt(input); }); // HELPERS // LOCALES function localeWeek (mom) { return weekOfYear(mom, this._week.dow, this._week.doy).week; } var defaultLocaleWeek = { dow : 0, // Sunday is the first day of the week. doy : 6 // The week that contains Jan 1st is the first week of the year. }; function localeFirstDayOfWeek () { return this._week.dow; } function localeFirstDayOfYear () { return this._week.doy; } // MOMENTS function getSetWeek (input) { var week = this.localeData().week(this); return input == null ? week : this.add((input - week) * 7, 'd'); } function getSetISOWeek (input) { var week = weekOfYear(this, 1, 4).week; return input == null ? week : this.add((input - week) * 7, 'd'); } // FORMATTING addFormatToken('d', 0, 'do', 'day'); addFormatToken('dd', 0, 0, function (format) { return this.localeData().weekdaysMin(this, format); }); addFormatToken('ddd', 0, 0, function (format) { return this.localeData().weekdaysShort(this, format); }); addFormatToken('dddd', 0, 0, function (format) { return this.localeData().weekdays(this, format); }); addFormatToken('e', 0, 0, 'weekday'); addFormatToken('E', 0, 0, 'isoWeekday'); // ALIASES addUnitAlias('day', 'd'); addUnitAlias('weekday', 'e'); addUnitAlias('isoWeekday', 'E'); // PRIORITY addUnitPriority('day', 11); addUnitPriority('weekday', 11); addUnitPriority('isoWeekday', 11); // PARSING addRegexToken('d', match1to2); addRegexToken('e', match1to2); addRegexToken('E', match1to2); addRegexToken('dd', function (isStrict, locale) { return locale.weekdaysMinRegex(isStrict); }); addRegexToken('ddd', function (isStrict, locale) { return locale.weekdaysShortRegex(isStrict); }); addRegexToken('dddd', function (isStrict, locale) { return locale.weekdaysRegex(isStrict); }); addWeekParseToken(['dd', 'ddd', 'dddd'], function (input, week, config, token) { var weekday = config._locale.weekdaysParse(input, token, config._strict); // if we didn't get a weekday name, mark the date as invalid if (weekday != null) { week.d = weekday; } else { getParsingFlags(config).invalidWeekday = input; } }); addWeekParseToken(['d', 'e', 'E'], function (input, week, config, token) { week[token] = toInt(input); }); // HELPERS function parseWeekday(input, locale) { if (typeof input !== 'string') { return input; } if (!isNaN(input)) { return parseInt(input, 10); } input = locale.weekdaysParse(input); if (typeof input === 'number') { return input; } return null; } function parseIsoWeekday(input, locale) { if (typeof input === 'string') { return locale.weekdaysParse(input) % 7 || 7; } return isNaN(input) ? null : input; } // LOCALES var defaultLocaleWeekdays = 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_'); function localeWeekdays (m, format) { if (!m) { return isArray(this._weekdays) ? this._weekdays : this._weekdays['standalone']; } return isArray(this._weekdays) ? this._weekdays[m.day()] : this._weekdays[this._weekdays.isFormat.test(format) ? 'format' : 'standalone'][m.day()]; } var defaultLocaleWeekdaysShort = 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_'); function localeWeekdaysShort (m) { return (m) ? this._weekdaysShort[m.day()] : this._weekdaysShort; } var defaultLocaleWeekdaysMin = 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_'); function localeWeekdaysMin (m) { return (m) ? this._weekdaysMin[m.day()] : this._weekdaysMin; } function handleStrictParse$1(weekdayName, format, strict) { var i, ii, mom, llc = weekdayName.toLocaleLowerCase(); if (!this._weekdaysParse) { this._weekdaysParse = []; this._shortWeekdaysParse = []; this._minWeekdaysParse = []; for (i = 0; i < 7; ++i) { mom = createUTC([2000, 1]).day(i); this._minWeekdaysParse[i] = this.weekdaysMin(mom, '').toLocaleLowerCase(); this._shortWeekdaysParse[i] = this.weekdaysShort(mom, '').toLocaleLowerCase(); this._weekdaysParse[i] = this.weekdays(mom, '').toLocaleLowerCase(); } } if (strict) { if (format === 'dddd') { ii = indexOf$1.call(this._weekdaysParse, llc); return ii !== -1 ? ii : null; } else if (format === 'ddd') { ii = indexOf$1.call(this._shortWeekdaysParse, llc); return ii !== -1 ? ii : null; } else { ii = indexOf$1.call(this._minWeekdaysParse, llc); return ii !== -1 ? ii : null; } } else { if (format === 'dddd') { ii = indexOf$1.call(this._weekdaysParse, llc); if (ii !== -1) { return ii; } ii = indexOf$1.call(this._shortWeekdaysParse, llc); if (ii !== -1) { return ii; } ii = indexOf$1.call(this._minWeekdaysParse, llc); return ii !== -1 ? ii : null; } else if (format === 'ddd') { ii = indexOf$1.call(this._shortWeekdaysParse, llc); if (ii !== -1) { return ii; } ii = indexOf$1.call(this._weekdaysParse, llc); if (ii !== -1) { return ii; } ii = indexOf$1.call(this._minWeekdaysParse, llc); return ii !== -1 ? ii : null; } else { ii = indexOf$1.call(this._minWeekdaysParse, llc); if (ii !== -1) { return ii; } ii = indexOf$1.call(this._weekdaysParse, llc); if (ii !== -1) { return ii; } ii = indexOf$1.call(this._shortWeekdaysParse, llc); return ii !== -1 ? ii : null; } } } function localeWeekdaysParse (weekdayName, format, strict) { var i, mom, regex; if (this._weekdaysParseExact) { return handleStrictParse$1.call(this, weekdayName, format, strict); } if (!this._weekdaysParse) { this._weekdaysParse = []; this._minWeekdaysParse = []; this._shortWeekdaysParse = []; this._fullWeekdaysParse = []; } for (i = 0; i < 7; i++) { // make the regex if we don't have it already mom = createUTC([2000, 1]).day(i); if (strict && !this._fullWeekdaysParse[i]) { this._fullWeekdaysParse[i] = new RegExp('^' + this.weekdays(mom, '').replace('.', '\.?') + '$', 'i'); this._shortWeekdaysParse[i] = new RegExp('^' + this.weekdaysShort(mom, '').replace('.', '\.?') + '$', 'i'); this._minWeekdaysParse[i] = new RegExp('^' + this.weekdaysMin(mom, '').replace('.', '\.?') + '$', 'i'); } if (!this._weekdaysParse[i]) { regex = '^' + this.weekdays(mom, '') + '|^' + this.weekdaysShort(mom, '') + '|^' + this.weekdaysMin(mom, ''); this._weekdaysParse[i] = new RegExp(regex.replace('.', ''), 'i'); } // test the regex if (strict && format === 'dddd' && this._fullWeekdaysParse[i].test(weekdayName)) { return i; } else if (strict && format === 'ddd' && this._shortWeekdaysParse[i].test(weekdayName)) { return i; } else if (strict && format === 'dd' && this._minWeekdaysParse[i].test(weekdayName)) { return i; } else if (!strict && this._weekdaysParse[i].test(weekdayName)) { return i; } } } // MOMENTS function getSetDayOfWeek (input) { if (!this.isValid()) { return input != null ? this : NaN; } var day = this._isUTC ? this._d.getUTCDay() : this._d.getDay(); if (input != null) { input = parseWeekday(input, this.localeData()); return this.add(input - day, 'd'); } else { return day; } } function getSetLocaleDayOfWeek (input) { if (!this.isValid()) { return input != null ? this : NaN; } var weekday = (this.day() + 7 - this.localeData()._week.dow) % 7; return input == null ? weekday : this.add(input - weekday, 'd'); } function getSetISODayOfWeek (input) { if (!this.isValid()) { return input != null ? this : NaN; } // behaves the same as moment#day except // as a getter, returns 7 instead of 0 (1-7 range instead of 0-6) // as a setter, sunday should belong to the previous week. if (input != null) { var weekday = parseIsoWeekday(input, this.localeData()); return this.day(this.day() % 7 ? weekday : weekday - 7); } else { return this.day() || 7; } } var defaultWeekdaysRegex = matchWord; function weekdaysRegex (isStrict) { if (this._weekdaysParseExact) { if (!hasOwnProp(this, '_weekdaysRegex')) { computeWeekdaysParse.call(this); } if (isStrict) { return this._weekdaysStrictRegex; } else { return this._weekdaysRegex; } } else { if (!hasOwnProp(this, '_weekdaysRegex')) { this._weekdaysRegex = defaultWeekdaysRegex; } return this._weekdaysStrictRegex && isStrict ? this._weekdaysStrictRegex : this._weekdaysRegex; } } var defaultWeekdaysShortRegex = matchWord; function weekdaysShortRegex (isStrict) { if (this._weekdaysParseExact) { if (!hasOwnProp(this, '_weekdaysRegex')) { computeWeekdaysParse.call(this); } if (isStrict) { return this._weekdaysShortStrictRegex; } else { return this._weekdaysShortRegex; } } else { if (!hasOwnProp(this, '_weekdaysShortRegex')) { this._weekdaysShortRegex = defaultWeekdaysShortRegex; } return this._weekdaysShortStrictRegex && isStrict ? this._weekdaysShortStrictRegex : this._weekdaysShortRegex; } } var defaultWeekdaysMinRegex = matchWord; function weekdaysMinRegex (isStrict) { if (this._weekdaysParseExact) { if (!hasOwnProp(this, '_weekdaysRegex')) { computeWeekdaysParse.call(this); } if (isStrict) { return this._weekdaysMinStrictRegex; } else { return this._weekdaysMinRegex; } } else { if (!hasOwnProp(this, '_weekdaysMinRegex')) { this._weekdaysMinRegex = defaultWeekdaysMinRegex; } return this._weekdaysMinStrictRegex && isStrict ? this._weekdaysMinStrictRegex : this._weekdaysMinRegex; } } function computeWeekdaysParse () { function cmpLenRev(a, b) { return b.length - a.length; } var minPieces = [], shortPieces = [], longPieces = [], mixedPieces = [], i, mom, minp, shortp, longp; for (i = 0; i < 7; i++) { // make the regex if we don't have it already mom = createUTC([2000, 1]).day(i); minp = this.weekdaysMin(mom, ''); shortp = this.weekdaysShort(mom, ''); longp = this.weekdays(mom, ''); minPieces.push(minp); shortPieces.push(shortp); longPieces.push(longp); mixedPieces.push(minp); mixedPieces.push(shortp); mixedPieces.push(longp); } // Sorting makes sure if one weekday (or abbr) is a prefix of another it // will match the longer piece. minPieces.sort(cmpLenRev); shortPieces.sort(cmpLenRev); longPieces.sort(cmpLenRev); mixedPieces.sort(cmpLenRev); for (i = 0; i < 7; i++) { shortPieces[i] = regexEscape(shortPieces[i]); longPieces[i] = regexEscape(longPieces[i]); mixedPieces[i] = regexEscape(mixedPieces[i]); } this._weekdaysRegex = new RegExp('^(' + mixedPieces.join('|') + ')', 'i'); this._weekdaysShortRegex = this._weekdaysRegex; this._weekdaysMinRegex = this._weekdaysRegex; this._weekdaysStrictRegex = new RegExp('^(' + longPieces.join('|') + ')', 'i'); this._weekdaysShortStrictRegex = new RegExp('^(' + shortPieces.join('|') + ')', 'i'); this._weekdaysMinStrictRegex = new RegExp('^(' + minPieces.join('|') + ')', 'i'); } // FORMATTING function hFormat() { return this.hours() % 12 || 12; } function kFormat() { return this.hours() || 24; } addFormatToken('H', ['HH', 2], 0, 'hour'); addFormatToken('h', ['hh', 2], 0, hFormat); addFormatToken('k', ['kk', 2], 0, kFormat); addFormatToken('hmm', 0, 0, function () { return '' + hFormat.apply(this) + zeroFill(this.minutes(), 2); }); addFormatToken('hmmss', 0, 0, function () { return '' + hFormat.apply(this) + zeroFill(this.minutes(), 2) + zeroFill(this.seconds(), 2); }); addFormatToken('Hmm', 0, 0, function () { return '' + this.hours() + zeroFill(this.minutes(), 2); }); addFormatToken('Hmmss', 0, 0, function () { return '' + this.hours() + zeroFill(this.minutes(), 2) + zeroFill(this.seconds(), 2); }); function meridiem (token, lowercase) { addFormatToken(token, 0, 0, function () { return this.localeData().meridiem(this.hours(), this.minutes(), lowercase); }); } meridiem('a', true); meridiem('A', false); // ALIASES addUnitAlias('hour', 'h'); // PRIORITY addUnitPriority('hour', 13); // PARSING function matchMeridiem (isStrict, locale) { return locale._meridiemParse; } addRegexToken('a', matchMeridiem); addRegexToken('A', matchMeridiem); addRegexToken('H', match1to2); addRegexToken('h', match1to2); addRegexToken('k', match1to2); addRegexToken('HH', match1to2, match2); addRegexToken('hh', match1to2, match2); addRegexToken('kk', match1to2, match2); addRegexToken('hmm', match3to4); addRegexToken('hmmss', match5to6); addRegexToken('Hmm', match3to4); addRegexToken('Hmmss', match5to6); addParseToken(['H', 'HH'], HOUR); addParseToken(['k', 'kk'], function (input, array, config) { var kInput = toInt(input); array[HOUR] = kInput === 24 ? 0 : kInput; }); addParseToken(['a', 'A'], function (input, array, config) { config._isPm = config._locale.isPM(input); config._meridiem = input; }); addParseToken(['h', 'hh'], function (input, array, config) { array[HOUR] = toInt(input); getParsingFlags(config).bigHour = true; }); addParseToken('hmm', function (input, array, config) { var pos = input.length - 2; array[HOUR] = toInt(input.substr(0, pos)); array[MINUTE] = toInt(input.substr(pos)); getParsingFlags(config).bigHour = true; }); addParseToken('hmmss', function (input, array, config) { var pos1 = input.length - 4; var pos2 = input.length - 2; array[HOUR] = toInt(input.substr(0, pos1)); array[MINUTE] = toInt(input.substr(pos1, 2)); array[SECOND] = toInt(input.substr(pos2)); getParsingFlags(config).bigHour = true; }); addParseToken('Hmm', function (input, array, config) { var pos = input.length - 2; array[HOUR] = toInt(input.substr(0, pos)); array[MINUTE] = toInt(input.substr(pos)); }); addParseToken('Hmmss', function (input, array, config) { var pos1 = input.length - 4; var pos2 = input.length - 2; array[HOUR] = toInt(input.substr(0, pos1)); array[MINUTE] = toInt(input.substr(pos1, 2)); array[SECOND] = toInt(input.substr(pos2)); }); // LOCALES function localeIsPM (input) { // IE8 Quirks Mode & IE7 Standards Mode do not allow accessing strings like arrays // Using charAt should be more compatible. return ((input + '').toLowerCase().charAt(0) === 'p'); } var defaultLocaleMeridiemParse = /[ap]\.?m?\.?/i; function localeMeridiem (hours, minutes, isLower) { if (hours > 11) { return isLower ? 'pm' : 'PM'; } else { return isLower ? 'am' : 'AM'; } } // MOMENTS // Setting the hour should keep the time, because the user explicitly // specified which hour he wants. So trying to maintain the same hour (in // a new timezone) makes sense. Adding/subtracting hours does not follow // this rule. var getSetHour = makeGetSet('Hours', true); // months // week // weekdays // meridiem var baseConfig = { calendar: defaultCalendar, longDateFormat: defaultLongDateFormat, invalidDate: defaultInvalidDate, ordinal: defaultOrdinal, dayOfMonthOrdinalParse: defaultDayOfMonthOrdinalParse, relativeTime: defaultRelativeTime, months: defaultLocaleMonths, monthsShort: defaultLocaleMonthsShort, week: defaultLocaleWeek, weekdays: defaultLocaleWeekdays, weekdaysMin: defaultLocaleWeekdaysMin, weekdaysShort: defaultLocaleWeekdaysShort, meridiemParse: defaultLocaleMeridiemParse }; // internal storage for locale config files var locales = {}; var localeFamilies = {}; var globalLocale; function normalizeLocale(key) { return key ? key.toLowerCase().replace('_', '-') : key; } // pick the locale from the array // try ['en-au', 'en-gb'] as 'en-au', 'en-gb', 'en', as in move through the list trying each // substring from most specific to least, but move to the next array item if it's a more specific variant than the current root function chooseLocale(names) { var i = 0, j, next, locale, split; while (i < names.length) { split = normalizeLocale(names[i]).split('-'); j = split.length; next = normalizeLocale(names[i + 1]); next = next ? next.split('-') : null; while (j > 0) { locale = loadLocale(split.slice(0, j).join('-')); if (locale) { return locale; } if (next && next.length >= j && compareArrays(split, next, true) >= j - 1) { //the next array item is better than a shallower substring of this one break; } j--; } i++; } return null; } function loadLocale(name) { var oldLocale = null; // TODO: Find a better way to register and load all the locales in Node if (!locales[name] && (typeof module !== 'undefined') && module && module.exports) { try { oldLocale = globalLocale._abbr; require('./locale/' + name); // because defineLocale currently also sets the global locale, we // want to undo that for lazy loaded locales getSetGlobalLocale(oldLocale); } catch (e) { } } return locales[name]; } // This function will load locale and then set the global locale. If // no arguments are passed in, it will simply return the current global // locale key. function getSetGlobalLocale (key, values) { var data; if (key) { if (isUndefined(values)) { data = getLocale(key); } else { data = defineLocale(key, values); } if (data) { // moment.duration._locale = moment._locale = data; globalLocale = data; } } return globalLocale._abbr; } function defineLocale (name, config) { if (config !== null) { var parentConfig = baseConfig; config.abbr = name; if (locales[name] != null) { deprecateSimple('defineLocaleOverride', 'use moment.updateLocale(localeName, config) to change ' + 'an existing locale. moment.defineLocale(localeName, ' + 'config) should only be used for creating a new locale ' + 'See http://momentjs.com/guides/#/warnings/define-locale/ for more info.'); parentConfig = locales[name]._config; } else if (config.parentLocale != null) { if (locales[config.parentLocale] != null) { parentConfig = locales[config.parentLocale]._config; } else { if (!localeFamilies[config.parentLocale]) { localeFamilies[config.parentLocale] = []; } localeFamilies[config.parentLocale].push({ name: name, config: config }); return null; } } locales[name] = new Locale(mergeConfigs(parentConfig, config)); if (localeFamilies[name]) { localeFamilies[name].forEach(function (x) { defineLocale(x.name, x.config); }); } // backwards compat for now: also set the locale // make sure we set the locale AFTER all child locales have been // created, so we won't end up with the child locale set. getSetGlobalLocale(name); return locales[name]; } else { // useful for testing delete locales[name]; return null; } } function updateLocale(name, config) { if (config != null) { var locale, parentConfig = baseConfig; // MERGE if (locales[name] != null) { parentConfig = locales[name]._config; } config = mergeConfigs(parentConfig, config); locale = new Locale(config); locale.parentLocale = locales[name]; locales[name] = locale; // backwards compat for now: also set the locale getSetGlobalLocale(name); } else { // pass null for config to unupdate, useful for tests if (locales[name] != null) { if (locales[name].parentLocale != null) { locales[name] = locales[name].parentLocale; } else if (locales[name] != null) { delete locales[name]; } } } return locales[name]; } // returns locale data function getLocale (key) { var locale; if (key && key._locale && key._locale._abbr) { key = key._locale._abbr; } if (!key) { return globalLocale; } if (!isArray(key)) { //short-circuit everything else locale = loadLocale(key); if (locale) { return locale; } key = [key]; } return chooseLocale(key); } function listLocales() { return keys$1(locales); } function checkOverflow (m) { var overflow; var a = m._a; if (a && getParsingFlags(m).overflow === -2) { overflow = a[MONTH] < 0 || a[MONTH] > 11 ? MONTH : a[DATE] < 1 || a[DATE] > daysInMonth(a[YEAR], a[MONTH]) ? DATE : a[HOUR] < 0 || a[HOUR] > 24 || (a[HOUR] === 24 && (a[MINUTE] !== 0 || a[SECOND] !== 0 || a[MILLISECOND] !== 0)) ? HOUR : a[MINUTE] < 0 || a[MINUTE] > 59 ? MINUTE : a[SECOND] < 0 || a[SECOND] > 59 ? SECOND : a[MILLISECOND] < 0 || a[MILLISECOND] > 999 ? MILLISECOND : -1; if (getParsingFlags(m)._overflowDayOfYear && (overflow < YEAR || overflow > DATE)) { overflow = DATE; } if (getParsingFlags(m)._overflowWeeks && overflow === -1) { overflow = WEEK; } if (getParsingFlags(m)._overflowWeekday && overflow === -1) { overflow = WEEKDAY; } getParsingFlags(m).overflow = overflow; } return m; } // iso 8601 regex // 0000-00-00 0000-W00 or 0000-W00-0 + T + 00 or 00:00 or 00:00:00 or 00:00:00.000 + +00:00 or +0000 or +00) var extendedIsoRegex = /^\s*((?:[+-]\d{6}|\d{4})-(?:\d\d-\d\d|W\d\d-\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?::\d\d(?::\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/; var basicIsoRegex = /^\s*((?:[+-]\d{6}|\d{4})(?:\d\d\d\d|W\d\d\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?:\d\d(?:\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/; var tzRegex = /Z|[+-]\d\d(?::?\d\d)?/; var isoDates = [ ['YYYYYY-MM-DD', /[+-]\d{6}-\d\d-\d\d/], ['YYYY-MM-DD', /\d{4}-\d\d-\d\d/], ['GGGG-[W]WW-E', /\d{4}-W\d\d-\d/], ['GGGG-[W]WW', /\d{4}-W\d\d/, false], ['YYYY-DDD', /\d{4}-\d{3}/], ['YYYY-MM', /\d{4}-\d\d/, false], ['YYYYYYMMDD', /[+-]\d{10}/], ['YYYYMMDD', /\d{8}/], // YYYYMM is NOT allowed by the standard ['GGGG[W]WWE', /\d{4}W\d{3}/], ['GGGG[W]WW', /\d{4}W\d{2}/, false], ['YYYYDDD', /\d{7}/] ]; // iso time formats and regexes var isoTimes = [ ['HH:mm:ss.SSSS', /\d\d:\d\d:\d\d\.\d+/], ['HH:mm:ss,SSSS', /\d\d:\d\d:\d\d,\d+/], ['HH:mm:ss', /\d\d:\d\d:\d\d/], ['HH:mm', /\d\d:\d\d/], ['HHmmss.SSSS', /\d\d\d\d\d\d\.\d+/], ['HHmmss,SSSS', /\d\d\d\d\d\d,\d+/], ['HHmmss', /\d\d\d\d\d\d/], ['HHmm', /\d\d\d\d/], ['HH', /\d\d/] ]; var aspNetJsonRegex = /^\/?Date\((\-?\d+)/i; // date from iso format function configFromISO(config) { var i, l, string = config._i, match = extendedIsoRegex.exec(string) || basicIsoRegex.exec(string), allowTime, dateFormat, timeFormat, tzFormat; if (match) { getParsingFlags(config).iso = true; for (i = 0, l = isoDates.length; i < l; i++) { if (isoDates[i][1].exec(match[1])) { dateFormat = isoDates[i][0]; allowTime = isoDates[i][2] !== false; break; } } if (dateFormat == null) { config._isValid = false; return; } if (match[3]) { for (i = 0, l = isoTimes.length; i < l; i++) { if (isoTimes[i][1].exec(match[3])) { // match[2] should be 'T' or space timeFormat = (match[2] || ' ') + isoTimes[i][0]; break; } } if (timeFormat == null) { config._isValid = false; return; } } if (!allowTime && timeFormat != null) { config._isValid = false; return; } if (match[4]) { if (tzRegex.exec(match[4])) { tzFormat = 'Z'; } else { config._isValid = false; return; } } config._f = dateFormat + (timeFormat || '') + (tzFormat || ''); configFromStringAndFormat(config); } else { config._isValid = false; } } // RFC 2822 regex: For details see https://tools.ietf.org/html/rfc2822#section-3.3 var basicRfcRegex = /^((?:Mon|Tue|Wed|Thu|Fri|Sat|Sun),?\s)?(\d?\d\s(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s(?:\d\d)?\d\d\s)(\d\d:\d\d)(\:\d\d)?(\s(?:UT|GMT|[ECMP][SD]T|[A-IK-Za-ik-z]|[+-]\d{4}))$/; // date and time from ref 2822 format function configFromRFC2822(config) { var string, match, dayFormat, dateFormat, timeFormat, tzFormat; var timezones = { ' GMT': ' +0000', ' EDT': ' -0400', ' EST': ' -0500', ' CDT': ' -0500', ' CST': ' -0600', ' MDT': ' -0600', ' MST': ' -0700', ' PDT': ' -0700', ' PST': ' -0800' }; var military = 'YXWVUTSRQPONZABCDEFGHIKLM'; var timezone, timezoneIndex; string = config._i .replace(/\([^\)]*\)|[\n\t]/g, ' ') // Remove comments and folding whitespace .replace(/(\s\s+)/g, ' ') // Replace multiple-spaces with a single space .replace(/^\s|\s$/g, ''); // Remove leading and trailing spaces match = basicRfcRegex.exec(string); if (match) { dayFormat = match[1] ? 'ddd' + ((match[1].length === 5) ? ', ' : ' ') : ''; dateFormat = 'D MMM ' + ((match[2].length > 10) ? 'YYYY ' : 'YY '); timeFormat = 'HH:mm' + (match[4] ? ':ss' : ''); // TODO: Replace the vanilla JS Date object with an indepentent day-of-week check. if (match[1]) { // day of week given var momentDate = new Date(match[2]); var momentDay = ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'][momentDate.getDay()]; if (match[1].substr(0,3) !== momentDay) { getParsingFlags(config).weekdayMismatch = true; config._isValid = false; return; } } switch (match[5].length) { case 2: // military if (timezoneIndex === 0) { timezone = ' +0000'; } else { timezoneIndex = military.indexOf(match[5][1].toUpperCase()) - 12; timezone = ((timezoneIndex < 0) ? ' -' : ' +') + (('' + timezoneIndex).replace(/^-?/, '0')).match(/..$/)[0] + '00'; } break; case 4: // Zone timezone = timezones[match[5]]; break; default: // UT or +/-9999 timezone = timezones[' GMT']; } match[5] = timezone; config._i = match.splice(1).join(''); tzFormat = ' ZZ'; config._f = dayFormat + dateFormat + timeFormat + tzFormat; configFromStringAndFormat(config); getParsingFlags(config).rfc2822 = true; } else { config._isValid = false; } } // date from iso format or fallback function configFromString(config) { var matched = aspNetJsonRegex.exec(config._i); if (matched !== null) { config._d = new Date(+matched[1]); return; } configFromISO(config); if (config._isValid === false) { delete config._isValid; } else { return; } configFromRFC2822(config); if (config._isValid === false) { delete config._isValid; } else { return; } // Final attempt, use Input Fallback hooks.createFromInputFallback(config); } hooks.createFromInputFallback = deprecate( 'value provided is not in a recognized RFC2822 or ISO format. moment construction falls back to js Date(), ' + 'which is not reliable across all browsers and versions. Non RFC2822/ISO date formats are ' + 'discouraged and will be removed in an upcoming major release. Please refer to ' + 'http://momentjs.com/guides/#/warnings/js-date/ for more info.', function (config) { config._d = new Date(config._i + (config._useUTC ? ' UTC' : '')); } ); // Pick the first defined of two or three arguments. function defaults(a, b, c) { if (a != null) { return a; } if (b != null) { return b; } return c; } function currentDateArray(config) { // hooks is actually the exported moment object var nowValue = new Date(hooks.now()); if (config._useUTC) { return [nowValue.getUTCFullYear(), nowValue.getUTCMonth(), nowValue.getUTCDate()]; } return [nowValue.getFullYear(), nowValue.getMonth(), nowValue.getDate()]; } // convert an array to a date. // the array should mirror the parameters below // note: all values past the year are optional and will default to the lowest possible value. // [year, month, day , hour, minute, second, millisecond] function configFromArray (config) { var i, date, input = [], currentDate, yearToUse; if (config._d) { return; } currentDate = currentDateArray(config); //compute day of the year from weeks and weekdays if (config._w && config._a[DATE] == null && config._a[MONTH] == null) { dayOfYearFromWeekInfo(config); } //if the day of the year is set, figure out what it is if (config._dayOfYear != null) { yearToUse = defaults(config._a[YEAR], currentDate[YEAR]); if (config._dayOfYear > daysInYear(yearToUse) || config._dayOfYear === 0) { getParsingFlags(config)._overflowDayOfYear = true; } date = createUTCDate(yearToUse, 0, config._dayOfYear); config._a[MONTH] = date.getUTCMonth(); config._a[DATE] = date.getUTCDate(); } // Default to current date. // * if no year, month, day of month are given, default to today // * if day of month is given, default month and year // * if month is given, default only year // * if year is given, don't default anything for (i = 0; i < 3 && config._a[i] == null; ++i) { config._a[i] = input[i] = currentDate[i]; } // Zero out whatever was not defaulted, including time for (; i < 7; i++) { config._a[i] = input[i] = (config._a[i] == null) ? (i === 2 ? 1 : 0) : config._a[i]; } // Check for 24:00:00.000 if (config._a[HOUR] === 24 && config._a[MINUTE] === 0 && config._a[SECOND] === 0 && config._a[MILLISECOND] === 0) { config._nextDay = true; config._a[HOUR] = 0; } config._d = (config._useUTC ? createUTCDate : createDate).apply(null, input); // Apply timezone offset from input. The actual utcOffset can be changed // with parseZone. if (config._tzm != null) { config._d.setUTCMinutes(config._d.getUTCMinutes() - config._tzm); } if (config._nextDay) { config._a[HOUR] = 24; } } function dayOfYearFromWeekInfo(config) { var w, weekYear, week, weekday, dow, doy, temp, weekdayOverflow; w = config._w; if (w.GG != null || w.W != null || w.E != null) { dow = 1; doy = 4; // TODO: We need to take the current isoWeekYear, but that depends on // how we interpret now (local, utc, fixed offset). So create // a now version of current config (take local/utc/offset flags, and // create now). weekYear = defaults(w.GG, config._a[YEAR], weekOfYear(createLocal(), 1, 4).year); week = defaults(w.W, 1); weekday = defaults(w.E, 1); if (weekday < 1 || weekday > 7) { weekdayOverflow = true; } } else { dow = config._locale._week.dow; doy = config._locale._week.doy; var curWeek = weekOfYear(createLocal(), dow, doy); weekYear = defaults(w.gg, config._a[YEAR], curWeek.year); // Default to current week. week = defaults(w.w, curWeek.week); if (w.d != null) { // weekday -- low day numbers are considered next week weekday = w.d; if (weekday < 0 || weekday > 6) { weekdayOverflow = true; } } else if (w.e != null) { // local weekday -- counting starts from begining of week weekday = w.e + dow; if (w.e < 0 || w.e > 6) { weekdayOverflow = true; } } else { // default to begining of week weekday = dow; } } if (week < 1 || week > weeksInYear(weekYear, dow, doy)) { getParsingFlags(config)._overflowWeeks = true; } else if (weekdayOverflow != null) { getParsingFlags(config)._overflowWeekday = true; } else { temp = dayOfYearFromWeeks(weekYear, week, weekday, dow, doy); config._a[YEAR] = temp.year; config._dayOfYear = temp.dayOfYear; } } // constant that refers to the ISO standard hooks.ISO_8601 = function () {}; // constant that refers to the RFC 2822 form hooks.RFC_2822 = function () {}; // date from string and format string function configFromStringAndFormat(config) { // TODO: Move this to another part of the creation flow to prevent circular deps if (config._f === hooks.ISO_8601) { configFromISO(config); return; } if (config._f === hooks.RFC_2822) { configFromRFC2822(config); return; } config._a = []; getParsingFlags(config).empty = true; // This array is used to make a Date, either with `new Date` or `Date.UTC` var string = '' + config._i, i, parsedInput, tokens, token, skipped, stringLength = string.length, totalParsedInputLength = 0; tokens = expandFormat(config._f, config._locale).match(formattingTokens) || []; for (i = 0; i < tokens.length; i++) { token = tokens[i]; parsedInput = (string.match(getParseRegexForToken(token, config)) || [])[0]; // console.log('token', token, 'parsedInput', parsedInput, // 'regex', getParseRegexForToken(token, config)); if (parsedInput) { skipped = string.substr(0, string.indexOf(parsedInput)); if (skipped.length > 0) { getParsingFlags(config).unusedInput.push(skipped); } string = string.slice(string.indexOf(parsedInput) + parsedInput.length); totalParsedInputLength += parsedInput.length; } // don't parse if it's not a known token if (formatTokenFunctions[token]) { if (parsedInput) { getParsingFlags(config).empty = false; } else { getParsingFlags(config).unusedTokens.push(token); } addTimeToArrayFromToken(token, parsedInput, config); } else if (config._strict && !parsedInput) { getParsingFlags(config).unusedTokens.push(token); } } // add remaining unparsed input length to the string getParsingFlags(config).charsLeftOver = stringLength - totalParsedInputLength; if (string.length > 0) { getParsingFlags(config).unusedInput.push(string); } // clear _12h flag if hour is <= 12 if (config._a[HOUR] <= 12 && getParsingFlags(config).bigHour === true && config._a[HOUR] > 0) { getParsingFlags(config).bigHour = undefined; } getParsingFlags(config).parsedDateParts = config._a.slice(0); getParsingFlags(config).meridiem = config._meridiem; // handle meridiem config._a[HOUR] = meridiemFixWrap(config._locale, config._a[HOUR], config._meridiem); configFromArray(config); checkOverflow(config); } function meridiemFixWrap (locale, hour, meridiem) { var isPm; if (meridiem == null) { // nothing to do return hour; } if (locale.meridiemHour != null) { return locale.meridiemHour(hour, meridiem); } else if (locale.isPM != null) { // Fallback isPm = locale.isPM(meridiem); if (isPm && hour < 12) { hour += 12; } if (!isPm && hour === 12) { hour = 0; } return hour; } else { // this is not supposed to happen return hour; } } // date from string and array of format strings function configFromStringAndArray(config) { var tempConfig, bestMoment, scoreToBeat, i, currentScore; if (config._f.length === 0) { getParsingFlags(config).invalidFormat = true; config._d = new Date(NaN); return; } for (i = 0; i < config._f.length; i++) { currentScore = 0; tempConfig = copyConfig({}, config); if (config._useUTC != null) { tempConfig._useUTC = config._useUTC; } tempConfig._f = config._f[i]; configFromStringAndFormat(tempConfig); if (!isValid(tempConfig)) { continue; } // if there is any input that was not parsed add a penalty for that format currentScore += getParsingFlags(tempConfig).charsLeftOver; //or tokens currentScore += getParsingFlags(tempConfig).unusedTokens.length * 10; getParsingFlags(tempConfig).score = currentScore; if (scoreToBeat == null || currentScore < scoreToBeat) { scoreToBeat = currentScore; bestMoment = tempConfig; } } extend(config, bestMoment || tempConfig); } function configFromObject(config) { if (config._d) { return; } var i = normalizeObjectUnits(config._i); config._a = map([i.year, i.month, i.day || i.date, i.hour, i.minute, i.second, i.millisecond], function (obj) { return obj && parseInt(obj, 10); }); configFromArray(config); } function createFromConfig (config) { var res = new Moment(checkOverflow(prepareConfig(config))); if (res._nextDay) { // Adding is smart enough around DST res.add(1, 'd'); res._nextDay = undefined; } return res; } function prepareConfig (config) { var input = config._i, format = config._f; config._locale = config._locale || getLocale(config._l); if (input === null || (format === undefined && input === '')) { return createInvalid({nullInput: true}); } if (typeof input === 'string') { config._i = input = config._locale.preparse(input); } if (isMoment(input)) { return new Moment(checkOverflow(input)); } else if (isDate(input)) { config._d = input; } else if (isArray(format)) { configFromStringAndArray(config); } else if (format) { configFromStringAndFormat(config); } else { configFromInput(config); } if (!isValid(config)) { config._d = null; } return config; } function configFromInput(config) { var input = config._i; if (isUndefined(input)) { config._d = new Date(hooks.now()); } else if (isDate(input)) { config._d = new Date(input.valueOf()); } else if (typeof input === 'string') { configFromString(config); } else if (isArray(input)) { config._a = map(input.slice(0), function (obj) { return parseInt(obj, 10); }); configFromArray(config); } else if (isObject(input)) { configFromObject(config); } else if (isNumber(input)) { // from milliseconds config._d = new Date(input); } else { hooks.createFromInputFallback(config); } } function createLocalOrUTC (input, format, locale, strict, isUTC) { var c = {}; if (locale === true || locale === false) { strict = locale; locale = undefined; } if ((isObject(input) && isObjectEmpty(input)) || (isArray(input) && input.length === 0)) { input = undefined; } // object construction must be done this way. // https://github.com/moment/moment/issues/1423 c._isAMomentObject = true; c._useUTC = c._isUTC = isUTC; c._l = locale; c._i = input; c._f = format; c._strict = strict; return createFromConfig(c); } function createLocal (input, format, locale, strict) { return createLocalOrUTC(input, format, locale, strict, false); } var prototypeMin = deprecate( 'moment().min is deprecated, use moment.max instead. http://momentjs.com/guides/#/warnings/min-max/', function () { var other = createLocal.apply(null, arguments); if (this.isValid() && other.isValid()) { return other < this ? this : other; } else { return createInvalid(); } } ); var prototypeMax = deprecate( 'moment().max is deprecated, use moment.min instead. http://momentjs.com/guides/#/warnings/min-max/', function () { var other = createLocal.apply(null, arguments); if (this.isValid() && other.isValid()) { return other > this ? this : other; } else { return createInvalid(); } } ); // Pick a moment m from moments so that m[fn](other) is true for all // other. This relies on the function fn to be transitive. // // moments should either be an array of moment objects or an array, whose // first element is an array of moment objects. function pickBy(fn, moments) { var res, i; if (moments.length === 1 && isArray(moments[0])) { moments = moments[0]; } if (!moments.length) { return createLocal(); } res = moments[0]; for (i = 1; i < moments.length; ++i) { if (!moments[i].isValid() || moments[i][fn](res)) { res = moments[i]; } } return res; } // TODO: Use [].sort instead? function min () { var args = [].slice.call(arguments, 0); return pickBy('isBefore', args); } function max () { var args = [].slice.call(arguments, 0); return pickBy('isAfter', args); } var now = function () { return Date.now ? Date.now() : +(new Date()); }; var ordering = ['year', 'quarter', 'month', 'week', 'day', 'hour', 'minute', 'second', 'millisecond']; function isDurationValid(m) { for (var key in m) { if (!(ordering.indexOf(key) !== -1 && (m[key] == null || !isNaN(m[key])))) { return false; } } var unitHasDecimal = false; for (var i = 0; i < ordering.length; ++i) { if (m[ordering[i]]) { if (unitHasDecimal) { return false; // only allow non-integers for smallest unit } if (parseFloat(m[ordering[i]]) !== toInt(m[ordering[i]])) { unitHasDecimal = true; } } } return true; } function isValid$1() { return this._isValid; } function createInvalid$1() { return createDuration(NaN); } function Duration (duration) { var normalizedInput = normalizeObjectUnits(duration), years = normalizedInput.year || 0, quarters = normalizedInput.quarter || 0, months = normalizedInput.month || 0, weeks = normalizedInput.week || 0, days = normalizedInput.day || 0, hours = normalizedInput.hour || 0, minutes = normalizedInput.minute || 0, seconds = normalizedInput.second || 0, milliseconds = normalizedInput.millisecond || 0; this._isValid = isDurationValid(normalizedInput); // representation for dateAddRemove this._milliseconds = +milliseconds + seconds * 1e3 + // 1000 minutes * 6e4 + // 1000 * 60 hours * 1000 * 60 * 60; //using 1000 * 60 * 60 instead of 36e5 to avoid floating point rounding errors https://github.com/moment/moment/issues/2978 // Because of dateAddRemove treats 24 hours as different from a // day when working around DST, we need to store them separately this._days = +days + weeks * 7; // It is impossible translate months into days without knowing // which months you are are talking about, so we have to store // it separately. this._months = +months + quarters * 3 + years * 12; this._data = {}; this._locale = getLocale(); this._bubble(); } function isDuration (obj) { return obj instanceof Duration; } function absRound (number) { if (number < 0) { return Math.round(-1 * number) * -1; } else { return Math.round(number); } } // FORMATTING function offset (token, separator) { addFormatToken(token, 0, 0, function () { var offset = this.utcOffset(); var sign = '+'; if (offset < 0) { offset = -offset; sign = '-'; } return sign + zeroFill(~~(offset / 60), 2) + separator + zeroFill(~~(offset) % 60, 2); }); } offset('Z', ':'); offset('ZZ', ''); // PARSING addRegexToken('Z', matchShortOffset); addRegexToken('ZZ', matchShortOffset); addParseToken(['Z', 'ZZ'], function (input, array, config) { config._useUTC = true; config._tzm = offsetFromString(matchShortOffset, input); }); // HELPERS // timezone chunker // '+10:00' > ['10', '00'] // '-1530' > ['-15', '30'] var chunkOffset = /([\+\-]|\d\d)/gi; function offsetFromString(matcher, string) { var matches = (string || '').match(matcher); if (matches === null) { return null; } var chunk = matches[matches.length - 1] || []; var parts = (chunk + '').match(chunkOffset) || ['-', 0, 0]; var minutes = +(parts[1] * 60) + toInt(parts[2]); return minutes === 0 ? 0 : parts[0] === '+' ? minutes : -minutes; } // Return a moment from input, that is local/utc/zone equivalent to model. function cloneWithOffset(input, model) { var res, diff; if (model._isUTC) { res = model.clone(); diff = (isMoment(input) || isDate(input) ? input.valueOf() : createLocal(input).valueOf()) - res.valueOf(); // Use low-level api, because this fn is low-level api. res._d.setTime(res._d.valueOf() + diff); hooks.updateOffset(res, false); return res; } else { return createLocal(input).local(); } } function getDateOffset (m) { // On Firefox.24 Date#getTimezoneOffset returns a floating point. // https://github.com/moment/moment/pull/1871 return -Math.round(m._d.getTimezoneOffset() / 15) * 15; } // HOOKS // This function will be called whenever a moment is mutated. // It is intended to keep the offset in sync with the timezone. hooks.updateOffset = function () {}; // MOMENTS // keepLocalTime = true means only change the timezone, without // affecting the local hour. So 5:31:26 +0300 --[utcOffset(2, true)]--> // 5:31:26 +0200 It is possible that 5:31:26 doesn't exist with offset // +0200, so we adjust the time as needed, to be valid. // // Keeping the time actually adds/subtracts (one hour) // from the actual represented time. That is why we call updateOffset // a second time. In case it wants us to change the offset again // _changeInProgress == true case, then we have to adjust, because // there is no such time in the given timezone. function getSetOffset (input, keepLocalTime, keepMinutes) { var offset = this._offset || 0, localAdjust; if (!this.isValid()) { return input != null ? this : NaN; } if (input != null) { if (typeof input === 'string') { input = offsetFromString(matchShortOffset, input); if (input === null) { return this; } } else if (Math.abs(input) < 16 && !keepMinutes) { input = input * 60; } if (!this._isUTC && keepLocalTime) { localAdjust = getDateOffset(this); } this._offset = input; this._isUTC = true; if (localAdjust != null) { this.add(localAdjust, 'm'); } if (offset !== input) { if (!keepLocalTime || this._changeInProgress) { addSubtract(this, createDuration(input - offset, 'm'), 1, false); } else if (!this._changeInProgress) { this._changeInProgress = true; hooks.updateOffset(this, true); this._changeInProgress = null; } } return this; } else { return this._isUTC ? offset : getDateOffset(this); } } function getSetZone (input, keepLocalTime) { if (input != null) { if (typeof input !== 'string') { input = -input; } this.utcOffset(input, keepLocalTime); return this; } else { return -this.utcOffset(); } } function setOffsetToUTC (keepLocalTime) { return this.utcOffset(0, keepLocalTime); } function setOffsetToLocal (keepLocalTime) { if (this._isUTC) { this.utcOffset(0, keepLocalTime); this._isUTC = false; if (keepLocalTime) { this.subtract(getDateOffset(this), 'm'); } } return this; } function setOffsetToParsedOffset () { if (this._tzm != null) { this.utcOffset(this._tzm, false, true); } else if (typeof this._i === 'string') { var tZone = offsetFromString(matchOffset, this._i); if (tZone != null) { this.utcOffset(tZone); } else { this.utcOffset(0, true); } } return this; } function hasAlignedHourOffset (input) { if (!this.isValid()) { return false; } input = input ? createLocal(input).utcOffset() : 0; return (this.utcOffset() - input) % 60 === 0; } function isDaylightSavingTime () { return ( this.utcOffset() > this.clone().month(0).utcOffset() || this.utcOffset() > this.clone().month(5).utcOffset() ); } function isDaylightSavingTimeShifted () { if (!isUndefined(this._isDSTShifted)) { return this._isDSTShifted; } var c = {}; copyConfig(c, this); c = prepareConfig(c); if (c._a) { var other = c._isUTC ? createUTC(c._a) : createLocal(c._a); this._isDSTShifted = this.isValid() && compareArrays(c._a, other.toArray()) > 0; } else { this._isDSTShifted = false; } return this._isDSTShifted; } function isLocal () { return this.isValid() ? !this._isUTC : false; } function isUtcOffset () { return this.isValid() ? this._isUTC : false; } function isUtc () { return this.isValid() ? this._isUTC && this._offset === 0 : false; } // ASP.NET json date format regex var aspNetRegex = /^(\-)?(?:(\d*)[. ])?(\d+)\:(\d+)(?:\:(\d+)(\.\d*)?)?$/; // from http://docs.closure-library.googlecode.com/git/closure_goog_date_date.js.source.html // somewhat more in line with 4.4.3.2 2004 spec, but allows decimal anywhere // and further modified to allow for strings containing both week and day var isoRegex = /^(-)?P(?:(-?[0-9,.]*)Y)?(?:(-?[0-9,.]*)M)?(?:(-?[0-9,.]*)W)?(?:(-?[0-9,.]*)D)?(?:T(?:(-?[0-9,.]*)H)?(?:(-?[0-9,.]*)M)?(?:(-?[0-9,.]*)S)?)?$/; function createDuration (input, key) { var duration = input, // matching against regexp is expensive, do it on demand match = null, sign, ret, diffRes; if (isDuration(input)) { duration = { ms : input._milliseconds, d : input._days, M : input._months }; } else if (isNumber(input)) { duration = {}; if (key) { duration[key] = input; } else { duration.milliseconds = input; } } else if (!!(match = aspNetRegex.exec(input))) { sign = (match[1] === '-') ? -1 : 1; duration = { y : 0, d : toInt(match[DATE]) * sign, h : toInt(match[HOUR]) * sign, m : toInt(match[MINUTE]) * sign, s : toInt(match[SECOND]) * sign, ms : toInt(absRound(match[MILLISECOND] * 1000)) * sign // the millisecond decimal point is included in the match }; } else if (!!(match = isoRegex.exec(input))) { sign = (match[1] === '-') ? -1 : 1; duration = { y : parseIso(match[2], sign), M : parseIso(match[3], sign), w : parseIso(match[4], sign), d : parseIso(match[5], sign), h : parseIso(match[6], sign), m : parseIso(match[7], sign), s : parseIso(match[8], sign) }; } else if (duration == null) {// checks for null or undefined duration = {}; } else if (typeof duration === 'object' && ('from' in duration || 'to' in duration)) { diffRes = momentsDifference(createLocal(duration.from), createLocal(duration.to)); duration = {}; duration.ms = diffRes.milliseconds; duration.M = diffRes.months; } ret = new Duration(duration); if (isDuration(input) && hasOwnProp(input, '_locale')) { ret._locale = input._locale; } return ret; } createDuration.fn = Duration.prototype; createDuration.invalid = createInvalid$1; function parseIso (inp, sign) { // We'd normally use ~~inp for this, but unfortunately it also // converts floats to ints. // inp may be undefined, so careful calling replace on it. var res = inp && parseFloat(inp.replace(',', '.')); // apply sign while we're at it return (isNaN(res) ? 0 : res) * sign; } function positiveMomentsDifference(base, other) { var res = {milliseconds: 0, months: 0}; res.months = other.month() - base.month() + (other.year() - base.year()) * 12; if (base.clone().add(res.months, 'M').isAfter(other)) { --res.months; } res.milliseconds = +other - +(base.clone().add(res.months, 'M')); return res; } function momentsDifference(base, other) { var res; if (!(base.isValid() && other.isValid())) { return {milliseconds: 0, months: 0}; } other = cloneWithOffset(other, base); if (base.isBefore(other)) { res = positiveMomentsDifference(base, other); } else { res = positiveMomentsDifference(other, base); res.milliseconds = -res.milliseconds; res.months = -res.months; } return res; } // TODO: remove 'name' arg after deprecation is removed function createAdder(direction, name) { return function (val, period) { var dur, tmp; //invert the arguments, but complain about it if (period !== null && !isNaN(+period)) { deprecateSimple(name, 'moment().' + name + '(period, number) is deprecated. Please use moment().' + name + '(number, period). ' + 'See http://momentjs.com/guides/#/warnings/add-inverted-param/ for more info.'); tmp = val; val = period; period = tmp; } val = typeof val === 'string' ? +val : val; dur = createDuration(val, period); addSubtract(this, dur, direction); return this; }; } function addSubtract (mom, duration, isAdding, updateOffset) { var milliseconds = duration._milliseconds, days = absRound(duration._days), months = absRound(duration._months); if (!mom.isValid()) { // No op return; } updateOffset = updateOffset == null ? true : updateOffset; if (milliseconds) { mom._d.setTime(mom._d.valueOf() + milliseconds * isAdding); } if (days) { set$1(mom, 'Date', get(mom, 'Date') + days * isAdding); } if (months) { setMonth(mom, get(mom, 'Month') + months * isAdding); } if (updateOffset) { hooks.updateOffset(mom, days || months); } } var add = createAdder(1, 'add'); var subtract = createAdder(-1, 'subtract'); function getCalendarFormat(myMoment, now) { var diff = myMoment.diff(now, 'days', true); return diff < -6 ? 'sameElse' : diff < -1 ? 'lastWeek' : diff < 0 ? 'lastDay' : diff < 1 ? 'sameDay' : diff < 2 ? 'nextDay' : diff < 7 ? 'nextWeek' : 'sameElse'; } function calendar$1 (time, formats) { // We want to compare the start of today, vs this. // Getting start-of-today depends on whether we're local/utc/offset or not. var now = time || createLocal(), sod = cloneWithOffset(now, this).startOf('day'), format = hooks.calendarFormat(this, sod) || 'sameElse'; var output = formats && (isFunction(formats[format]) ? formats[format].call(this, now) : formats[format]); return this.format(output || this.localeData().calendar(format, this, createLocal(now))); } function clone () { return new Moment(this); } function isAfter (input, units) { var localInput = isMoment(input) ? input : createLocal(input); if (!(this.isValid() && localInput.isValid())) { return false; } units = normalizeUnits(!isUndefined(units) ? units : 'millisecond'); if (units === 'millisecond') { return this.valueOf() > localInput.valueOf(); } else { return localInput.valueOf() < this.clone().startOf(units).valueOf(); } } function isBefore (input, units) { var localInput = isMoment(input) ? input : createLocal(input); if (!(this.isValid() && localInput.isValid())) { return false; } units = normalizeUnits(!isUndefined(units) ? units : 'millisecond'); if (units === 'millisecond') { return this.valueOf() < localInput.valueOf(); } else { return this.clone().endOf(units).valueOf() < localInput.valueOf(); } } function isBetween (from, to, units, inclusivity) { inclusivity = inclusivity || '()'; return (inclusivity[0] === '(' ? this.isAfter(from, units) : !this.isBefore(from, units)) && (inclusivity[1] === ')' ? this.isBefore(to, units) : !this.isAfter(to, units)); } function isSame (input, units) { var localInput = isMoment(input) ? input : createLocal(input), inputMs; if (!(this.isValid() && localInput.isValid())) { return false; } units = normalizeUnits(units || 'millisecond'); if (units === 'millisecond') { return this.valueOf() === localInput.valueOf(); } else { inputMs = localInput.valueOf(); return this.clone().startOf(units).valueOf() <= inputMs && inputMs <= this.clone().endOf(units).valueOf(); } } function isSameOrAfter (input, units) { return this.isSame(input, units) || this.isAfter(input,units); } function isSameOrBefore (input, units) { return this.isSame(input, units) || this.isBefore(input,units); } function diff (input, units, asFloat) { var that, zoneDelta, delta, output; if (!this.isValid()) { return NaN; } that = cloneWithOffset(input, this); if (!that.isValid()) { return NaN; } zoneDelta = (that.utcOffset() - this.utcOffset()) * 6e4; units = normalizeUnits(units); if (units === 'year' || units === 'month' || units === 'quarter') { output = monthDiff(this, that); if (units === 'quarter') { output = output / 3; } else if (units === 'year') { output = output / 12; } } else { delta = this - that; output = units === 'second' ? delta / 1e3 : // 1000 units === 'minute' ? delta / 6e4 : // 1000 * 60 units === 'hour' ? delta / 36e5 : // 1000 * 60 * 60 units === 'day' ? (delta - zoneDelta) / 864e5 : // 1000 * 60 * 60 * 24, negate dst units === 'week' ? (delta - zoneDelta) / 6048e5 : // 1000 * 60 * 60 * 24 * 7, negate dst delta; } return asFloat ? output : absFloor(output); } function monthDiff (a, b) { // difference in months var wholeMonthDiff = ((b.year() - a.year()) * 12) + (b.month() - a.month()), // b is in (anchor - 1 month, anchor + 1 month) anchor = a.clone().add(wholeMonthDiff, 'months'), anchor2, adjust; if (b - anchor < 0) { anchor2 = a.clone().add(wholeMonthDiff - 1, 'months'); // linear across the month adjust = (b - anchor) / (anchor - anchor2); } else { anchor2 = a.clone().add(wholeMonthDiff + 1, 'months'); // linear across the month adjust = (b - anchor) / (anchor2 - anchor); } //check for negative zero, return zero if negative zero return -(wholeMonthDiff + adjust) || 0; } hooks.defaultFormat = 'YYYY-MM-DDTHH:mm:ssZ'; hooks.defaultFormatUtc = 'YYYY-MM-DDTHH:mm:ss[Z]'; function toString () { return this.clone().locale('en').format('ddd MMM DD YYYY HH:mm:ss [GMT]ZZ'); } function toISOString() { if (!this.isValid()) { return null; } var m = this.clone().utc(); if (m.year() < 0 || m.year() > 9999) { return formatMoment(m, 'YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]'); } if (isFunction(Date.prototype.toISOString)) { // native implementation is ~50x faster, use it when we can return this.toDate().toISOString(); } return formatMoment(m, 'YYYY-MM-DD[T]HH:mm:ss.SSS[Z]'); } /** * Return a human readable representation of a moment that can * also be evaluated to get a new moment which is the same * * @link https://nodejs.org/dist/latest/docs/api/util.html#util_custom_inspect_function_on_objects */ function inspect () { if (!this.isValid()) { return 'moment.invalid(/* ' + this._i + ' */)'; } var func = 'moment'; var zone = ''; if (!this.isLocal()) { func = this.utcOffset() === 0 ? 'moment.utc' : 'moment.parseZone'; zone = 'Z'; } var prefix = '[' + func + '("]'; var year = (0 <= this.year() && this.year() <= 9999) ? 'YYYY' : 'YYYYYY'; var datetime = '-MM-DD[T]HH:mm:ss.SSS'; var suffix = zone + '[")]'; return this.format(prefix + year + datetime + suffix); } function format (inputString) { if (!inputString) { inputString = this.isUtc() ? hooks.defaultFormatUtc : hooks.defaultFormat; } var output = formatMoment(this, inputString); return this.localeData().postformat(output); } function from (time, withoutSuffix) { if (this.isValid() && ((isMoment(time) && time.isValid()) || createLocal(time).isValid())) { return createDuration({to: this, from: time}).locale(this.locale()).humanize(!withoutSuffix); } else { return this.localeData().invalidDate(); } } function fromNow (withoutSuffix) { return this.from(createLocal(), withoutSuffix); } function to (time, withoutSuffix) { if (this.isValid() && ((isMoment(time) && time.isValid()) || createLocal(time).isValid())) { return createDuration({from: this, to: time}).locale(this.locale()).humanize(!withoutSuffix); } else { return this.localeData().invalidDate(); } } function toNow (withoutSuffix) { return this.to(createLocal(), withoutSuffix); } // If passed a locale key, it will set the locale for this // instance. Otherwise, it will return the locale configuration // variables for this instance. function locale (key) { var newLocaleData; if (key === undefined) { return this._locale._abbr; } else { newLocaleData = getLocale(key); if (newLocaleData != null) { this._locale = newLocaleData; } return this; } } var lang = deprecate( 'moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.', function (key) { if (key === undefined) { return this.localeData(); } else { return this.locale(key); } } ); function localeData () { return this._locale; } function startOf (units) { units = normalizeUnits(units); // the following switch intentionally omits break keywords // to utilize falling through the cases. switch (units) { case 'year': this.month(0); /* falls through */ case 'quarter': case 'month': this.date(1); /* falls through */ case 'week': case 'isoWeek': case 'day': case 'date': this.hours(0); /* falls through */ case 'hour': this.minutes(0); /* falls through */ case 'minute': this.seconds(0); /* falls through */ case 'second': this.milliseconds(0); } // weeks are a special case if (units === 'week') { this.weekday(0); } if (units === 'isoWeek') { this.isoWeekday(1); } // quarters are also special if (units === 'quarter') { this.month(Math.floor(this.month() / 3) * 3); } return this; } function endOf (units) { units = normalizeUnits(units); if (units === undefined || units === 'millisecond') { return this; } // 'date' is an alias for 'day', so it should be considered as such. if (units === 'date') { units = 'day'; } return this.startOf(units).add(1, (units === 'isoWeek' ? 'week' : units)).subtract(1, 'ms'); } function valueOf () { return this._d.valueOf() - ((this._offset || 0) * 60000); } function unix () { return Math.floor(this.valueOf() / 1000); } function toDate () { return new Date(this.valueOf()); } function toArray () { var m = this; return [m.year(), m.month(), m.date(), m.hour(), m.minute(), m.second(), m.millisecond()]; } function toObject () { var m = this; return { years: m.year(), months: m.month(), date: m.date(), hours: m.hours(), minutes: m.minutes(), seconds: m.seconds(), milliseconds: m.milliseconds() }; } function toJSON () { // new Date(NaN).toJSON() === null return this.isValid() ? this.toISOString() : null; } function isValid$2 () { return isValid(this); } function parsingFlags () { return extend({}, getParsingFlags(this)); } function invalidAt () { return getParsingFlags(this).overflow; } function creationData() { return { input: this._i, format: this._f, locale: this._locale, isUTC: this._isUTC, strict: this._strict }; } // FORMATTING addFormatToken(0, ['gg', 2], 0, function () { return this.weekYear() % 100; }); addFormatToken(0, ['GG', 2], 0, function () { return this.isoWeekYear() % 100; }); function addWeekYearFormatToken (token, getter) { addFormatToken(0, [token, token.length], 0, getter); } addWeekYearFormatToken('gggg', 'weekYear'); addWeekYearFormatToken('ggggg', 'weekYear'); addWeekYearFormatToken('GGGG', 'isoWeekYear'); addWeekYearFormatToken('GGGGG', 'isoWeekYear'); // ALIASES addUnitAlias('weekYear', 'gg'); addUnitAlias('isoWeekYear', 'GG'); // PRIORITY addUnitPriority('weekYear', 1); addUnitPriority('isoWeekYear', 1); // PARSING addRegexToken('G', matchSigned); addRegexToken('g', matchSigned); addRegexToken('GG', match1to2, match2); addRegexToken('gg', match1to2, match2); addRegexToken('GGGG', match1to4, match4); addRegexToken('gggg', match1to4, match4); addRegexToken('GGGGG', match1to6, match6); addRegexToken('ggggg', match1to6, match6); addWeekParseToken(['gggg', 'ggggg', 'GGGG', 'GGGGG'], function (input, week, config, token) { week[token.substr(0, 2)] = toInt(input); }); addWeekParseToken(['gg', 'GG'], function (input, week, config, token) { week[token] = hooks.parseTwoDigitYear(input); }); // MOMENTS function getSetWeekYear (input) { return getSetWeekYearHelper.call(this, input, this.week(), this.weekday(), this.localeData()._week.dow, this.localeData()._week.doy); } function getSetISOWeekYear (input) { return getSetWeekYearHelper.call(this, input, this.isoWeek(), this.isoWeekday(), 1, 4); } function getISOWeeksInYear () { return weeksInYear(this.year(), 1, 4); } function getWeeksInYear () { var weekInfo = this.localeData()._week; return weeksInYear(this.year(), weekInfo.dow, weekInfo.doy); } function getSetWeekYearHelper(input, week, weekday, dow, doy) { var weeksTarget; if (input == null) { return weekOfYear(this, dow, doy).year; } else { weeksTarget = weeksInYear(input, dow, doy); if (week > weeksTarget) { week = weeksTarget; } return setWeekAll.call(this, input, week, weekday, dow, doy); } } function setWeekAll(weekYear, week, weekday, dow, doy) { var dayOfYearData = dayOfYearFromWeeks(weekYear, week, weekday, dow, doy), date = createUTCDate(dayOfYearData.year, 0, dayOfYearData.dayOfYear); this.year(date.getUTCFullYear()); this.month(date.getUTCMonth()); this.date(date.getUTCDate()); return this; } // FORMATTING addFormatToken('Q', 0, 'Qo', 'quarter'); // ALIASES addUnitAlias('quarter', 'Q'); // PRIORITY addUnitPriority('quarter', 7); // PARSING addRegexToken('Q', match1); addParseToken('Q', function (input, array) { array[MONTH] = (toInt(input) - 1) * 3; }); // MOMENTS function getSetQuarter (input) { return input == null ? Math.ceil((this.month() + 1) / 3) : this.month((input - 1) * 3 + this.month() % 3); } // FORMATTING addFormatToken('D', ['DD', 2], 'Do', 'date'); // ALIASES addUnitAlias('date', 'D'); // PRIOROITY addUnitPriority('date', 9); // PARSING addRegexToken('D', match1to2); addRegexToken('DD', match1to2, match2); addRegexToken('Do', function (isStrict, locale) { // TODO: Remove "ordinalParse" fallback in next major release. return isStrict ? (locale._dayOfMonthOrdinalParse || locale._ordinalParse) : locale._dayOfMonthOrdinalParseLenient; }); addParseToken(['D', 'DD'], DATE); addParseToken('Do', function (input, array) { array[DATE] = toInt(input.match(match1to2)[0], 10); }); // MOMENTS var getSetDayOfMonth = makeGetSet('Date', true); // FORMATTING addFormatToken('DDD', ['DDDD', 3], 'DDDo', 'dayOfYear'); // ALIASES addUnitAlias('dayOfYear', 'DDD'); // PRIORITY addUnitPriority('dayOfYear', 4); // PARSING addRegexToken('DDD', match1to3); addRegexToken('DDDD', match3); addParseToken(['DDD', 'DDDD'], function (input, array, config) { config._dayOfYear = toInt(input); }); // HELPERS // MOMENTS function getSetDayOfYear (input) { var dayOfYear = Math.round((this.clone().startOf('day') - this.clone().startOf('year')) / 864e5) + 1; return input == null ? dayOfYear : this.add((input - dayOfYear), 'd'); } // FORMATTING addFormatToken('m', ['mm', 2], 0, 'minute'); // ALIASES addUnitAlias('minute', 'm'); // PRIORITY addUnitPriority('minute', 14); // PARSING addRegexToken('m', match1to2); addRegexToken('mm', match1to2, match2); addParseToken(['m', 'mm'], MINUTE); // MOMENTS var getSetMinute = makeGetSet('Minutes', false); // FORMATTING addFormatToken('s', ['ss', 2], 0, 'second'); // ALIASES addUnitAlias('second', 's'); // PRIORITY addUnitPriority('second', 15); // PARSING addRegexToken('s', match1to2); addRegexToken('ss', match1to2, match2); addParseToken(['s', 'ss'], SECOND); // MOMENTS var getSetSecond = makeGetSet('Seconds', false); // FORMATTING addFormatToken('S', 0, 0, function () { return ~~(this.millisecond() / 100); }); addFormatToken(0, ['SS', 2], 0, function () { return ~~(this.millisecond() / 10); }); addFormatToken(0, ['SSS', 3], 0, 'millisecond'); addFormatToken(0, ['SSSS', 4], 0, function () { return this.millisecond() * 10; }); addFormatToken(0, ['SSSSS', 5], 0, function () { return this.millisecond() * 100; }); addFormatToken(0, ['SSSSSS', 6], 0, function () { return this.millisecond() * 1000; }); addFormatToken(0, ['SSSSSSS', 7], 0, function () { return this.millisecond() * 10000; }); addFormatToken(0, ['SSSSSSSS', 8], 0, function () { return this.millisecond() * 100000; }); addFormatToken(0, ['SSSSSSSSS', 9], 0, function () { return this.millisecond() * 1000000; }); // ALIASES addUnitAlias('millisecond', 'ms'); // PRIORITY addUnitPriority('millisecond', 16); // PARSING addRegexToken('S', match1to3, match1); addRegexToken('SS', match1to3, match2); addRegexToken('SSS', match1to3, match3); var token; for (token = 'SSSS'; token.length <= 9; token += 'S') { addRegexToken(token, matchUnsigned); } function parseMs(input, array) { array[MILLISECOND] = toInt(('0.' + input) * 1000); } for (token = 'S'; token.length <= 9; token += 'S') { addParseToken(token, parseMs); } // MOMENTS var getSetMillisecond = makeGetSet('Milliseconds', false); // FORMATTING addFormatToken('z', 0, 0, 'zoneAbbr'); addFormatToken('zz', 0, 0, 'zoneName'); // MOMENTS function getZoneAbbr () { return this._isUTC ? 'UTC' : ''; } function getZoneName () { return this._isUTC ? 'Coordinated Universal Time' : ''; } var proto = Moment.prototype; proto.add = add; proto.calendar = calendar$1; proto.clone = clone; proto.diff = diff; proto.endOf = endOf; proto.format = format; proto.from = from; proto.fromNow = fromNow; proto.to = to; proto.toNow = toNow; proto.get = stringGet; proto.invalidAt = invalidAt; proto.isAfter = isAfter; proto.isBefore = isBefore; proto.isBetween = isBetween; proto.isSame = isSame; proto.isSameOrAfter = isSameOrAfter; proto.isSameOrBefore = isSameOrBefore; proto.isValid = isValid$2; proto.lang = lang; proto.locale = locale; proto.localeData = localeData; proto.max = prototypeMax; proto.min = prototypeMin; proto.parsingFlags = parsingFlags; proto.set = stringSet; proto.startOf = startOf; proto.subtract = subtract; proto.toArray = toArray; proto.toObject = toObject; proto.toDate = toDate; proto.toISOString = toISOString; proto.inspect = inspect; proto.toJSON = toJSON; proto.toString = toString; proto.unix = unix; proto.valueOf = valueOf; proto.creationData = creationData; // Year proto.year = getSetYear; proto.isLeapYear = getIsLeapYear; // Week Year proto.weekYear = getSetWeekYear; proto.isoWeekYear = getSetISOWeekYear; // Quarter proto.quarter = proto.quarters = getSetQuarter; // Month proto.month = getSetMonth; proto.daysInMonth = getDaysInMonth; // Week proto.week = proto.weeks = getSetWeek; proto.isoWeek = proto.isoWeeks = getSetISOWeek; proto.weeksInYear = getWeeksInYear; proto.isoWeeksInYear = getISOWeeksInYear; // Day proto.date = getSetDayOfMonth; proto.day = proto.days = getSetDayOfWeek; proto.weekday = getSetLocaleDayOfWeek; proto.isoWeekday = getSetISODayOfWeek; proto.dayOfYear = getSetDayOfYear; // Hour proto.hour = proto.hours = getSetHour; // Minute proto.minute = proto.minutes = getSetMinute; // Second proto.second = proto.seconds = getSetSecond; // Millisecond proto.millisecond = proto.milliseconds = getSetMillisecond; // Offset proto.utcOffset = getSetOffset; proto.utc = setOffsetToUTC; proto.local = setOffsetToLocal; proto.parseZone = setOffsetToParsedOffset; proto.hasAlignedHourOffset = hasAlignedHourOffset; proto.isDST = isDaylightSavingTime; proto.isLocal = isLocal; proto.isUtcOffset = isUtcOffset; proto.isUtc = isUtc; proto.isUTC = isUtc; // Timezone proto.zoneAbbr = getZoneAbbr; proto.zoneName = getZoneName; // Deprecations proto.dates = deprecate('dates accessor is deprecated. Use date instead.', getSetDayOfMonth); proto.months = deprecate('months accessor is deprecated. Use month instead', getSetMonth); proto.years = deprecate('years accessor is deprecated. Use year instead', getSetYear); proto.zone = deprecate('moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/', getSetZone); proto.isDSTShifted = deprecate('isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information', isDaylightSavingTimeShifted); function createUnix (input) { return createLocal(input * 1000); } function createInZone () { return createLocal.apply(null, arguments).parseZone(); } function preParsePostFormat (string) { return string; } var proto$1 = Locale.prototype; proto$1.calendar = calendar; proto$1.longDateFormat = longDateFormat; proto$1.invalidDate = invalidDate; proto$1.ordinal = ordinal; proto$1.preparse = preParsePostFormat; proto$1.postformat = preParsePostFormat; proto$1.relativeTime = relativeTime; proto$1.pastFuture = pastFuture; proto$1.set = set; // Month proto$1.months = localeMonths; proto$1.monthsShort = localeMonthsShort; proto$1.monthsParse = localeMonthsParse; proto$1.monthsRegex = monthsRegex; proto$1.monthsShortRegex = monthsShortRegex; // Week proto$1.week = localeWeek; proto$1.firstDayOfYear = localeFirstDayOfYear; proto$1.firstDayOfWeek = localeFirstDayOfWeek; // Day of Week proto$1.weekdays = localeWeekdays; proto$1.weekdaysMin = localeWeekdaysMin; proto$1.weekdaysShort = localeWeekdaysShort; proto$1.weekdaysParse = localeWeekdaysParse; proto$1.weekdaysRegex = weekdaysRegex; proto$1.weekdaysShortRegex = weekdaysShortRegex; proto$1.weekdaysMinRegex = weekdaysMinRegex; // Hours proto$1.isPM = localeIsPM; proto$1.meridiem = localeMeridiem; function get$1 (format, index, field, setter) { var locale = getLocale(); var utc = createUTC().set(setter, index); return locale[field](utc, format); } function listMonthsImpl (format, index, field) { if (isNumber(format)) { index = format; format = undefined; } format = format || ''; if (index != null) { return get$1(format, index, field, 'month'); } var i; var out = []; for (i = 0; i < 12; i++) { out[i] = get$1(format, i, field, 'month'); } return out; } // () // (5) // (fmt, 5) // (fmt) // (true) // (true, 5) // (true, fmt, 5) // (true, fmt) function listWeekdaysImpl (localeSorted, format, index, field) { if (typeof localeSorted === 'boolean') { if (isNumber(format)) { index = format; format = undefined; } format = format || ''; } else { format = localeSorted; index = format; localeSorted = false; if (isNumber(format)) { index = format; format = undefined; } format = format || ''; } var locale = getLocale(), shift = localeSorted ? locale._week.dow : 0; if (index != null) { return get$1(format, (index + shift) % 7, field, 'day'); } var i; var out = []; for (i = 0; i < 7; i++) { out[i] = get$1(format, (i + shift) % 7, field, 'day'); } return out; } function listMonths (format, index) { return listMonthsImpl(format, index, 'months'); } function listMonthsShort (format, index) { return listMonthsImpl(format, index, 'monthsShort'); } function listWeekdays (localeSorted, format, index) { return listWeekdaysImpl(localeSorted, format, index, 'weekdays'); } function listWeekdaysShort (localeSorted, format, index) { return listWeekdaysImpl(localeSorted, format, index, 'weekdaysShort'); } function listWeekdaysMin (localeSorted, format, index) { return listWeekdaysImpl(localeSorted, format, index, 'weekdaysMin'); } getSetGlobalLocale('en', { dayOfMonthOrdinalParse: /\d{1,2}(th|st|nd|rd)/, ordinal : function (number) { var b = number % 10, output = (toInt(number % 100 / 10) === 1) ? 'th' : (b === 1) ? 'st' : (b === 2) ? 'nd' : (b === 3) ? 'rd' : 'th'; return number + output; } }); // Side effect imports hooks.lang = deprecate('moment.lang is deprecated. Use moment.locale instead.', getSetGlobalLocale); hooks.langData = deprecate('moment.langData is deprecated. Use moment.localeData instead.', getLocale); var mathAbs = Math.abs; function abs () { var data = this._data; this._milliseconds = mathAbs(this._milliseconds); this._days = mathAbs(this._days); this._months = mathAbs(this._months); data.milliseconds = mathAbs(data.milliseconds); data.seconds = mathAbs(data.seconds); data.minutes = mathAbs(data.minutes); data.hours = mathAbs(data.hours); data.months = mathAbs(data.months); data.years = mathAbs(data.years); return this; } function addSubtract$1 (duration, input, value, direction) { var other = createDuration(input, value); duration._milliseconds += direction * other._milliseconds; duration._days += direction * other._days; duration._months += direction * other._months; return duration._bubble(); } // supports only 2.0-style add(1, 's') or add(duration) function add$1 (input, value) { return addSubtract$1(this, input, value, 1); } // supports only 2.0-style subtract(1, 's') or subtract(duration) function subtract$1 (input, value) { return addSubtract$1(this, input, value, -1); } function absCeil (number) { if (number < 0) { return Math.floor(number); } else { return Math.ceil(number); } } function bubble () { var milliseconds = this._milliseconds; var days = this._days; var months = this._months; var data = this._data; var seconds, minutes, hours, years, monthsFromDays; // if we have a mix of positive and negative values, bubble down first // check: https://github.com/moment/moment/issues/2166 if (!((milliseconds >= 0 && days >= 0 && months >= 0) || (milliseconds <= 0 && days <= 0 && months <= 0))) { milliseconds += absCeil(monthsToDays(months) + days) * 864e5; days = 0; months = 0; } // The following code bubbles up values, see the tests for // examples of what that means. data.milliseconds = milliseconds % 1000; seconds = absFloor(milliseconds / 1000); data.seconds = seconds % 60; minutes = absFloor(seconds / 60); data.minutes = minutes % 60; hours = absFloor(minutes / 60); data.hours = hours % 24; days += absFloor(hours / 24); // convert days to months monthsFromDays = absFloor(daysToMonths(days)); months += monthsFromDays; days -= absCeil(monthsToDays(monthsFromDays)); // 12 months -> 1 year years = absFloor(months / 12); months %= 12; data.days = days; data.months = months; data.years = years; return this; } function daysToMonths (days) { // 400 years have 146097 days (taking into account leap year rules) // 400 years have 12 months === 4800 return days * 4800 / 146097; } function monthsToDays (months) { // the reverse of daysToMonths return months * 146097 / 4800; } function as (units) { if (!this.isValid()) { return NaN; } var days; var months; var milliseconds = this._milliseconds; units = normalizeUnits(units); if (units === 'month' || units === 'year') { days = this._days + milliseconds / 864e5; months = this._months + daysToMonths(days); return units === 'month' ? months : months / 12; } else { // handle milliseconds separately because of floating point math errors (issue #1867) days = this._days + Math.round(monthsToDays(this._months)); switch (units) { case 'week' : return days / 7 + milliseconds / 6048e5; case 'day' : return days + milliseconds / 864e5; case 'hour' : return days * 24 + milliseconds / 36e5; case 'minute' : return days * 1440 + milliseconds / 6e4; case 'second' : return days * 86400 + milliseconds / 1000; // Math.floor prevents floating point math errors here case 'millisecond': return Math.floor(days * 864e5) + milliseconds; default: throw new Error('Unknown unit ' + units); } } } // TODO: Use this.as('ms')? function valueOf$1 () { if (!this.isValid()) { return NaN; } return ( this._milliseconds + this._days * 864e5 + (this._months % 12) * 2592e6 + toInt(this._months / 12) * 31536e6 ); } function makeAs (alias) { return function () { return this.as(alias); }; } var asMilliseconds = makeAs('ms'); var asSeconds = makeAs('s'); var asMinutes = makeAs('m'); var asHours = makeAs('h'); var asDays = makeAs('d'); var asWeeks = makeAs('w'); var asMonths = makeAs('M'); var asYears = makeAs('y'); function get$2 (units) { units = normalizeUnits(units); return this.isValid() ? this[units + 's']() : NaN; } function makeGetter(name) { return function () { return this.isValid() ? this._data[name] : NaN; }; } var milliseconds = makeGetter('milliseconds'); var seconds = makeGetter('seconds'); var minutes = makeGetter('minutes'); var hours = makeGetter('hours'); var days = makeGetter('days'); var months = makeGetter('months'); var years = makeGetter('years'); function weeks () { return absFloor(this.days() / 7); } var round = Math.round; var thresholds = { ss: 44, // a few seconds to seconds s : 45, // seconds to minute m : 45, // minutes to hour h : 22, // hours to day d : 26, // days to month M : 11 // months to year }; // helper function for moment.fn.from, moment.fn.fromNow, and moment.duration.fn.humanize function substituteTimeAgo(string, number, withoutSuffix, isFuture, locale) { return locale.relativeTime(number || 1, !!withoutSuffix, string, isFuture); } function relativeTime$1 (posNegDuration, withoutSuffix, locale) { var duration = createDuration(posNegDuration).abs(); var seconds = round(duration.as('s')); var minutes = round(duration.as('m')); var hours = round(duration.as('h')); var days = round(duration.as('d')); var months = round(duration.as('M')); var years = round(duration.as('y')); var a = seconds <= thresholds.ss && ['s', seconds] || seconds < thresholds.s && ['ss', seconds] || minutes <= 1 && ['m'] || minutes < thresholds.m && ['mm', minutes] || hours <= 1 && ['h'] || hours < thresholds.h && ['hh', hours] || days <= 1 && ['d'] || days < thresholds.d && ['dd', days] || months <= 1 && ['M'] || months < thresholds.M && ['MM', months] || years <= 1 && ['y'] || ['yy', years]; a[2] = withoutSuffix; a[3] = +posNegDuration > 0; a[4] = locale; return substituteTimeAgo.apply(null, a); } // This function allows you to set the rounding function for relative time strings function getSetRelativeTimeRounding (roundingFunction) { if (roundingFunction === undefined) { return round; } if (typeof(roundingFunction) === 'function') { round = roundingFunction; return true; } return false; } // This function allows you to set a threshold for relative time strings function getSetRelativeTimeThreshold (threshold, limit) { if (thresholds[threshold] === undefined) { return false; } if (limit === undefined) { return thresholds[threshold]; } thresholds[threshold] = limit; if (threshold === 's') { thresholds.ss = limit - 1; } return true; } function humanize (withSuffix) { if (!this.isValid()) { return this.localeData().invalidDate(); } var locale = this.localeData(); var output = relativeTime$1(this, !withSuffix, locale); if (withSuffix) { output = locale.pastFuture(+this, output); } return locale.postformat(output); } var abs$1 = Math.abs; function toISOString$1() { // for ISO strings we do not use the normal bubbling rules: // * milliseconds bubble up until they become hours // * days do not bubble at all // * months bubble up until they become years // This is because there is no context-free conversion between hours and days // (think of clock changes) // and also not between days and months (28-31 days per month) if (!this.isValid()) { return this.localeData().invalidDate(); } var seconds = abs$1(this._milliseconds) / 1000; var days = abs$1(this._days); var months = abs$1(this._months); var minutes, hours, years; // 3600 seconds -> 60 minutes -> 1 hour minutes = absFloor(seconds / 60); hours = absFloor(minutes / 60); seconds %= 60; minutes %= 60; // 12 months -> 1 year years = absFloor(months / 12); months %= 12; // inspired by https://github.com/dordille/moment-isoduration/blob/master/moment.isoduration.js var Y = years; var M = months; var D = days; var h = hours; var m = minutes; var s = seconds; var total = this.asSeconds(); if (!total) { // this is the same as C#'s (Noda) and python (isodate)... // but not other JS (goog.date) return 'P0D'; } return (total < 0 ? '-' : '') + 'P' + (Y ? Y + 'Y' : '') + (M ? M + 'M' : '') + (D ? D + 'D' : '') + ((h || m || s) ? 'T' : '') + (h ? h + 'H' : '') + (m ? m + 'M' : '') + (s ? s + 'S' : ''); } var proto$2 = Duration.prototype; proto$2.isValid = isValid$1; proto$2.abs = abs; proto$2.add = add$1; proto$2.subtract = subtract$1; proto$2.as = as; proto$2.asMilliseconds = asMilliseconds; proto$2.asSeconds = asSeconds; proto$2.asMinutes = asMinutes; proto$2.asHours = asHours; proto$2.asDays = asDays; proto$2.asWeeks = asWeeks; proto$2.asMonths = asMonths; proto$2.asYears = asYears; proto$2.valueOf = valueOf$1; proto$2._bubble = bubble; proto$2.get = get$2; proto$2.milliseconds = milliseconds; proto$2.seconds = seconds; proto$2.minutes = minutes; proto$2.hours = hours; proto$2.days = days; proto$2.weeks = weeks; proto$2.months = months; proto$2.years = years; proto$2.humanize = humanize; proto$2.toISOString = toISOString$1; proto$2.toString = toISOString$1; proto$2.toJSON = toISOString$1; proto$2.locale = locale; proto$2.localeData = localeData; // Deprecations proto$2.toIsoString = deprecate('toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)', toISOString$1); proto$2.lang = lang; // Side effect imports // FORMATTING addFormatToken('X', 0, 0, 'unix'); addFormatToken('x', 0, 0, 'valueOf'); // PARSING addRegexToken('x', matchSigned); addRegexToken('X', matchTimestamp); addParseToken('X', function (input, array, config) { config._d = new Date(parseFloat(input, 10) * 1000); }); addParseToken('x', function (input, array, config) { config._d = new Date(toInt(input)); }); // Side effect imports hooks.version = '2.18.1'; setHookCallback(createLocal); hooks.fn = proto; hooks.min = min; hooks.max = max; hooks.now = now; hooks.utc = createUTC; hooks.unix = createUnix; hooks.months = listMonths; hooks.isDate = isDate; hooks.locale = getSetGlobalLocale; hooks.invalid = createInvalid; hooks.duration = createDuration; hooks.isMoment = isMoment; hooks.weekdays = listWeekdays; hooks.parseZone = createInZone; hooks.localeData = getLocale; hooks.isDuration = isDuration; hooks.monthsShort = listMonthsShort; hooks.weekdaysMin = listWeekdaysMin; hooks.defineLocale = defineLocale; hooks.updateLocale = updateLocale; hooks.locales = listLocales; hooks.weekdaysShort = listWeekdaysShort; hooks.normalizeUnits = normalizeUnits; hooks.relativeTimeRounding = getSetRelativeTimeRounding; hooks.relativeTimeThreshold = getSetRelativeTimeThreshold; hooks.calendarFormat = getCalendarFormat; hooks.prototype = proto; return hooks; }))); },{}],7:[function(require,module,exports){ /** * @namespace Chart */ var Chart = require(29)(); Chart.helpers = require(45); // @todo dispatch these helpers into appropriated helpers/helpers.* file and write unit tests! require(27)(Chart); Chart.defaults = require(25); Chart.Element = require(26); Chart.elements = require(40); Chart.Interaction = require(28); Chart.platform = require(48); require(31)(Chart); require(22)(Chart); require(23)(Chart); require(24)(Chart); require(30)(Chart); require(33)(Chart); require(32)(Chart); require(35)(Chart); require(54)(Chart); require(52)(Chart); require(53)(Chart); require(55)(Chart); require(56)(Chart); require(57)(Chart); // Controllers must be loaded after elements // See Chart.core.datasetController.dataElementType require(15)(Chart); require(16)(Chart); require(17)(Chart); require(18)(Chart); require(19)(Chart); require(20)(Chart); require(21)(Chart); require(8)(Chart); require(9)(Chart); require(10)(Chart); require(11)(Chart); require(12)(Chart); require(13)(Chart); require(14)(Chart); // Loading built-it plugins var plugins = []; plugins.push( require(49)(Chart), require(50)(Chart), require(51)(Chart) ); Chart.plugins.register(plugins); Chart.platform.initialize(); module.exports = Chart; if (typeof window !== 'undefined') { window.Chart = Chart; } // DEPRECATIONS /** * Provided for backward compatibility, use Chart.helpers.canvas instead. * @namespace Chart.canvasHelpers * @deprecated since version 2.6.0 * @todo remove at version 3 * @private */ Chart.canvasHelpers = Chart.helpers.canvas; },{"10":10,"11":11,"12":12,"13":13,"14":14,"15":15,"16":16,"17":17,"18":18,"19":19,"20":20,"21":21,"22":22,"23":23,"24":24,"25":25,"26":26,"27":27,"28":28,"29":29,"30":30,"31":31,"32":32,"33":33,"35":35,"40":40,"45":45,"48":48,"49":49,"50":50,"51":51,"52":52,"53":53,"54":54,"55":55,"56":56,"57":57,"8":8,"9":9}],8:[function(require,module,exports){ 'use strict'; module.exports = function(Chart) { Chart.Bar = function(context, config) { config.type = 'bar'; return new Chart(context, config); }; }; },{}],9:[function(require,module,exports){ 'use strict'; module.exports = function(Chart) { Chart.Bubble = function(context, config) { config.type = 'bubble'; return new Chart(context, config); }; }; },{}],10:[function(require,module,exports){ 'use strict'; module.exports = function(Chart) { Chart.Doughnut = function(context, config) { config.type = 'doughnut'; return new Chart(context, config); }; }; },{}],11:[function(require,module,exports){ 'use strict'; module.exports = function(Chart) { Chart.Line = function(context, config) { config.type = 'line'; return new Chart(context, config); }; }; },{}],12:[function(require,module,exports){ 'use strict'; module.exports = function(Chart) { Chart.PolarArea = function(context, config) { config.type = 'polarArea'; return new Chart(context, config); }; }; },{}],13:[function(require,module,exports){ 'use strict'; module.exports = function(Chart) { Chart.Radar = function(context, config) { config.type = 'radar'; return new Chart(context, config); }; }; },{}],14:[function(require,module,exports){ 'use strict'; module.exports = function(Chart) { Chart.Scatter = function(context, config) { config.type = 'scatter'; return new Chart(context, config); }; }; },{}],15:[function(require,module,exports){ 'use strict'; var defaults = require(25); var elements = require(40); var helpers = require(45); defaults._set('bar', { hover: { mode: 'label' }, scales: { xAxes: [{ type: 'category', // Specific to Bar Controller categoryPercentage: 0.8, barPercentage: 0.9, // offset settings offset: true, // grid line settings gridLines: { offsetGridLines: true } }], yAxes: [{ type: 'linear' }] } }); defaults._set('horizontalBar', { hover: { mode: 'index', axis: 'y' }, scales: { xAxes: [{ type: 'linear', position: 'bottom' }], yAxes: [{ position: 'left', type: 'category', // Specific to Horizontal Bar Controller categoryPercentage: 0.8, barPercentage: 0.9, // offset settings offset: true, // grid line settings gridLines: { offsetGridLines: true } }] }, elements: { rectangle: { borderSkipped: 'left' } }, tooltips: { callbacks: { title: function(item, data) { // Pick first xLabel for now var title = ''; if (item.length > 0) { if (item[0].yLabel) { title = item[0].yLabel; } else if (data.labels.length > 0 && item[0].index < data.labels.length) { title = data.labels[item[0].index]; } } return title; }, label: function(item, data) { var datasetLabel = data.datasets[item.datasetIndex].label || ''; return datasetLabel + ': ' + item.xLabel; } }, mode: 'index', axis: 'y' } }); module.exports = function(Chart) { Chart.controllers.bar = Chart.DatasetController.extend({ dataElementType: elements.Rectangle, initialize: function() { var me = this; var meta; Chart.DatasetController.prototype.initialize.apply(me, arguments); meta = me.getMeta(); meta.stack = me.getDataset().stack; meta.bar = true; }, update: function(reset) { var me = this; var rects = me.getMeta().data; var i, ilen; me._ruler = me.getRuler(); for (i = 0, ilen = rects.length; i < ilen; ++i) { me.updateElement(rects[i], i, reset); } }, updateElement: function(rectangle, index, reset) { var me = this; var chart = me.chart; var meta = me.getMeta(); var dataset = me.getDataset(); var custom = rectangle.custom || {}; var rectangleOptions = chart.options.elements.rectangle; rectangle._xScale = me.getScaleForId(meta.xAxisID); rectangle._yScale = me.getScaleForId(meta.yAxisID); rectangle._datasetIndex = me.index; rectangle._index = index; rectangle._model = { datasetLabel: dataset.label, label: chart.data.labels[index], borderSkipped: custom.borderSkipped ? custom.borderSkipped : rectangleOptions.borderSkipped, backgroundColor: custom.backgroundColor ? custom.backgroundColor : helpers.valueAtIndexOrDefault(dataset.backgroundColor, index, rectangleOptions.backgroundColor), borderColor: custom.borderColor ? custom.borderColor : helpers.valueAtIndexOrDefault(dataset.borderColor, index, rectangleOptions.borderColor), borderWidth: custom.borderWidth ? custom.borderWidth : helpers.valueAtIndexOrDefault(dataset.borderWidth, index, rectangleOptions.borderWidth) }; me.updateElementGeometry(rectangle, index, reset); rectangle.pivot(); }, /** * @private */ updateElementGeometry: function(rectangle, index, reset) { var me = this; var model = rectangle._model; var vscale = me.getValueScale(); var base = vscale.getBasePixel(); var horizontal = vscale.isHorizontal(); var ruler = me._ruler || me.getRuler(); var vpixels = me.calculateBarValuePixels(me.index, index); var ipixels = me.calculateBarIndexPixels(me.index, index, ruler); model.horizontal = horizontal; model.base = reset ? base : vpixels.base; model.x = horizontal ? reset ? base : vpixels.head : ipixels.center; model.y = horizontal ? ipixels.center : reset ? base : vpixels.head; model.height = horizontal ? ipixels.size : undefined; model.width = horizontal ? undefined : ipixels.size; }, /** * @private */ getValueScaleId: function() { return this.getMeta().yAxisID; }, /** * @private */ getIndexScaleId: function() { return this.getMeta().xAxisID; }, /** * @private */ getValueScale: function() { return this.getScaleForId(this.getValueScaleId()); }, /** * @private */ getIndexScale: function() { return this.getScaleForId(this.getIndexScaleId()); }, /** * Returns the effective number of stacks based on groups and bar visibility. * @private */ getStackCount: function(last) { var me = this; var chart = me.chart; var scale = me.getIndexScale(); var stacked = scale.options.stacked; var ilen = last === undefined ? chart.data.datasets.length : last + 1; var stacks = []; var i, meta; for (i = 0; i < ilen; ++i) { meta = chart.getDatasetMeta(i); if (meta.bar && chart.isDatasetVisible(i) && (stacked === false || (stacked === true && stacks.indexOf(meta.stack) === -1) || (stacked === undefined && (meta.stack === undefined || stacks.indexOf(meta.stack) === -1)))) { stacks.push(meta.stack); } } return stacks.length; }, /** * Returns the stack index for the given dataset based on groups and bar visibility. * @private */ getStackIndex: function(datasetIndex) { return this.getStackCount(datasetIndex) - 1; }, /** * @private */ getRuler: function() { var me = this; var scale = me.getIndexScale(); var stackCount = me.getStackCount(); var datasetIndex = me.index; var pixels = []; var isHorizontal = scale.isHorizontal(); var start = isHorizontal ? scale.left : scale.top; var end = start + (isHorizontal ? scale.width : scale.height); var i, ilen; for (i = 0, ilen = me.getMeta().data.length; i < ilen; ++i) { pixels.push(scale.getPixelForValue(null, i, datasetIndex)); } return { pixels: pixels, start: start, end: end, stackCount: stackCount, scale: scale }; }, /** * Note: pixel values are not clamped to the scale area. * @private */ calculateBarValuePixels: function(datasetIndex, index) { var me = this; var chart = me.chart; var meta = me.getMeta(); var scale = me.getValueScale(); var datasets = chart.data.datasets; var value = scale.getRightValue(datasets[datasetIndex].data[index]); var stacked = scale.options.stacked; var stack = meta.stack; var start = 0; var i, imeta, ivalue, base, head, size; if (stacked || (stacked === undefined && stack !== undefined)) { for (i = 0; i < datasetIndex; ++i) { imeta = chart.getDatasetMeta(i); if (imeta.bar && imeta.stack === stack && imeta.controller.getValueScaleId() === scale.id && chart.isDatasetVisible(i)) { ivalue = scale.getRightValue(datasets[i].data[index]); if ((value < 0 && ivalue < 0) || (value >= 0 && ivalue > 0)) { start += ivalue; } } } } base = scale.getPixelForValue(start); head = scale.getPixelForValue(start + value); size = (head - base) / 2; return { size: size, base: base, head: head, center: head + size / 2 }; }, /** * @private */ calculateBarIndexPixels: function(datasetIndex, index, ruler) { var me = this; var options = ruler.scale.options; var stackIndex = me.getStackIndex(datasetIndex); var pixels = ruler.pixels; var base = pixels[index]; var length = pixels.length; var start = ruler.start; var end = ruler.end; var leftSampleSize, rightSampleSize, leftCategorySize, rightCategorySize, fullBarSize, size; if (length === 1) { leftSampleSize = base > start ? base - start : end - base; rightSampleSize = base < end ? end - base : base - start; } else { if (index > 0) { leftSampleSize = (base - pixels[index - 1]) / 2; if (index === length - 1) { rightSampleSize = leftSampleSize; } } if (index < length - 1) { rightSampleSize = (pixels[index + 1] - base) / 2; if (index === 0) { leftSampleSize = rightSampleSize; } } } leftCategorySize = leftSampleSize * options.categoryPercentage; rightCategorySize = rightSampleSize * options.categoryPercentage; fullBarSize = (leftCategorySize + rightCategorySize) / ruler.stackCount; size = fullBarSize * options.barPercentage; size = Math.min( helpers.valueOrDefault(options.barThickness, size), helpers.valueOrDefault(options.maxBarThickness, Infinity)); base -= leftCategorySize; base += fullBarSize * stackIndex; base += (fullBarSize - size) / 2; return { size: size, base: base, head: base + size, center: base + size / 2 }; }, draw: function() { var me = this; var chart = me.chart; var scale = me.getValueScale(); var rects = me.getMeta().data; var dataset = me.getDataset(); var ilen = rects.length; var i = 0; helpers.canvas.clipArea(chart.ctx, chart.chartArea); for (; i < ilen; ++i) { if (!isNaN(scale.getRightValue(dataset.data[i]))) { rects[i].draw(); } } helpers.canvas.unclipArea(chart.ctx); }, setHoverStyle: function(rectangle) { var dataset = this.chart.data.datasets[rectangle._datasetIndex]; var index = rectangle._index; var custom = rectangle.custom || {}; var model = rectangle._model; model.backgroundColor = custom.hoverBackgroundColor ? custom.hoverBackgroundColor : helpers.valueAtIndexOrDefault(dataset.hoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor)); model.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : helpers.valueAtIndexOrDefault(dataset.hoverBorderColor, index, helpers.getHoverColor(model.borderColor)); model.borderWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : helpers.valueAtIndexOrDefault(dataset.hoverBorderWidth, index, model.borderWidth); }, removeHoverStyle: function(rectangle) { var dataset = this.chart.data.datasets[rectangle._datasetIndex]; var index = rectangle._index; var custom = rectangle.custom || {}; var model = rectangle._model; var rectangleElementOptions = this.chart.options.elements.rectangle; model.backgroundColor = custom.backgroundColor ? custom.backgroundColor : helpers.valueAtIndexOrDefault(dataset.backgroundColor, index, rectangleElementOptions.backgroundColor); model.borderColor = custom.borderColor ? custom.borderColor : helpers.valueAtIndexOrDefault(dataset.borderColor, index, rectangleElementOptions.borderColor); model.borderWidth = custom.borderWidth ? custom.borderWidth : helpers.valueAtIndexOrDefault(dataset.borderWidth, index, rectangleElementOptions.borderWidth); } }); Chart.controllers.horizontalBar = Chart.controllers.bar.extend({ /** * @private */ getValueScaleId: function() { return this.getMeta().xAxisID; }, /** * @private */ getIndexScaleId: function() { return this.getMeta().yAxisID; } }); }; },{"25":25,"40":40,"45":45}],16:[function(require,module,exports){ 'use strict'; var defaults = require(25); var elements = require(40); var helpers = require(45); defaults._set('bubble', { hover: { mode: 'single' }, scales: { xAxes: [{ type: 'linear', // bubble should probably use a linear scale by default position: 'bottom', id: 'x-axis-0' // need an ID so datasets can reference the scale }], yAxes: [{ type: 'linear', position: 'left', id: 'y-axis-0' }] }, tooltips: { callbacks: { title: function() { // Title doesn't make sense for scatter since we format the data as a point return ''; }, label: function(item, data) { var datasetLabel = data.datasets[item.datasetIndex].label || ''; var dataPoint = data.datasets[item.datasetIndex].data[item.index]; return datasetLabel + ': (' + item.xLabel + ', ' + item.yLabel + ', ' + dataPoint.r + ')'; } } } }); module.exports = function(Chart) { Chart.controllers.bubble = Chart.DatasetController.extend({ /** * @protected */ dataElementType: elements.Point, /** * @protected */ update: function(reset) { var me = this; var meta = me.getMeta(); var points = meta.data; // Update Points helpers.each(points, function(point, index) { me.updateElement(point, index, reset); }); }, /** * @protected */ updateElement: function(point, index, reset) { var me = this; var meta = me.getMeta(); var custom = point.custom || {}; var xScale = me.getScaleForId(meta.xAxisID); var yScale = me.getScaleForId(meta.yAxisID); var options = me._resolveElementOptions(point, index); var data = me.getDataset().data[index]; var dsIndex = me.index; var x = reset ? xScale.getPixelForDecimal(0.5) : xScale.getPixelForValue(typeof data === 'object' ? data : NaN, index, dsIndex); var y = reset ? yScale.getBasePixel() : yScale.getPixelForValue(data, index, dsIndex); point._xScale = xScale; point._yScale = yScale; point._options = options; point._datasetIndex = dsIndex; point._index = index; point._model = { backgroundColor: options.backgroundColor, borderColor: options.borderColor, borderWidth: options.borderWidth, hitRadius: options.hitRadius, pointStyle: options.pointStyle, radius: reset ? 0 : options.radius, skip: custom.skip || isNaN(x) || isNaN(y), x: x, y: y, }; point.pivot(); }, /** * @protected */ setHoverStyle: function(point) { var model = point._model; var options = point._options; model.backgroundColor = helpers.valueOrDefault(options.hoverBackgroundColor, helpers.getHoverColor(options.backgroundColor)); model.borderColor = helpers.valueOrDefault(options.hoverBorderColor, helpers.getHoverColor(options.borderColor)); model.borderWidth = helpers.valueOrDefault(options.hoverBorderWidth, options.borderWidth); model.radius = options.radius + options.hoverRadius; }, /** * @protected */ removeHoverStyle: function(point) { var model = point._model; var options = point._options; model.backgroundColor = options.backgroundColor; model.borderColor = options.borderColor; model.borderWidth = options.borderWidth; model.radius = options.radius; }, /** * @private */ _resolveElementOptions: function(point, index) { var me = this; var chart = me.chart; var datasets = chart.data.datasets; var dataset = datasets[me.index]; var custom = point.custom || {}; var options = chart.options.elements.point; var resolve = helpers.options.resolve; var data = dataset.data[index]; var values = {}; var i, ilen, key; // Scriptable options var context = { chart: chart, dataIndex: index, dataset: dataset, datasetIndex: me.index }; var keys = [ 'backgroundColor', 'borderColor', 'borderWidth', 'hoverBackgroundColor', 'hoverBorderColor', 'hoverBorderWidth', 'hoverRadius', 'hitRadius', 'pointStyle' ]; for (i = 0, ilen = keys.length; i < ilen; ++i) { key = keys[i]; values[key] = resolve([ custom[key], dataset[key], options[key] ], context, index); } // Custom radius resolution values.radius = resolve([ custom.radius, data ? data.r : undefined, dataset.radius, options.radius ], context, index); return values; } }); }; },{"25":25,"40":40,"45":45}],17:[function(require,module,exports){ 'use strict'; var defaults = require(25); var elements = require(40); var helpers = require(45); defaults._set('doughnut', { animation: { // Boolean - Whether we animate the rotation of the Doughnut animateRotate: true, // Boolean - Whether we animate scaling the Doughnut from the centre animateScale: false }, hover: { mode: 'single' }, legendCallback: function(chart) { var text = []; text.push('
    '); var data = chart.data; var datasets = data.datasets; var labels = data.labels; if (datasets.length) { for (var i = 0; i < datasets[0].data.length; ++i) { text.push('
  • '); if (labels[i]) { text.push(labels[i]); } text.push('
  • '); } } text.push('
'); return text.join(''); }, legend: { labels: { generateLabels: function(chart) { var data = chart.data; if (data.labels.length && data.datasets.length) { return data.labels.map(function(label, i) { var meta = chart.getDatasetMeta(0); var ds = data.datasets[0]; var arc = meta.data[i]; var custom = arc && arc.custom || {}; var valueAtIndexOrDefault = helpers.valueAtIndexOrDefault; var arcOpts = chart.options.elements.arc; var fill = custom.backgroundColor ? custom.backgroundColor : valueAtIndexOrDefault(ds.backgroundColor, i, arcOpts.backgroundColor); var stroke = custom.borderColor ? custom.borderColor : valueAtIndexOrDefault(ds.borderColor, i, arcOpts.borderColor); var bw = custom.borderWidth ? custom.borderWidth : valueAtIndexOrDefault(ds.borderWidth, i, arcOpts.borderWidth); return { text: label, fillStyle: fill, strokeStyle: stroke, lineWidth: bw, hidden: isNaN(ds.data[i]) || meta.data[i].hidden, // Extra data used for toggling the correct item index: i }; }); } return []; } }, onClick: function(e, legendItem) { var index = legendItem.index; var chart = this.chart; var i, ilen, meta; for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) { meta = chart.getDatasetMeta(i); // toggle visibility of index if exists if (meta.data[index]) { meta.data[index].hidden = !meta.data[index].hidden; } } chart.update(); } }, // The percentage of the chart that we cut out of the middle. cutoutPercentage: 50, // The rotation of the chart, where the first data arc begins. rotation: Math.PI * -0.5, // The total circumference of the chart. circumference: Math.PI * 2.0, // Need to override these to give a nice default tooltips: { callbacks: { title: function() { return ''; }, label: function(tooltipItem, data) { var dataLabel = data.labels[tooltipItem.index]; var value = ': ' + data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index]; if (helpers.isArray(dataLabel)) { // show value on first line of multiline label // need to clone because we are changing the value dataLabel = dataLabel.slice(); dataLabel[0] += value; } else { dataLabel += value; } return dataLabel; } } } }); defaults._set('pie', helpers.clone(defaults.doughnut)); defaults._set('pie', { cutoutPercentage: 0 }); module.exports = function(Chart) { Chart.controllers.doughnut = Chart.controllers.pie = Chart.DatasetController.extend({ dataElementType: elements.Arc, linkScales: helpers.noop, // Get index of the dataset in relation to the visible datasets. This allows determining the inner and outer radius correctly getRingIndex: function(datasetIndex) { var ringIndex = 0; for (var j = 0; j < datasetIndex; ++j) { if (this.chart.isDatasetVisible(j)) { ++ringIndex; } } return ringIndex; }, update: function(reset) { var me = this; var chart = me.chart; var chartArea = chart.chartArea; var opts = chart.options; var arcOpts = opts.elements.arc; var availableWidth = chartArea.right - chartArea.left - arcOpts.borderWidth; var availableHeight = chartArea.bottom - chartArea.top - arcOpts.borderWidth; var minSize = Math.min(availableWidth, availableHeight); var offset = {x: 0, y: 0}; var meta = me.getMeta(); var cutoutPercentage = opts.cutoutPercentage; var circumference = opts.circumference; // If the chart's circumference isn't a full circle, calculate minSize as a ratio of the width/height of the arc if (circumference < Math.PI * 2.0) { var startAngle = opts.rotation % (Math.PI * 2.0); startAngle += Math.PI * 2.0 * (startAngle >= Math.PI ? -1 : startAngle < -Math.PI ? 1 : 0); var endAngle = startAngle + circumference; var start = {x: Math.cos(startAngle), y: Math.sin(startAngle)}; var end = {x: Math.cos(endAngle), y: Math.sin(endAngle)}; var contains0 = (startAngle <= 0 && endAngle >= 0) || (startAngle <= Math.PI * 2.0 && Math.PI * 2.0 <= endAngle); var contains90 = (startAngle <= Math.PI * 0.5 && Math.PI * 0.5 <= endAngle) || (startAngle <= Math.PI * 2.5 && Math.PI * 2.5 <= endAngle); var contains180 = (startAngle <= -Math.PI && -Math.PI <= endAngle) || (startAngle <= Math.PI && Math.PI <= endAngle); var contains270 = (startAngle <= -Math.PI * 0.5 && -Math.PI * 0.5 <= endAngle) || (startAngle <= Math.PI * 1.5 && Math.PI * 1.5 <= endAngle); var cutout = cutoutPercentage / 100.0; var min = {x: contains180 ? -1 : Math.min(start.x * (start.x < 0 ? 1 : cutout), end.x * (end.x < 0 ? 1 : cutout)), y: contains270 ? -1 : Math.min(start.y * (start.y < 0 ? 1 : cutout), end.y * (end.y < 0 ? 1 : cutout))}; var max = {x: contains0 ? 1 : Math.max(start.x * (start.x > 0 ? 1 : cutout), end.x * (end.x > 0 ? 1 : cutout)), y: contains90 ? 1 : Math.max(start.y * (start.y > 0 ? 1 : cutout), end.y * (end.y > 0 ? 1 : cutout))}; var size = {width: (max.x - min.x) * 0.5, height: (max.y - min.y) * 0.5}; minSize = Math.min(availableWidth / size.width, availableHeight / size.height); offset = {x: (max.x + min.x) * -0.5, y: (max.y + min.y) * -0.5}; } chart.borderWidth = me.getMaxBorderWidth(meta.data); chart.outerRadius = Math.max((minSize - chart.borderWidth) / 2, 0); chart.innerRadius = Math.max(cutoutPercentage ? (chart.outerRadius / 100) * (cutoutPercentage) : 0, 0); chart.radiusLength = (chart.outerRadius - chart.innerRadius) / chart.getVisibleDatasetCount(); chart.offsetX = offset.x * chart.outerRadius; chart.offsetY = offset.y * chart.outerRadius; meta.total = me.calculateTotal(); me.outerRadius = chart.outerRadius - (chart.radiusLength * me.getRingIndex(me.index)); me.innerRadius = Math.max(me.outerRadius - chart.radiusLength, 0); helpers.each(meta.data, function(arc, index) { me.updateElement(arc, index, reset); }); }, updateElement: function(arc, index, reset) { var me = this; var chart = me.chart; var chartArea = chart.chartArea; var opts = chart.options; var animationOpts = opts.animation; var centerX = (chartArea.left + chartArea.right) / 2; var centerY = (chartArea.top + chartArea.bottom) / 2; var startAngle = opts.rotation; // non reset case handled later var endAngle = opts.rotation; // non reset case handled later var dataset = me.getDataset(); var circumference = reset && animationOpts.animateRotate ? 0 : arc.hidden ? 0 : me.calculateCircumference(dataset.data[index]) * (opts.circumference / (2.0 * Math.PI)); var innerRadius = reset && animationOpts.animateScale ? 0 : me.innerRadius; var outerRadius = reset && animationOpts.animateScale ? 0 : me.outerRadius; var valueAtIndexOrDefault = helpers.valueAtIndexOrDefault; helpers.extend(arc, { // Utility _datasetIndex: me.index, _index: index, // Desired view properties _model: { x: centerX + chart.offsetX, y: centerY + chart.offsetY, startAngle: startAngle, endAngle: endAngle, circumference: circumference, outerRadius: outerRadius, innerRadius: innerRadius, label: valueAtIndexOrDefault(dataset.label, index, chart.data.labels[index]) } }); var model = arc._model; // Resets the visual styles this.removeHoverStyle(arc); // Set correct angles if not resetting if (!reset || !animationOpts.animateRotate) { if (index === 0) { model.startAngle = opts.rotation; } else { model.startAngle = me.getMeta().data[index - 1]._model.endAngle; } model.endAngle = model.startAngle + model.circumference; } arc.pivot(); }, removeHoverStyle: function(arc) { Chart.DatasetController.prototype.removeHoverStyle.call(this, arc, this.chart.options.elements.arc); }, calculateTotal: function() { var dataset = this.getDataset(); var meta = this.getMeta(); var total = 0; var value; helpers.each(meta.data, function(element, index) { value = dataset.data[index]; if (!isNaN(value) && !element.hidden) { total += Math.abs(value); } }); /* if (total === 0) { total = NaN; }*/ return total; }, calculateCircumference: function(value) { var total = this.getMeta().total; if (total > 0 && !isNaN(value)) { return (Math.PI * 2.0) * (value / total); } return 0; }, // gets the max border or hover width to properly scale pie charts getMaxBorderWidth: function(arcs) { var max = 0; var index = this.index; var length = arcs.length; var borderWidth; var hoverWidth; for (var i = 0; i < length; i++) { borderWidth = arcs[i]._model ? arcs[i]._model.borderWidth : 0; hoverWidth = arcs[i]._chart ? arcs[i]._chart.config.data.datasets[index].hoverBorderWidth : 0; max = borderWidth > max ? borderWidth : max; max = hoverWidth > max ? hoverWidth : max; } return max; } }); }; },{"25":25,"40":40,"45":45}],18:[function(require,module,exports){ 'use strict'; var defaults = require(25); var elements = require(40); var helpers = require(45); defaults._set('line', { showLines: true, spanGaps: false, hover: { mode: 'label' }, scales: { xAxes: [{ type: 'category', id: 'x-axis-0' }], yAxes: [{ type: 'linear', id: 'y-axis-0' }] } }); module.exports = function(Chart) { function lineEnabled(dataset, options) { return helpers.valueOrDefault(dataset.showLine, options.showLines); } Chart.controllers.line = Chart.DatasetController.extend({ datasetElementType: elements.Line, dataElementType: elements.Point, update: function(reset) { var me = this; var meta = me.getMeta(); var line = meta.dataset; var points = meta.data || []; var options = me.chart.options; var lineElementOptions = options.elements.line; var scale = me.getScaleForId(meta.yAxisID); var i, ilen, custom; var dataset = me.getDataset(); var showLine = lineEnabled(dataset, options); // Update Line if (showLine) { custom = line.custom || {}; // Compatibility: If the properties are defined with only the old name, use those values if ((dataset.tension !== undefined) && (dataset.lineTension === undefined)) { dataset.lineTension = dataset.tension; } // Utility line._scale = scale; line._datasetIndex = me.index; // Data line._children = points; // Model line._model = { // Appearance // The default behavior of lines is to break at null values, according // to https://github.com/chartjs/Chart.js/issues/2435#issuecomment-216718158 // This option gives lines the ability to span gaps spanGaps: dataset.spanGaps ? dataset.spanGaps : options.spanGaps, tension: custom.tension ? custom.tension : helpers.valueOrDefault(dataset.lineTension, lineElementOptions.tension), backgroundColor: custom.backgroundColor ? custom.backgroundColor : (dataset.backgroundColor || lineElementOptions.backgroundColor), borderWidth: custom.borderWidth ? custom.borderWidth : (dataset.borderWidth || lineElementOptions.borderWidth), borderColor: custom.borderColor ? custom.borderColor : (dataset.borderColor || lineElementOptions.borderColor), borderCapStyle: custom.borderCapStyle ? custom.borderCapStyle : (dataset.borderCapStyle || lineElementOptions.borderCapStyle), borderDash: custom.borderDash ? custom.borderDash : (dataset.borderDash || lineElementOptions.borderDash), borderDashOffset: custom.borderDashOffset ? custom.borderDashOffset : (dataset.borderDashOffset || lineElementOptions.borderDashOffset), borderJoinStyle: custom.borderJoinStyle ? custom.borderJoinStyle : (dataset.borderJoinStyle || lineElementOptions.borderJoinStyle), fill: custom.fill ? custom.fill : (dataset.fill !== undefined ? dataset.fill : lineElementOptions.fill), steppedLine: custom.steppedLine ? custom.steppedLine : helpers.valueOrDefault(dataset.steppedLine, lineElementOptions.stepped), cubicInterpolationMode: custom.cubicInterpolationMode ? custom.cubicInterpolationMode : helpers.valueOrDefault(dataset.cubicInterpolationMode, lineElementOptions.cubicInterpolationMode), }; line.pivot(); } // Update Points for (i = 0, ilen = points.length; i < ilen; ++i) { me.updateElement(points[i], i, reset); } if (showLine && line._model.tension !== 0) { me.updateBezierControlPoints(); } // Now pivot the point for animation for (i = 0, ilen = points.length; i < ilen; ++i) { points[i].pivot(); } }, getPointBackgroundColor: function(point, index) { var backgroundColor = this.chart.options.elements.point.backgroundColor; var dataset = this.getDataset(); var custom = point.custom || {}; if (custom.backgroundColor) { backgroundColor = custom.backgroundColor; } else if (dataset.pointBackgroundColor) { backgroundColor = helpers.valueAtIndexOrDefault(dataset.pointBackgroundColor, index, backgroundColor); } else if (dataset.backgroundColor) { backgroundColor = dataset.backgroundColor; } return backgroundColor; }, getPointBorderColor: function(point, index) { var borderColor = this.chart.options.elements.point.borderColor; var dataset = this.getDataset(); var custom = point.custom || {}; if (custom.borderColor) { borderColor = custom.borderColor; } else if (dataset.pointBorderColor) { borderColor = helpers.valueAtIndexOrDefault(dataset.pointBorderColor, index, borderColor); } else if (dataset.borderColor) { borderColor = dataset.borderColor; } return borderColor; }, getPointBorderWidth: function(point, index) { var borderWidth = this.chart.options.elements.point.borderWidth; var dataset = this.getDataset(); var custom = point.custom || {}; if (!isNaN(custom.borderWidth)) { borderWidth = custom.borderWidth; } else if (!isNaN(dataset.pointBorderWidth) || helpers.isArray(dataset.pointBorderWidth)) { borderWidth = helpers.valueAtIndexOrDefault(dataset.pointBorderWidth, index, borderWidth); } else if (!isNaN(dataset.borderWidth)) { borderWidth = dataset.borderWidth; } return borderWidth; }, updateElement: function(point, index, reset) { var me = this; var meta = me.getMeta(); var custom = point.custom || {}; var dataset = me.getDataset(); var datasetIndex = me.index; var value = dataset.data[index]; var yScale = me.getScaleForId(meta.yAxisID); var xScale = me.getScaleForId(meta.xAxisID); var pointOptions = me.chart.options.elements.point; var x, y; // Compatibility: If the properties are defined with only the old name, use those values if ((dataset.radius !== undefined) && (dataset.pointRadius === undefined)) { dataset.pointRadius = dataset.radius; } if ((dataset.hitRadius !== undefined) && (dataset.pointHitRadius === undefined)) { dataset.pointHitRadius = dataset.hitRadius; } x = xScale.getPixelForValue(typeof value === 'object' ? value : NaN, index, datasetIndex); y = reset ? yScale.getBasePixel() : me.calculatePointY(value, index, datasetIndex); // Utility point._xScale = xScale; point._yScale = yScale; point._datasetIndex = datasetIndex; point._index = index; // Desired view properties point._model = { x: x, y: y, skip: custom.skip || isNaN(x) || isNaN(y), // Appearance radius: custom.radius || helpers.valueAtIndexOrDefault(dataset.pointRadius, index, pointOptions.radius), pointStyle: custom.pointStyle || helpers.valueAtIndexOrDefault(dataset.pointStyle, index, pointOptions.pointStyle), backgroundColor: me.getPointBackgroundColor(point, index), borderColor: me.getPointBorderColor(point, index), borderWidth: me.getPointBorderWidth(point, index), tension: meta.dataset._model ? meta.dataset._model.tension : 0, steppedLine: meta.dataset._model ? meta.dataset._model.steppedLine : false, // Tooltip hitRadius: custom.hitRadius || helpers.valueAtIndexOrDefault(dataset.pointHitRadius, index, pointOptions.hitRadius) }; }, calculatePointY: function(value, index, datasetIndex) { var me = this; var chart = me.chart; var meta = me.getMeta(); var yScale = me.getScaleForId(meta.yAxisID); var sumPos = 0; var sumNeg = 0; var i, ds, dsMeta; if (yScale.options.stacked) { for (i = 0; i < datasetIndex; i++) { ds = chart.data.datasets[i]; dsMeta = chart.getDatasetMeta(i); if (dsMeta.type === 'line' && dsMeta.yAxisID === yScale.id && chart.isDatasetVisible(i)) { var stackedRightValue = Number(yScale.getRightValue(ds.data[index])); if (stackedRightValue < 0) { sumNeg += stackedRightValue || 0; } else { sumPos += stackedRightValue || 0; } } } var rightValue = Number(yScale.getRightValue(value)); if (rightValue < 0) { return yScale.getPixelForValue(sumNeg + rightValue); } return yScale.getPixelForValue(sumPos + rightValue); } return yScale.getPixelForValue(value); }, updateBezierControlPoints: function() { var me = this; var meta = me.getMeta(); var area = me.chart.chartArea; var points = (meta.data || []); var i, ilen, point, model, controlPoints; // Only consider points that are drawn in case the spanGaps option is used if (meta.dataset._model.spanGaps) { points = points.filter(function(pt) { return !pt._model.skip; }); } function capControlPoint(pt, min, max) { return Math.max(Math.min(pt, max), min); } if (meta.dataset._model.cubicInterpolationMode === 'monotone') { helpers.splineCurveMonotone(points); } else { for (i = 0, ilen = points.length; i < ilen; ++i) { point = points[i]; model = point._model; controlPoints = helpers.splineCurve( helpers.previousItem(points, i)._model, model, helpers.nextItem(points, i)._model, meta.dataset._model.tension ); model.controlPointPreviousX = controlPoints.previous.x; model.controlPointPreviousY = controlPoints.previous.y; model.controlPointNextX = controlPoints.next.x; model.controlPointNextY = controlPoints.next.y; } } if (me.chart.options.elements.line.capBezierPoints) { for (i = 0, ilen = points.length; i < ilen; ++i) { model = points[i]._model; model.controlPointPreviousX = capControlPoint(model.controlPointPreviousX, area.left, area.right); model.controlPointPreviousY = capControlPoint(model.controlPointPreviousY, area.top, area.bottom); model.controlPointNextX = capControlPoint(model.controlPointNextX, area.left, area.right); model.controlPointNextY = capControlPoint(model.controlPointNextY, area.top, area.bottom); } } }, draw: function() { var me = this; var chart = me.chart; var meta = me.getMeta(); var points = meta.data || []; var area = chart.chartArea; var ilen = points.length; var i = 0; helpers.canvas.clipArea(chart.ctx, area); if (lineEnabled(me.getDataset(), chart.options)) { meta.dataset.draw(); } helpers.canvas.unclipArea(chart.ctx); // Draw the points for (; i < ilen; ++i) { points[i].draw(area); } }, setHoverStyle: function(point) { // Point var dataset = this.chart.data.datasets[point._datasetIndex]; var index = point._index; var custom = point.custom || {}; var model = point._model; model.radius = custom.hoverRadius || helpers.valueAtIndexOrDefault(dataset.pointHoverRadius, index, this.chart.options.elements.point.hoverRadius); model.backgroundColor = custom.hoverBackgroundColor || helpers.valueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor)); model.borderColor = custom.hoverBorderColor || helpers.valueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.getHoverColor(model.borderColor)); model.borderWidth = custom.hoverBorderWidth || helpers.valueAtIndexOrDefault(dataset.pointHoverBorderWidth, index, model.borderWidth); }, removeHoverStyle: function(point) { var me = this; var dataset = me.chart.data.datasets[point._datasetIndex]; var index = point._index; var custom = point.custom || {}; var model = point._model; // Compatibility: If the properties are defined with only the old name, use those values if ((dataset.radius !== undefined) && (dataset.pointRadius === undefined)) { dataset.pointRadius = dataset.radius; } model.radius = custom.radius || helpers.valueAtIndexOrDefault(dataset.pointRadius, index, me.chart.options.elements.point.radius); model.backgroundColor = me.getPointBackgroundColor(point, index); model.borderColor = me.getPointBorderColor(point, index); model.borderWidth = me.getPointBorderWidth(point, index); } }); }; },{"25":25,"40":40,"45":45}],19:[function(require,module,exports){ 'use strict'; var defaults = require(25); var elements = require(40); var helpers = require(45); defaults._set('polarArea', { scale: { type: 'radialLinear', angleLines: { display: false }, gridLines: { circular: true }, pointLabels: { display: false }, ticks: { beginAtZero: true } }, // Boolean - Whether to animate the rotation of the chart animation: { animateRotate: true, animateScale: true }, startAngle: -0.5 * Math.PI, legendCallback: function(chart) { var text = []; text.push('
    '); var data = chart.data; var datasets = data.datasets; var labels = data.labels; if (datasets.length) { for (var i = 0; i < datasets[0].data.length; ++i) { text.push('
  • '); if (labels[i]) { text.push(labels[i]); } text.push('
  • '); } } text.push('
'); return text.join(''); }, legend: { labels: { generateLabels: function(chart) { var data = chart.data; if (data.labels.length && data.datasets.length) { return data.labels.map(function(label, i) { var meta = chart.getDatasetMeta(0); var ds = data.datasets[0]; var arc = meta.data[i]; var custom = arc.custom || {}; var valueAtIndexOrDefault = helpers.valueAtIndexOrDefault; var arcOpts = chart.options.elements.arc; var fill = custom.backgroundColor ? custom.backgroundColor : valueAtIndexOrDefault(ds.backgroundColor, i, arcOpts.backgroundColor); var stroke = custom.borderColor ? custom.borderColor : valueAtIndexOrDefault(ds.borderColor, i, arcOpts.borderColor); var bw = custom.borderWidth ? custom.borderWidth : valueAtIndexOrDefault(ds.borderWidth, i, arcOpts.borderWidth); return { text: label, fillStyle: fill, strokeStyle: stroke, lineWidth: bw, hidden: isNaN(ds.data[i]) || meta.data[i].hidden, // Extra data used for toggling the correct item index: i }; }); } return []; } }, onClick: function(e, legendItem) { var index = legendItem.index; var chart = this.chart; var i, ilen, meta; for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) { meta = chart.getDatasetMeta(i); meta.data[index].hidden = !meta.data[index].hidden; } chart.update(); } }, // Need to override these to give a nice default tooltips: { callbacks: { title: function() { return ''; }, label: function(item, data) { return data.labels[item.index] + ': ' + item.yLabel; } } } }); module.exports = function(Chart) { Chart.controllers.polarArea = Chart.DatasetController.extend({ dataElementType: elements.Arc, linkScales: helpers.noop, update: function(reset) { var me = this; var chart = me.chart; var chartArea = chart.chartArea; var meta = me.getMeta(); var opts = chart.options; var arcOpts = opts.elements.arc; var minSize = Math.min(chartArea.right - chartArea.left, chartArea.bottom - chartArea.top); chart.outerRadius = Math.max((minSize - arcOpts.borderWidth / 2) / 2, 0); chart.innerRadius = Math.max(opts.cutoutPercentage ? (chart.outerRadius / 100) * (opts.cutoutPercentage) : 1, 0); chart.radiusLength = (chart.outerRadius - chart.innerRadius) / chart.getVisibleDatasetCount(); me.outerRadius = chart.outerRadius - (chart.radiusLength * me.index); me.innerRadius = me.outerRadius - chart.radiusLength; meta.count = me.countVisibleElements(); helpers.each(meta.data, function(arc, index) { me.updateElement(arc, index, reset); }); }, updateElement: function(arc, index, reset) { var me = this; var chart = me.chart; var dataset = me.getDataset(); var opts = chart.options; var animationOpts = opts.animation; var scale = chart.scale; var labels = chart.data.labels; var circumference = me.calculateCircumference(dataset.data[index]); var centerX = scale.xCenter; var centerY = scale.yCenter; // If there is NaN data before us, we need to calculate the starting angle correctly. // We could be way more efficient here, but its unlikely that the polar area chart will have a lot of data var visibleCount = 0; var meta = me.getMeta(); for (var i = 0; i < index; ++i) { if (!isNaN(dataset.data[i]) && !meta.data[i].hidden) { ++visibleCount; } } // var negHalfPI = -0.5 * Math.PI; var datasetStartAngle = opts.startAngle; var distance = arc.hidden ? 0 : scale.getDistanceFromCenterForValue(dataset.data[index]); var startAngle = datasetStartAngle + (circumference * visibleCount); var endAngle = startAngle + (arc.hidden ? 0 : circumference); var resetRadius = animationOpts.animateScale ? 0 : scale.getDistanceFromCenterForValue(dataset.data[index]); helpers.extend(arc, { // Utility _datasetIndex: me.index, _index: index, _scale: scale, // Desired view properties _model: { x: centerX, y: centerY, innerRadius: 0, outerRadius: reset ? resetRadius : distance, startAngle: reset && animationOpts.animateRotate ? datasetStartAngle : startAngle, endAngle: reset && animationOpts.animateRotate ? datasetStartAngle : endAngle, label: helpers.valueAtIndexOrDefault(labels, index, labels[index]) } }); // Apply border and fill style me.removeHoverStyle(arc); arc.pivot(); }, removeHoverStyle: function(arc) { Chart.DatasetController.prototype.removeHoverStyle.call(this, arc, this.chart.options.elements.arc); }, countVisibleElements: function() { var dataset = this.getDataset(); var meta = this.getMeta(); var count = 0; helpers.each(meta.data, function(element, index) { if (!isNaN(dataset.data[index]) && !element.hidden) { count++; } }); return count; }, calculateCircumference: function(value) { var count = this.getMeta().count; if (count > 0 && !isNaN(value)) { return (2 * Math.PI) / count; } return 0; } }); }; },{"25":25,"40":40,"45":45}],20:[function(require,module,exports){ 'use strict'; var defaults = require(25); var elements = require(40); var helpers = require(45); defaults._set('radar', { scale: { type: 'radialLinear' }, elements: { line: { tension: 0 // no bezier in radar } } }); module.exports = function(Chart) { Chart.controllers.radar = Chart.DatasetController.extend({ datasetElementType: elements.Line, dataElementType: elements.Point, linkScales: helpers.noop, update: function(reset) { var me = this; var meta = me.getMeta(); var line = meta.dataset; var points = meta.data; var custom = line.custom || {}; var dataset = me.getDataset(); var lineElementOptions = me.chart.options.elements.line; var scale = me.chart.scale; // Compatibility: If the properties are defined with only the old name, use those values if ((dataset.tension !== undefined) && (dataset.lineTension === undefined)) { dataset.lineTension = dataset.tension; } helpers.extend(meta.dataset, { // Utility _datasetIndex: me.index, _scale: scale, // Data _children: points, _loop: true, // Model _model: { // Appearance tension: custom.tension ? custom.tension : helpers.valueOrDefault(dataset.lineTension, lineElementOptions.tension), backgroundColor: custom.backgroundColor ? custom.backgroundColor : (dataset.backgroundColor || lineElementOptions.backgroundColor), borderWidth: custom.borderWidth ? custom.borderWidth : (dataset.borderWidth || lineElementOptions.borderWidth), borderColor: custom.borderColor ? custom.borderColor : (dataset.borderColor || lineElementOptions.borderColor), fill: custom.fill ? custom.fill : (dataset.fill !== undefined ? dataset.fill : lineElementOptions.fill), borderCapStyle: custom.borderCapStyle ? custom.borderCapStyle : (dataset.borderCapStyle || lineElementOptions.borderCapStyle), borderDash: custom.borderDash ? custom.borderDash : (dataset.borderDash || lineElementOptions.borderDash), borderDashOffset: custom.borderDashOffset ? custom.borderDashOffset : (dataset.borderDashOffset || lineElementOptions.borderDashOffset), borderJoinStyle: custom.borderJoinStyle ? custom.borderJoinStyle : (dataset.borderJoinStyle || lineElementOptions.borderJoinStyle), } }); meta.dataset.pivot(); // Update Points helpers.each(points, function(point, index) { me.updateElement(point, index, reset); }, me); // Update bezier control points me.updateBezierControlPoints(); }, updateElement: function(point, index, reset) { var me = this; var custom = point.custom || {}; var dataset = me.getDataset(); var scale = me.chart.scale; var pointElementOptions = me.chart.options.elements.point; var pointPosition = scale.getPointPositionForValue(index, dataset.data[index]); // Compatibility: If the properties are defined with only the old name, use those values if ((dataset.radius !== undefined) && (dataset.pointRadius === undefined)) { dataset.pointRadius = dataset.radius; } if ((dataset.hitRadius !== undefined) && (dataset.pointHitRadius === undefined)) { dataset.pointHitRadius = dataset.hitRadius; } helpers.extend(point, { // Utility _datasetIndex: me.index, _index: index, _scale: scale, // Desired view properties _model: { x: reset ? scale.xCenter : pointPosition.x, // value not used in dataset scale, but we want a consistent API between scales y: reset ? scale.yCenter : pointPosition.y, // Appearance tension: custom.tension ? custom.tension : helpers.valueOrDefault(dataset.lineTension, me.chart.options.elements.line.tension), radius: custom.radius ? custom.radius : helpers.valueAtIndexOrDefault(dataset.pointRadius, index, pointElementOptions.radius), backgroundColor: custom.backgroundColor ? custom.backgroundColor : helpers.valueAtIndexOrDefault(dataset.pointBackgroundColor, index, pointElementOptions.backgroundColor), borderColor: custom.borderColor ? custom.borderColor : helpers.valueAtIndexOrDefault(dataset.pointBorderColor, index, pointElementOptions.borderColor), borderWidth: custom.borderWidth ? custom.borderWidth : helpers.valueAtIndexOrDefault(dataset.pointBorderWidth, index, pointElementOptions.borderWidth), pointStyle: custom.pointStyle ? custom.pointStyle : helpers.valueAtIndexOrDefault(dataset.pointStyle, index, pointElementOptions.pointStyle), // Tooltip hitRadius: custom.hitRadius ? custom.hitRadius : helpers.valueAtIndexOrDefault(dataset.pointHitRadius, index, pointElementOptions.hitRadius) } }); point._model.skip = custom.skip ? custom.skip : (isNaN(point._model.x) || isNaN(point._model.y)); }, updateBezierControlPoints: function() { var chartArea = this.chart.chartArea; var meta = this.getMeta(); helpers.each(meta.data, function(point, index) { var model = point._model; var controlPoints = helpers.splineCurve( helpers.previousItem(meta.data, index, true)._model, model, helpers.nextItem(meta.data, index, true)._model, model.tension ); // Prevent the bezier going outside of the bounds of the graph model.controlPointPreviousX = Math.max(Math.min(controlPoints.previous.x, chartArea.right), chartArea.left); model.controlPointPreviousY = Math.max(Math.min(controlPoints.previous.y, chartArea.bottom), chartArea.top); model.controlPointNextX = Math.max(Math.min(controlPoints.next.x, chartArea.right), chartArea.left); model.controlPointNextY = Math.max(Math.min(controlPoints.next.y, chartArea.bottom), chartArea.top); // Now pivot the point for animation point.pivot(); }); }, setHoverStyle: function(point) { // Point var dataset = this.chart.data.datasets[point._datasetIndex]; var custom = point.custom || {}; var index = point._index; var model = point._model; model.radius = custom.hoverRadius ? custom.hoverRadius : helpers.valueAtIndexOrDefault(dataset.pointHoverRadius, index, this.chart.options.elements.point.hoverRadius); model.backgroundColor = custom.hoverBackgroundColor ? custom.hoverBackgroundColor : helpers.valueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor)); model.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : helpers.valueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.getHoverColor(model.borderColor)); model.borderWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : helpers.valueAtIndexOrDefault(dataset.pointHoverBorderWidth, index, model.borderWidth); }, removeHoverStyle: function(point) { var dataset = this.chart.data.datasets[point._datasetIndex]; var custom = point.custom || {}; var index = point._index; var model = point._model; var pointElementOptions = this.chart.options.elements.point; model.radius = custom.radius ? custom.radius : helpers.valueAtIndexOrDefault(dataset.pointRadius, index, pointElementOptions.radius); model.backgroundColor = custom.backgroundColor ? custom.backgroundColor : helpers.valueAtIndexOrDefault(dataset.pointBackgroundColor, index, pointElementOptions.backgroundColor); model.borderColor = custom.borderColor ? custom.borderColor : helpers.valueAtIndexOrDefault(dataset.pointBorderColor, index, pointElementOptions.borderColor); model.borderWidth = custom.borderWidth ? custom.borderWidth : helpers.valueAtIndexOrDefault(dataset.pointBorderWidth, index, pointElementOptions.borderWidth); } }); }; },{"25":25,"40":40,"45":45}],21:[function(require,module,exports){ 'use strict'; var defaults = require(25); defaults._set('scatter', { hover: { mode: 'single' }, scales: { xAxes: [{ id: 'x-axis-1', // need an ID so datasets can reference the scale type: 'linear', // scatter should not use a category axis position: 'bottom' }], yAxes: [{ id: 'y-axis-1', type: 'linear', position: 'left' }] }, showLines: false, tooltips: { callbacks: { title: function() { return ''; // doesn't make sense for scatter since data are formatted as a point }, label: function(item) { return '(' + item.xLabel + ', ' + item.yLabel + ')'; } } } }); module.exports = function(Chart) { // Scatter charts use line controllers Chart.controllers.scatter = Chart.controllers.line; }; },{"25":25}],22:[function(require,module,exports){ /* global window: false */ 'use strict'; var defaults = require(25); var Element = require(26); var helpers = require(45); defaults._set('global', { animation: { duration: 1000, easing: 'easeOutQuart', onProgress: helpers.noop, onComplete: helpers.noop } }); module.exports = function(Chart) { Chart.Animation = Element.extend({ chart: null, // the animation associated chart instance currentStep: 0, // the current animation step numSteps: 60, // default number of steps easing: '', // the easing to use for this animation render: null, // render function used by the animation service onAnimationProgress: null, // user specified callback to fire on each step of the animation onAnimationComplete: null, // user specified callback to fire when the animation finishes }); Chart.animationService = { frameDuration: 17, animations: [], dropFrames: 0, request: null, /** * @param {Chart} chart - The chart to animate. * @param {Chart.Animation} animation - The animation that we will animate. * @param {Number} duration - The animation duration in ms. * @param {Boolean} lazy - if true, the chart is not marked as animating to enable more responsive interactions */ addAnimation: function(chart, animation, duration, lazy) { var animations = this.animations; var i, ilen; animation.chart = chart; if (!lazy) { chart.animating = true; } for (i = 0, ilen = animations.length; i < ilen; ++i) { if (animations[i].chart === chart) { animations[i] = animation; return; } } animations.push(animation); // If there are no animations queued, manually kickstart a digest, for lack of a better word if (animations.length === 1) { this.requestAnimationFrame(); } }, cancelAnimation: function(chart) { var index = helpers.findIndex(this.animations, function(animation) { return animation.chart === chart; }); if (index !== -1) { this.animations.splice(index, 1); chart.animating = false; } }, requestAnimationFrame: function() { var me = this; if (me.request === null) { // Skip animation frame requests until the active one is executed. // This can happen when processing mouse events, e.g. 'mousemove' // and 'mouseout' events will trigger multiple renders. me.request = helpers.requestAnimFrame.call(window, function() { me.request = null; me.startDigest(); }); } }, /** * @private */ startDigest: function() { var me = this; var startTime = Date.now(); var framesToDrop = 0; if (me.dropFrames > 1) { framesToDrop = Math.floor(me.dropFrames); me.dropFrames = me.dropFrames % 1; } me.advance(1 + framesToDrop); var endTime = Date.now(); me.dropFrames += (endTime - startTime) / me.frameDuration; // Do we have more stuff to animate? if (me.animations.length > 0) { me.requestAnimationFrame(); } }, /** * @private */ advance: function(count) { var animations = this.animations; var animation, chart; var i = 0; while (i < animations.length) { animation = animations[i]; chart = animation.chart; animation.currentStep = (animation.currentStep || 0) + count; animation.currentStep = Math.min(animation.currentStep, animation.numSteps); helpers.callback(animation.render, [chart, animation], chart); helpers.callback(animation.onAnimationProgress, [animation], chart); if (animation.currentStep >= animation.numSteps) { helpers.callback(animation.onAnimationComplete, [animation], chart); chart.animating = false; animations.splice(i, 1); } else { ++i; } } } }; /** * Provided for backward compatibility, use Chart.Animation instead * @prop Chart.Animation#animationObject * @deprecated since version 2.6.0 * @todo remove at version 3 */ Object.defineProperty(Chart.Animation.prototype, 'animationObject', { get: function() { return this; } }); /** * Provided for backward compatibility, use Chart.Animation#chart instead * @prop Chart.Animation#chartInstance * @deprecated since version 2.6.0 * @todo remove at version 3 */ Object.defineProperty(Chart.Animation.prototype, 'chartInstance', { get: function() { return this.chart; }, set: function(value) { this.chart = value; } }); }; },{"25":25,"26":26,"45":45}],23:[function(require,module,exports){ 'use strict'; var defaults = require(25); var helpers = require(45); var Interaction = require(28); var platform = require(48); module.exports = function(Chart) { var plugins = Chart.plugins; // Create a dictionary of chart types, to allow for extension of existing types Chart.types = {}; // Store a reference to each instance - allowing us to globally resize chart instances on window resize. // Destroy method on the chart will remove the instance of the chart from this reference. Chart.instances = {}; // Controllers available for dataset visualization eg. bar, line, slice, etc. Chart.controllers = {}; /** * Initializes the given config with global and chart default values. */ function initConfig(config) { config = config || {}; // Do NOT use configMerge() for the data object because this method merges arrays // and so would change references to labels and datasets, preventing data updates. var data = config.data = config.data || {}; data.datasets = data.datasets || []; data.labels = data.labels || []; config.options = helpers.configMerge( defaults.global, defaults[config.type], config.options || {}); return config; } /** * Updates the config of the chart * @param chart {Chart} chart to update the options for */ function updateConfig(chart) { var newOptions = chart.options; // Update Scale(s) with options if (newOptions.scale) { chart.scale.options = newOptions.scale; } else if (newOptions.scales) { newOptions.scales.xAxes.concat(newOptions.scales.yAxes).forEach(function(scaleOptions) { chart.scales[scaleOptions.id].options = scaleOptions; }); } // Tooltip chart.tooltip._options = newOptions.tooltips; } function positionIsHorizontal(position) { return position === 'top' || position === 'bottom'; } helpers.extend(Chart.prototype, /** @lends Chart */ { /** * @private */ construct: function(item, config) { var me = this; config = initConfig(config); var context = platform.acquireContext(item, config); var canvas = context && context.canvas; var height = canvas && canvas.height; var width = canvas && canvas.width; me.id = helpers.uid(); me.ctx = context; me.canvas = canvas; me.config = config; me.width = width; me.height = height; me.aspectRatio = height ? width / height : null; me.options = config.options; me._bufferedRender = false; /** * Provided for backward compatibility, Chart and Chart.Controller have been merged, * the "instance" still need to be defined since it might be called from plugins. * @prop Chart#chart * @deprecated since version 2.6.0 * @todo remove at version 3 * @private */ me.chart = me; me.controller = me; // chart.chart.controller #inception // Add the chart instance to the global namespace Chart.instances[me.id] = me; // Define alias to the config data: `chart.data === chart.config.data` Object.defineProperty(me, 'data', { get: function() { return me.config.data; }, set: function(value) { me.config.data = value; } }); if (!context || !canvas) { // The given item is not a compatible context2d element, let's return before finalizing // the chart initialization but after setting basic chart / controller properties that // can help to figure out that the chart is not valid (e.g chart.canvas !== null); // https://github.com/chartjs/Chart.js/issues/2807 console.error("Failed to create chart: can't acquire context from the given item"); return; } me.initialize(); me.update(); }, /** * @private */ initialize: function() { var me = this; // Before init plugin notification plugins.notify(me, 'beforeInit'); helpers.retinaScale(me, me.options.devicePixelRatio); me.bindEvents(); if (me.options.responsive) { // Initial resize before chart draws (must be silent to preserve initial animations). me.resize(true); } // Make sure scales have IDs and are built before we build any controllers. me.ensureScalesHaveIDs(); me.buildScales(); me.initToolTip(); // After init plugin notification plugins.notify(me, 'afterInit'); return me; }, clear: function() { helpers.canvas.clear(this); return this; }, stop: function() { // Stops any current animation loop occurring Chart.animationService.cancelAnimation(this); return this; }, resize: function(silent) { var me = this; var options = me.options; var canvas = me.canvas; var aspectRatio = (options.maintainAspectRatio && me.aspectRatio) || null; // the canvas render width and height will be casted to integers so make sure that // the canvas display style uses the same integer values to avoid blurring effect. // Set to 0 instead of canvas.size because the size defaults to 300x150 if the element is collased var newWidth = Math.max(0, Math.floor(helpers.getMaximumWidth(canvas))); var newHeight = Math.max(0, Math.floor(aspectRatio ? newWidth / aspectRatio : helpers.getMaximumHeight(canvas))); if (me.width === newWidth && me.height === newHeight) { return; } canvas.width = me.width = newWidth; canvas.height = me.height = newHeight; canvas.style.width = newWidth + 'px'; canvas.style.height = newHeight + 'px'; helpers.retinaScale(me, options.devicePixelRatio); if (!silent) { // Notify any plugins about the resize var newSize = {width: newWidth, height: newHeight}; plugins.notify(me, 'resize', [newSize]); // Notify of resize if (me.options.onResize) { me.options.onResize(me, newSize); } me.stop(); me.update(me.options.responsiveAnimationDuration); } }, ensureScalesHaveIDs: function() { var options = this.options; var scalesOptions = options.scales || {}; var scaleOptions = options.scale; helpers.each(scalesOptions.xAxes, function(xAxisOptions, index) { xAxisOptions.id = xAxisOptions.id || ('x-axis-' + index); }); helpers.each(scalesOptions.yAxes, function(yAxisOptions, index) { yAxisOptions.id = yAxisOptions.id || ('y-axis-' + index); }); if (scaleOptions) { scaleOptions.id = scaleOptions.id || 'scale'; } }, /** * Builds a map of scale ID to scale object for future lookup. */ buildScales: function() { var me = this; var options = me.options; var scales = me.scales = {}; var items = []; if (options.scales) { items = items.concat( (options.scales.xAxes || []).map(function(xAxisOptions) { return {options: xAxisOptions, dtype: 'category', dposition: 'bottom'}; }), (options.scales.yAxes || []).map(function(yAxisOptions) { return {options: yAxisOptions, dtype: 'linear', dposition: 'left'}; }) ); } if (options.scale) { items.push({ options: options.scale, dtype: 'radialLinear', isDefault: true, dposition: 'chartArea' }); } helpers.each(items, function(item) { var scaleOptions = item.options; var scaleType = helpers.valueOrDefault(scaleOptions.type, item.dtype); var scaleClass = Chart.scaleService.getScaleConstructor(scaleType); if (!scaleClass) { return; } if (positionIsHorizontal(scaleOptions.position) !== positionIsHorizontal(item.dposition)) { scaleOptions.position = item.dposition; } var scale = new scaleClass({ id: scaleOptions.id, options: scaleOptions, ctx: me.ctx, chart: me }); scales[scale.id] = scale; scale.mergeTicksOptions(); // TODO(SB): I think we should be able to remove this custom case (options.scale) // and consider it as a regular scale part of the "scales"" map only! This would // make the logic easier and remove some useless? custom code. if (item.isDefault) { me.scale = scale; } }); Chart.scaleService.addScalesToLayout(this); }, buildOrUpdateControllers: function() { var me = this; var types = []; var newControllers = []; helpers.each(me.data.datasets, function(dataset, datasetIndex) { var meta = me.getDatasetMeta(datasetIndex); var type = dataset.type || me.config.type; if (meta.type && meta.type !== type) { me.destroyDatasetMeta(datasetIndex); meta = me.getDatasetMeta(datasetIndex); } meta.type = type; types.push(meta.type); if (meta.controller) { meta.controller.updateIndex(datasetIndex); } else { var ControllerClass = Chart.controllers[meta.type]; if (ControllerClass === undefined) { throw new Error('"' + meta.type + '" is not a chart type.'); } meta.controller = new ControllerClass(me, datasetIndex); newControllers.push(meta.controller); } }, me); return newControllers; }, /** * Reset the elements of all datasets * @private */ resetElements: function() { var me = this; helpers.each(me.data.datasets, function(dataset, datasetIndex) { me.getDatasetMeta(datasetIndex).controller.reset(); }, me); }, /** * Resets the chart back to it's state before the initial animation */ reset: function() { this.resetElements(); this.tooltip.initialize(); }, update: function(config) { var me = this; if (!config || typeof config !== 'object') { // backwards compatibility config = { duration: config, lazy: arguments[1] }; } updateConfig(me); if (plugins.notify(me, 'beforeUpdate') === false) { return; } // In case the entire data object changed me.tooltip._data = me.data; // Make sure dataset controllers are updated and new controllers are reset var newControllers = me.buildOrUpdateControllers(); // Make sure all dataset controllers have correct meta data counts helpers.each(me.data.datasets, function(dataset, datasetIndex) { me.getDatasetMeta(datasetIndex).controller.buildOrUpdateElements(); }, me); me.updateLayout(); // Can only reset the new controllers after the scales have been updated helpers.each(newControllers, function(controller) { controller.reset(); }); me.updateDatasets(); // Need to reset tooltip in case it is displayed with elements that are removed // after update. me.tooltip.initialize(); // Last active contains items that were previously in the tooltip. // When we reset the tooltip, we need to clear it me.lastActive = []; // Do this before render so that any plugins that need final scale updates can use it plugins.notify(me, 'afterUpdate'); if (me._bufferedRender) { me._bufferedRequest = { duration: config.duration, easing: config.easing, lazy: config.lazy }; } else { me.render(config); } }, /** * Updates the chart layout unless a plugin returns `false` to the `beforeLayout` * hook, in which case, plugins will not be called on `afterLayout`. * @private */ updateLayout: function() { var me = this; if (plugins.notify(me, 'beforeLayout') === false) { return; } Chart.layoutService.update(this, this.width, this.height); /** * Provided for backward compatibility, use `afterLayout` instead. * @method IPlugin#afterScaleUpdate * @deprecated since version 2.5.0 * @todo remove at version 3 * @private */ plugins.notify(me, 'afterScaleUpdate'); plugins.notify(me, 'afterLayout'); }, /** * Updates all datasets unless a plugin returns `false` to the `beforeDatasetsUpdate` * hook, in which case, plugins will not be called on `afterDatasetsUpdate`. * @private */ updateDatasets: function() { var me = this; if (plugins.notify(me, 'beforeDatasetsUpdate') === false) { return; } for (var i = 0, ilen = me.data.datasets.length; i < ilen; ++i) { me.updateDataset(i); } plugins.notify(me, 'afterDatasetsUpdate'); }, /** * Updates dataset at index unless a plugin returns `false` to the `beforeDatasetUpdate` * hook, in which case, plugins will not be called on `afterDatasetUpdate`. * @private */ updateDataset: function(index) { var me = this; var meta = me.getDatasetMeta(index); var args = { meta: meta, index: index }; if (plugins.notify(me, 'beforeDatasetUpdate', [args]) === false) { return; } meta.controller.update(); plugins.notify(me, 'afterDatasetUpdate', [args]); }, render: function(config) { var me = this; if (!config || typeof config !== 'object') { // backwards compatibility config = { duration: config, lazy: arguments[1] }; } var duration = config.duration; var lazy = config.lazy; if (plugins.notify(me, 'beforeRender') === false) { return; } var animationOptions = me.options.animation; var onComplete = function(animation) { plugins.notify(me, 'afterRender'); helpers.callback(animationOptions && animationOptions.onComplete, [animation], me); }; if (animationOptions && ((typeof duration !== 'undefined' && duration !== 0) || (typeof duration === 'undefined' && animationOptions.duration !== 0))) { var animation = new Chart.Animation({ numSteps: (duration || animationOptions.duration) / 16.66, // 60 fps easing: config.easing || animationOptions.easing, render: function(chart, animationObject) { var easingFunction = helpers.easing.effects[animationObject.easing]; var currentStep = animationObject.currentStep; var stepDecimal = currentStep / animationObject.numSteps; chart.draw(easingFunction(stepDecimal), stepDecimal, currentStep); }, onAnimationProgress: animationOptions.onProgress, onAnimationComplete: onComplete }); Chart.animationService.addAnimation(me, animation, duration, lazy); } else { me.draw(); // See https://github.com/chartjs/Chart.js/issues/3781 onComplete(new Chart.Animation({numSteps: 0, chart: me})); } return me; }, draw: function(easingValue) { var me = this; me.clear(); if (helpers.isNullOrUndef(easingValue)) { easingValue = 1; } me.transition(easingValue); if (plugins.notify(me, 'beforeDraw', [easingValue]) === false) { return; } // Draw all the scales helpers.each(me.boxes, function(box) { box.draw(me.chartArea); }, me); if (me.scale) { me.scale.draw(); } me.drawDatasets(easingValue); me._drawTooltip(easingValue); plugins.notify(me, 'afterDraw', [easingValue]); }, /** * @private */ transition: function(easingValue) { var me = this; for (var i = 0, ilen = (me.data.datasets || []).length; i < ilen; ++i) { if (me.isDatasetVisible(i)) { me.getDatasetMeta(i).controller.transition(easingValue); } } me.tooltip.transition(easingValue); }, /** * Draws all datasets unless a plugin returns `false` to the `beforeDatasetsDraw` * hook, in which case, plugins will not be called on `afterDatasetsDraw`. * @private */ drawDatasets: function(easingValue) { var me = this; if (plugins.notify(me, 'beforeDatasetsDraw', [easingValue]) === false) { return; } // Draw datasets reversed to support proper line stacking for (var i = (me.data.datasets || []).length - 1; i >= 0; --i) { if (me.isDatasetVisible(i)) { me.drawDataset(i, easingValue); } } plugins.notify(me, 'afterDatasetsDraw', [easingValue]); }, /** * Draws dataset at index unless a plugin returns `false` to the `beforeDatasetDraw` * hook, in which case, plugins will not be called on `afterDatasetDraw`. * @private */ drawDataset: function(index, easingValue) { var me = this; var meta = me.getDatasetMeta(index); var args = { meta: meta, index: index, easingValue: easingValue }; if (plugins.notify(me, 'beforeDatasetDraw', [args]) === false) { return; } meta.controller.draw(easingValue); plugins.notify(me, 'afterDatasetDraw', [args]); }, /** * Draws tooltip unless a plugin returns `false` to the `beforeTooltipDraw` * hook, in which case, plugins will not be called on `afterTooltipDraw`. * @private */ _drawTooltip: function(easingValue) { var me = this; var tooltip = me.tooltip; var args = { tooltip: tooltip, easingValue: easingValue }; if (plugins.notify(me, 'beforeTooltipDraw', [args]) === false) { return; } tooltip.draw(); plugins.notify(me, 'afterTooltipDraw', [args]); }, // Get the single element that was clicked on // @return : An object containing the dataset index and element index of the matching element. Also contains the rectangle that was draw getElementAtEvent: function(e) { return Interaction.modes.single(this, e); }, getElementsAtEvent: function(e) { return Interaction.modes.label(this, e, {intersect: true}); }, getElementsAtXAxis: function(e) { return Interaction.modes['x-axis'](this, e, {intersect: true}); }, getElementsAtEventForMode: function(e, mode, options) { var method = Interaction.modes[mode]; if (typeof method === 'function') { return method(this, e, options); } return []; }, getDatasetAtEvent: function(e) { return Interaction.modes.dataset(this, e, {intersect: true}); }, getDatasetMeta: function(datasetIndex) { var me = this; var dataset = me.data.datasets[datasetIndex]; if (!dataset._meta) { dataset._meta = {}; } var meta = dataset._meta[me.id]; if (!meta) { meta = dataset._meta[me.id] = { type: null, data: [], dataset: null, controller: null, hidden: null, // See isDatasetVisible() comment xAxisID: null, yAxisID: null }; } return meta; }, getVisibleDatasetCount: function() { var count = 0; for (var i = 0, ilen = this.data.datasets.length; i < ilen; ++i) { if (this.isDatasetVisible(i)) { count++; } } return count; }, isDatasetVisible: function(datasetIndex) { var meta = this.getDatasetMeta(datasetIndex); // meta.hidden is a per chart dataset hidden flag override with 3 states: if true or false, // the dataset.hidden value is ignored, else if null, the dataset hidden state is returned. return typeof meta.hidden === 'boolean' ? !meta.hidden : !this.data.datasets[datasetIndex].hidden; }, generateLegend: function() { return this.options.legendCallback(this); }, /** * @private */ destroyDatasetMeta: function(datasetIndex) { var id = this.id; var dataset = this.data.datasets[datasetIndex]; var meta = dataset._meta && dataset._meta[id]; if (meta) { meta.controller.destroy(); delete dataset._meta[id]; } }, destroy: function() { var me = this; var canvas = me.canvas; var i, ilen; me.stop(); // dataset controllers need to cleanup associated data for (i = 0, ilen = me.data.datasets.length; i < ilen; ++i) { me.destroyDatasetMeta(i); } if (canvas) { me.unbindEvents(); helpers.canvas.clear(me); platform.releaseContext(me.ctx); me.canvas = null; me.ctx = null; } plugins.notify(me, 'destroy'); delete Chart.instances[me.id]; }, toBase64Image: function() { return this.canvas.toDataURL.apply(this.canvas, arguments); }, initToolTip: function() { var me = this; me.tooltip = new Chart.Tooltip({ _chart: me, _chartInstance: me, // deprecated, backward compatibility _data: me.data, _options: me.options.tooltips }, me); }, /** * @private */ bindEvents: function() { var me = this; var listeners = me._listeners = {}; var listener = function() { me.eventHandler.apply(me, arguments); }; helpers.each(me.options.events, function(type) { platform.addEventListener(me, type, listener); listeners[type] = listener; }); // Elements used to detect size change should not be injected for non responsive charts. // See https://github.com/chartjs/Chart.js/issues/2210 if (me.options.responsive) { listener = function() { me.resize(); }; platform.addEventListener(me, 'resize', listener); listeners.resize = listener; } }, /** * @private */ unbindEvents: function() { var me = this; var listeners = me._listeners; if (!listeners) { return; } delete me._listeners; helpers.each(listeners, function(listener, type) { platform.removeEventListener(me, type, listener); }); }, updateHoverStyle: function(elements, mode, enabled) { var method = enabled ? 'setHoverStyle' : 'removeHoverStyle'; var element, i, ilen; for (i = 0, ilen = elements.length; i < ilen; ++i) { element = elements[i]; if (element) { this.getDatasetMeta(element._datasetIndex).controller[method](element); } } }, /** * @private */ eventHandler: function(e) { var me = this; var tooltip = me.tooltip; if (plugins.notify(me, 'beforeEvent', [e]) === false) { return; } // Buffer any update calls so that renders do not occur me._bufferedRender = true; me._bufferedRequest = null; var changed = me.handleEvent(e); changed |= tooltip && tooltip.handleEvent(e); plugins.notify(me, 'afterEvent', [e]); var bufferedRequest = me._bufferedRequest; if (bufferedRequest) { // If we have an update that was triggered, we need to do a normal render me.render(bufferedRequest); } else if (changed && !me.animating) { // If entering, leaving, or changing elements, animate the change via pivot me.stop(); // We only need to render at this point. Updating will cause scales to be // recomputed generating flicker & using more memory than necessary. me.render(me.options.hover.animationDuration, true); } me._bufferedRender = false; me._bufferedRequest = null; return me; }, /** * Handle an event * @private * @param {IEvent} event the event to handle * @return {Boolean} true if the chart needs to re-render */ handleEvent: function(e) { var me = this; var options = me.options || {}; var hoverOptions = options.hover; var changed = false; me.lastActive = me.lastActive || []; // Find Active Elements for hover and tooltips if (e.type === 'mouseout') { me.active = []; } else { me.active = me.getElementsAtEventForMode(e, hoverOptions.mode, hoverOptions); } // Invoke onHover hook // Need to call with native event here to not break backwards compatibility helpers.callback(options.onHover || options.hover.onHover, [e.native, me.active], me); if (e.type === 'mouseup' || e.type === 'click') { if (options.onClick) { // Use e.native here for backwards compatibility options.onClick.call(me, e.native, me.active); } } // Remove styling for last active (even if it may still be active) if (me.lastActive.length) { me.updateHoverStyle(me.lastActive, hoverOptions.mode, false); } // Built in hover styling if (me.active.length && hoverOptions.mode) { me.updateHoverStyle(me.active, hoverOptions.mode, true); } changed = !helpers.arrayEquals(me.active, me.lastActive); // Remember Last Actives me.lastActive = me.active; return changed; } }); /** * Provided for backward compatibility, use Chart instead. * @class Chart.Controller * @deprecated since version 2.6.0 * @todo remove at version 3 * @private */ Chart.Controller = Chart; }; },{"25":25,"28":28,"45":45,"48":48}],24:[function(require,module,exports){ 'use strict'; var helpers = require(45); module.exports = function(Chart) { var arrayEvents = ['push', 'pop', 'shift', 'splice', 'unshift']; /** * Hooks the array methods that add or remove values ('push', pop', 'shift', 'splice', * 'unshift') and notify the listener AFTER the array has been altered. Listeners are * called on the 'onData*' callbacks (e.g. onDataPush, etc.) with same arguments. */ function listenArrayEvents(array, listener) { if (array._chartjs) { array._chartjs.listeners.push(listener); return; } Object.defineProperty(array, '_chartjs', { configurable: true, enumerable: false, value: { listeners: [listener] } }); arrayEvents.forEach(function(key) { var method = 'onData' + key.charAt(0).toUpperCase() + key.slice(1); var base = array[key]; Object.defineProperty(array, key, { configurable: true, enumerable: false, value: function() { var args = Array.prototype.slice.call(arguments); var res = base.apply(this, args); helpers.each(array._chartjs.listeners, function(object) { if (typeof object[method] === 'function') { object[method].apply(object, args); } }); return res; } }); }); } /** * Removes the given array event listener and cleanup extra attached properties (such as * the _chartjs stub and overridden methods) if array doesn't have any more listeners. */ function unlistenArrayEvents(array, listener) { var stub = array._chartjs; if (!stub) { return; } var listeners = stub.listeners; var index = listeners.indexOf(listener); if (index !== -1) { listeners.splice(index, 1); } if (listeners.length > 0) { return; } arrayEvents.forEach(function(key) { delete array[key]; }); delete array._chartjs; } // Base class for all dataset controllers (line, bar, etc) Chart.DatasetController = function(chart, datasetIndex) { this.initialize(chart, datasetIndex); }; helpers.extend(Chart.DatasetController.prototype, { /** * Element type used to generate a meta dataset (e.g. Chart.element.Line). * @type {Chart.core.element} */ datasetElementType: null, /** * Element type used to generate a meta data (e.g. Chart.element.Point). * @type {Chart.core.element} */ dataElementType: null, initialize: function(chart, datasetIndex) { var me = this; me.chart = chart; me.index = datasetIndex; me.linkScales(); me.addElements(); }, updateIndex: function(datasetIndex) { this.index = datasetIndex; }, linkScales: function() { var me = this; var meta = me.getMeta(); var dataset = me.getDataset(); if (meta.xAxisID === null) { meta.xAxisID = dataset.xAxisID || me.chart.options.scales.xAxes[0].id; } if (meta.yAxisID === null) { meta.yAxisID = dataset.yAxisID || me.chart.options.scales.yAxes[0].id; } }, getDataset: function() { return this.chart.data.datasets[this.index]; }, getMeta: function() { return this.chart.getDatasetMeta(this.index); }, getScaleForId: function(scaleID) { return this.chart.scales[scaleID]; }, reset: function() { this.update(true); }, /** * @private */ destroy: function() { if (this._data) { unlistenArrayEvents(this._data, this); } }, createMetaDataset: function() { var me = this; var type = me.datasetElementType; return type && new type({ _chart: me.chart, _datasetIndex: me.index }); }, createMetaData: function(index) { var me = this; var type = me.dataElementType; return type && new type({ _chart: me.chart, _datasetIndex: me.index, _index: index }); }, addElements: function() { var me = this; var meta = me.getMeta(); var data = me.getDataset().data || []; var metaData = meta.data; var i, ilen; for (i = 0, ilen = data.length; i < ilen; ++i) { metaData[i] = metaData[i] || me.createMetaData(i); } meta.dataset = meta.dataset || me.createMetaDataset(); }, addElementAndReset: function(index) { var element = this.createMetaData(index); this.getMeta().data.splice(index, 0, element); this.updateElement(element, index, true); }, buildOrUpdateElements: function() { var me = this; var dataset = me.getDataset(); var data = dataset.data || (dataset.data = []); // In order to correctly handle data addition/deletion animation (an thus simulate // real-time charts), we need to monitor these data modifications and synchronize // the internal meta data accordingly. if (me._data !== data) { if (me._data) { // This case happens when the user replaced the data array instance. unlistenArrayEvents(me._data, me); } listenArrayEvents(data, me); me._data = data; } // Re-sync meta data in case the user replaced the data array or if we missed // any updates and so make sure that we handle number of datapoints changing. me.resyncElements(); }, update: helpers.noop, transition: function(easingValue) { var meta = this.getMeta(); var elements = meta.data || []; var ilen = elements.length; var i = 0; for (; i < ilen; ++i) { elements[i].transition(easingValue); } if (meta.dataset) { meta.dataset.transition(easingValue); } }, draw: function() { var meta = this.getMeta(); var elements = meta.data || []; var ilen = elements.length; var i = 0; if (meta.dataset) { meta.dataset.draw(); } for (; i < ilen; ++i) { elements[i].draw(); } }, removeHoverStyle: function(element, elementOpts) { var dataset = this.chart.data.datasets[element._datasetIndex]; var index = element._index; var custom = element.custom || {}; var valueOrDefault = helpers.valueAtIndexOrDefault; var model = element._model; model.backgroundColor = custom.backgroundColor ? custom.backgroundColor : valueOrDefault(dataset.backgroundColor, index, elementOpts.backgroundColor); model.borderColor = custom.borderColor ? custom.borderColor : valueOrDefault(dataset.borderColor, index, elementOpts.borderColor); model.borderWidth = custom.borderWidth ? custom.borderWidth : valueOrDefault(dataset.borderWidth, index, elementOpts.borderWidth); }, setHoverStyle: function(element) { var dataset = this.chart.data.datasets[element._datasetIndex]; var index = element._index; var custom = element.custom || {}; var valueOrDefault = helpers.valueAtIndexOrDefault; var getHoverColor = helpers.getHoverColor; var model = element._model; model.backgroundColor = custom.hoverBackgroundColor ? custom.hoverBackgroundColor : valueOrDefault(dataset.hoverBackgroundColor, index, getHoverColor(model.backgroundColor)); model.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : valueOrDefault(dataset.hoverBorderColor, index, getHoverColor(model.borderColor)); model.borderWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : valueOrDefault(dataset.hoverBorderWidth, index, model.borderWidth); }, /** * @private */ resyncElements: function() { var me = this; var meta = me.getMeta(); var data = me.getDataset().data; var numMeta = meta.data.length; var numData = data.length; if (numData < numMeta) { meta.data.splice(numData, numMeta - numData); } else if (numData > numMeta) { me.insertElements(numMeta, numData - numMeta); } }, /** * @private */ insertElements: function(start, count) { for (var i = 0; i < count; ++i) { this.addElementAndReset(start + i); } }, /** * @private */ onDataPush: function() { this.insertElements(this.getDataset().data.length - 1, arguments.length); }, /** * @private */ onDataPop: function() { this.getMeta().data.pop(); }, /** * @private */ onDataShift: function() { this.getMeta().data.shift(); }, /** * @private */ onDataSplice: function(start, count) { this.getMeta().data.splice(start, count); this.insertElements(start, arguments.length - 2); }, /** * @private */ onDataUnshift: function() { this.insertElements(0, arguments.length); } }); Chart.DatasetController.extend = helpers.inherits; }; },{"45":45}],25:[function(require,module,exports){ 'use strict'; var helpers = require(45); module.exports = { /** * @private */ _set: function(scope, values) { return helpers.merge(this[scope] || (this[scope] = {}), values); } }; },{"45":45}],26:[function(require,module,exports){ 'use strict'; var color = require(2); var helpers = require(45); function interpolate(start, view, model, ease) { var keys = Object.keys(model); var i, ilen, key, actual, origin, target, type, c0, c1; for (i = 0, ilen = keys.length; i < ilen; ++i) { key = keys[i]; target = model[key]; // if a value is added to the model after pivot() has been called, the view // doesn't contain it, so let's initialize the view to the target value. if (!view.hasOwnProperty(key)) { view[key] = target; } actual = view[key]; if (actual === target || key[0] === '_') { continue; } if (!start.hasOwnProperty(key)) { start[key] = actual; } origin = start[key]; type = typeof target; if (type === typeof origin) { if (type === 'string') { c0 = color(origin); if (c0.valid) { c1 = color(target); if (c1.valid) { view[key] = c1.mix(c0, ease).rgbString(); continue; } } } else if (type === 'number' && isFinite(origin) && isFinite(target)) { view[key] = origin + (target - origin) * ease; continue; } } view[key] = target; } } var Element = function(configuration) { helpers.extend(this, configuration); this.initialize.apply(this, arguments); }; helpers.extend(Element.prototype, { initialize: function() { this.hidden = false; }, pivot: function() { var me = this; if (!me._view) { me._view = helpers.clone(me._model); } me._start = {}; return me; }, transition: function(ease) { var me = this; var model = me._model; var start = me._start; var view = me._view; // No animation -> No Transition if (!model || ease === 1) { me._view = model; me._start = null; return me; } if (!view) { view = me._view = {}; } if (!start) { start = me._start = {}; } interpolate(start, view, model, ease); return me; }, tooltipPosition: function() { return { x: this._model.x, y: this._model.y }; }, hasValue: function() { return helpers.isNumber(this._model.x) && helpers.isNumber(this._model.y); } }); Element.extend = helpers.inherits; module.exports = Element; },{"2":2,"45":45}],27:[function(require,module,exports){ /* global window: false */ /* global document: false */ 'use strict'; var color = require(2); var defaults = require(25); var helpers = require(45); module.exports = function(Chart) { // -- Basic js utility methods helpers.configMerge = function(/* objects ... */) { return helpers.merge(helpers.clone(arguments[0]), [].slice.call(arguments, 1), { merger: function(key, target, source, options) { var tval = target[key] || {}; var sval = source[key]; if (key === 'scales') { // scale config merging is complex. Add our own function here for that target[key] = helpers.scaleMerge(tval, sval); } else if (key === 'scale') { // used in polar area & radar charts since there is only one scale target[key] = helpers.merge(tval, [Chart.scaleService.getScaleDefaults(sval.type), sval]); } else { helpers._merger(key, target, source, options); } } }); }; helpers.scaleMerge = function(/* objects ... */) { return helpers.merge(helpers.clone(arguments[0]), [].slice.call(arguments, 1), { merger: function(key, target, source, options) { if (key === 'xAxes' || key === 'yAxes') { var slen = source[key].length; var i, type, scale; if (!target[key]) { target[key] = []; } for (i = 0; i < slen; ++i) { scale = source[key][i]; type = helpers.valueOrDefault(scale.type, key === 'xAxes' ? 'category' : 'linear'); if (i >= target[key].length) { target[key].push({}); } if (!target[key][i].type || (scale.type && scale.type !== target[key][i].type)) { // new/untyped scale or type changed: let's apply the new defaults // then merge source scale to correctly overwrite the defaults. helpers.merge(target[key][i], [Chart.scaleService.getScaleDefaults(type), scale]); } else { // scales type are the same helpers.merge(target[key][i], scale); } } } else { helpers._merger(key, target, source, options); } } }); }; helpers.where = function(collection, filterCallback) { if (helpers.isArray(collection) && Array.prototype.filter) { return collection.filter(filterCallback); } var filtered = []; helpers.each(collection, function(item) { if (filterCallback(item)) { filtered.push(item); } }); return filtered; }; helpers.findIndex = Array.prototype.findIndex ? function(array, callback, scope) { return array.findIndex(callback, scope); } : function(array, callback, scope) { scope = scope === undefined ? array : scope; for (var i = 0, ilen = array.length; i < ilen; ++i) { if (callback.call(scope, array[i], i, array)) { return i; } } return -1; }; helpers.findNextWhere = function(arrayToSearch, filterCallback, startIndex) { // Default to start of the array if (helpers.isNullOrUndef(startIndex)) { startIndex = -1; } for (var i = startIndex + 1; i < arrayToSearch.length; i++) { var currentItem = arrayToSearch[i]; if (filterCallback(currentItem)) { return currentItem; } } }; helpers.findPreviousWhere = function(arrayToSearch, filterCallback, startIndex) { // Default to end of the array if (helpers.isNullOrUndef(startIndex)) { startIndex = arrayToSearch.length; } for (var i = startIndex - 1; i >= 0; i--) { var currentItem = arrayToSearch[i]; if (filterCallback(currentItem)) { return currentItem; } } }; // -- Math methods helpers.isNumber = function(n) { return !isNaN(parseFloat(n)) && isFinite(n); }; helpers.almostEquals = function(x, y, epsilon) { return Math.abs(x - y) < epsilon; }; helpers.almostWhole = function(x, epsilon) { var rounded = Math.round(x); return (((rounded - epsilon) < x) && ((rounded + epsilon) > x)); }; helpers.max = function(array) { return array.reduce(function(max, value) { if (!isNaN(value)) { return Math.max(max, value); } return max; }, Number.NEGATIVE_INFINITY); }; helpers.min = function(array) { return array.reduce(function(min, value) { if (!isNaN(value)) { return Math.min(min, value); } return min; }, Number.POSITIVE_INFINITY); }; helpers.sign = Math.sign ? function(x) { return Math.sign(x); } : function(x) { x = +x; // convert to a number if (x === 0 || isNaN(x)) { return x; } return x > 0 ? 1 : -1; }; helpers.log10 = Math.log10 ? function(x) { return Math.log10(x); } : function(x) { return Math.log(x) / Math.LN10; }; helpers.toRadians = function(degrees) { return degrees * (Math.PI / 180); }; helpers.toDegrees = function(radians) { return radians * (180 / Math.PI); }; // Gets the angle from vertical upright to the point about a centre. helpers.getAngleFromPoint = function(centrePoint, anglePoint) { var distanceFromXCenter = anglePoint.x - centrePoint.x; var distanceFromYCenter = anglePoint.y - centrePoint.y; var radialDistanceFromCenter = Math.sqrt(distanceFromXCenter * distanceFromXCenter + distanceFromYCenter * distanceFromYCenter); var angle = Math.atan2(distanceFromYCenter, distanceFromXCenter); if (angle < (-0.5 * Math.PI)) { angle += 2.0 * Math.PI; // make sure the returned angle is in the range of (-PI/2, 3PI/2] } return { angle: angle, distance: radialDistanceFromCenter }; }; helpers.distanceBetweenPoints = function(pt1, pt2) { return Math.sqrt(Math.pow(pt2.x - pt1.x, 2) + Math.pow(pt2.y - pt1.y, 2)); }; helpers.aliasPixel = function(pixelWidth) { return (pixelWidth % 2 === 0) ? 0 : 0.5; }; helpers.splineCurve = function(firstPoint, middlePoint, afterPoint, t) { // Props to Rob Spencer at scaled innovation for his post on splining between points // http://scaledinnovation.com/analytics/splines/aboutSplines.html // This function must also respect "skipped" points var previous = firstPoint.skip ? middlePoint : firstPoint; var current = middlePoint; var next = afterPoint.skip ? middlePoint : afterPoint; var d01 = Math.sqrt(Math.pow(current.x - previous.x, 2) + Math.pow(current.y - previous.y, 2)); var d12 = Math.sqrt(Math.pow(next.x - current.x, 2) + Math.pow(next.y - current.y, 2)); var s01 = d01 / (d01 + d12); var s12 = d12 / (d01 + d12); // If all points are the same, s01 & s02 will be inf s01 = isNaN(s01) ? 0 : s01; s12 = isNaN(s12) ? 0 : s12; var fa = t * s01; // scaling factor for triangle Ta var fb = t * s12; return { previous: { x: current.x - fa * (next.x - previous.x), y: current.y - fa * (next.y - previous.y) }, next: { x: current.x + fb * (next.x - previous.x), y: current.y + fb * (next.y - previous.y) } }; }; helpers.EPSILON = Number.EPSILON || 1e-14; helpers.splineCurveMonotone = function(points) { // This function calculates Bézier control points in a similar way than |splineCurve|, // but preserves monotonicity of the provided data and ensures no local extremums are added // between the dataset discrete points due to the interpolation. // See : https://en.wikipedia.org/wiki/Monotone_cubic_interpolation var pointsWithTangents = (points || []).map(function(point) { return { model: point._model, deltaK: 0, mK: 0 }; }); // Calculate slopes (deltaK) and initialize tangents (mK) var pointsLen = pointsWithTangents.length; var i, pointBefore, pointCurrent, pointAfter; for (i = 0; i < pointsLen; ++i) { pointCurrent = pointsWithTangents[i]; if (pointCurrent.model.skip) { continue; } pointBefore = i > 0 ? pointsWithTangents[i - 1] : null; pointAfter = i < pointsLen - 1 ? pointsWithTangents[i + 1] : null; if (pointAfter && !pointAfter.model.skip) { var slopeDeltaX = (pointAfter.model.x - pointCurrent.model.x); // In the case of two points that appear at the same x pixel, slopeDeltaX is 0 pointCurrent.deltaK = slopeDeltaX !== 0 ? (pointAfter.model.y - pointCurrent.model.y) / slopeDeltaX : 0; } if (!pointBefore || pointBefore.model.skip) { pointCurrent.mK = pointCurrent.deltaK; } else if (!pointAfter || pointAfter.model.skip) { pointCurrent.mK = pointBefore.deltaK; } else if (this.sign(pointBefore.deltaK) !== this.sign(pointCurrent.deltaK)) { pointCurrent.mK = 0; } else { pointCurrent.mK = (pointBefore.deltaK + pointCurrent.deltaK) / 2; } } // Adjust tangents to ensure monotonic properties var alphaK, betaK, tauK, squaredMagnitude; for (i = 0; i < pointsLen - 1; ++i) { pointCurrent = pointsWithTangents[i]; pointAfter = pointsWithTangents[i + 1]; if (pointCurrent.model.skip || pointAfter.model.skip) { continue; } if (helpers.almostEquals(pointCurrent.deltaK, 0, this.EPSILON)) { pointCurrent.mK = pointAfter.mK = 0; continue; } alphaK = pointCurrent.mK / pointCurrent.deltaK; betaK = pointAfter.mK / pointCurrent.deltaK; squaredMagnitude = Math.pow(alphaK, 2) + Math.pow(betaK, 2); if (squaredMagnitude <= 9) { continue; } tauK = 3 / Math.sqrt(squaredMagnitude); pointCurrent.mK = alphaK * tauK * pointCurrent.deltaK; pointAfter.mK = betaK * tauK * pointCurrent.deltaK; } // Compute control points var deltaX; for (i = 0; i < pointsLen; ++i) { pointCurrent = pointsWithTangents[i]; if (pointCurrent.model.skip) { continue; } pointBefore = i > 0 ? pointsWithTangents[i - 1] : null; pointAfter = i < pointsLen - 1 ? pointsWithTangents[i + 1] : null; if (pointBefore && !pointBefore.model.skip) { deltaX = (pointCurrent.model.x - pointBefore.model.x) / 3; pointCurrent.model.controlPointPreviousX = pointCurrent.model.x - deltaX; pointCurrent.model.controlPointPreviousY = pointCurrent.model.y - deltaX * pointCurrent.mK; } if (pointAfter && !pointAfter.model.skip) { deltaX = (pointAfter.model.x - pointCurrent.model.x) / 3; pointCurrent.model.controlPointNextX = pointCurrent.model.x + deltaX; pointCurrent.model.controlPointNextY = pointCurrent.model.y + deltaX * pointCurrent.mK; } } }; helpers.nextItem = function(collection, index, loop) { if (loop) { return index >= collection.length - 1 ? collection[0] : collection[index + 1]; } return index >= collection.length - 1 ? collection[collection.length - 1] : collection[index + 1]; }; helpers.previousItem = function(collection, index, loop) { if (loop) { return index <= 0 ? collection[collection.length - 1] : collection[index - 1]; } return index <= 0 ? collection[0] : collection[index - 1]; }; // Implementation of the nice number algorithm used in determining where axis labels will go helpers.niceNum = function(range, round) { var exponent = Math.floor(helpers.log10(range)); var fraction = range / Math.pow(10, exponent); var niceFraction; if (round) { if (fraction < 1.5) { niceFraction = 1; } else if (fraction < 3) { niceFraction = 2; } else if (fraction < 7) { niceFraction = 5; } else { niceFraction = 10; } } else if (fraction <= 1.0) { niceFraction = 1; } else if (fraction <= 2) { niceFraction = 2; } else if (fraction <= 5) { niceFraction = 5; } else { niceFraction = 10; } return niceFraction * Math.pow(10, exponent); }; // Request animation polyfill - http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/ helpers.requestAnimFrame = (function() { if (typeof window === 'undefined') { return function(callback) { callback(); }; } return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function(callback) { return window.setTimeout(callback, 1000 / 60); }; }()); // -- DOM methods helpers.getRelativePosition = function(evt, chart) { var mouseX, mouseY; var e = evt.originalEvent || evt; var canvas = evt.currentTarget || evt.srcElement; var boundingRect = canvas.getBoundingClientRect(); var touches = e.touches; if (touches && touches.length > 0) { mouseX = touches[0].clientX; mouseY = touches[0].clientY; } else { mouseX = e.clientX; mouseY = e.clientY; } // Scale mouse coordinates into canvas coordinates // by following the pattern laid out by 'jerryj' in the comments of // http://www.html5canvastutorials.com/advanced/html5-canvas-mouse-coordinates/ var paddingLeft = parseFloat(helpers.getStyle(canvas, 'padding-left')); var paddingTop = parseFloat(helpers.getStyle(canvas, 'padding-top')); var paddingRight = parseFloat(helpers.getStyle(canvas, 'padding-right')); var paddingBottom = parseFloat(helpers.getStyle(canvas, 'padding-bottom')); var width = boundingRect.right - boundingRect.left - paddingLeft - paddingRight; var height = boundingRect.bottom - boundingRect.top - paddingTop - paddingBottom; // We divide by the current device pixel ratio, because the canvas is scaled up by that amount in each direction. However // the backend model is in unscaled coordinates. Since we are going to deal with our model coordinates, we go back here mouseX = Math.round((mouseX - boundingRect.left - paddingLeft) / (width) * canvas.width / chart.currentDevicePixelRatio); mouseY = Math.round((mouseY - boundingRect.top - paddingTop) / (height) * canvas.height / chart.currentDevicePixelRatio); return { x: mouseX, y: mouseY }; }; // Private helper function to convert max-width/max-height values that may be percentages into a number function parseMaxStyle(styleValue, node, parentProperty) { var valueInPixels; if (typeof styleValue === 'string') { valueInPixels = parseInt(styleValue, 10); if (styleValue.indexOf('%') !== -1) { // percentage * size in dimension valueInPixels = valueInPixels / 100 * node.parentNode[parentProperty]; } } else { valueInPixels = styleValue; } return valueInPixels; } /** * Returns if the given value contains an effective constraint. * @private */ function isConstrainedValue(value) { return value !== undefined && value !== null && value !== 'none'; } // Private helper to get a constraint dimension // @param domNode : the node to check the constraint on // @param maxStyle : the style that defines the maximum for the direction we are using (maxWidth / maxHeight) // @param percentageProperty : property of parent to use when calculating width as a percentage // @see http://www.nathanaeljones.com/blog/2013/reading-max-width-cross-browser function getConstraintDimension(domNode, maxStyle, percentageProperty) { var view = document.defaultView; var parentNode = domNode.parentNode; var constrainedNode = view.getComputedStyle(domNode)[maxStyle]; var constrainedContainer = view.getComputedStyle(parentNode)[maxStyle]; var hasCNode = isConstrainedValue(constrainedNode); var hasCContainer = isConstrainedValue(constrainedContainer); var infinity = Number.POSITIVE_INFINITY; if (hasCNode || hasCContainer) { return Math.min( hasCNode ? parseMaxStyle(constrainedNode, domNode, percentageProperty) : infinity, hasCContainer ? parseMaxStyle(constrainedContainer, parentNode, percentageProperty) : infinity); } return 'none'; } // returns Number or undefined if no constraint helpers.getConstraintWidth = function(domNode) { return getConstraintDimension(domNode, 'max-width', 'clientWidth'); }; // returns Number or undefined if no constraint helpers.getConstraintHeight = function(domNode) { return getConstraintDimension(domNode, 'max-height', 'clientHeight'); }; helpers.getMaximumWidth = function(domNode) { var container = domNode.parentNode; if (!container) { return domNode.clientWidth; } var paddingLeft = parseInt(helpers.getStyle(container, 'padding-left'), 10); var paddingRight = parseInt(helpers.getStyle(container, 'padding-right'), 10); var w = container.clientWidth - paddingLeft - paddingRight; var cw = helpers.getConstraintWidth(domNode); return isNaN(cw) ? w : Math.min(w, cw); }; helpers.getMaximumHeight = function(domNode) { var container = domNode.parentNode; if (!container) { return domNode.clientHeight; } var paddingTop = parseInt(helpers.getStyle(container, 'padding-top'), 10); var paddingBottom = parseInt(helpers.getStyle(container, 'padding-bottom'), 10); var h = container.clientHeight - paddingTop - paddingBottom; var ch = helpers.getConstraintHeight(domNode); return isNaN(ch) ? h : Math.min(h, ch); }; helpers.getStyle = function(el, property) { return el.currentStyle ? el.currentStyle[property] : document.defaultView.getComputedStyle(el, null).getPropertyValue(property); }; helpers.retinaScale = function(chart, forceRatio) { var pixelRatio = chart.currentDevicePixelRatio = forceRatio || window.devicePixelRatio || 1; if (pixelRatio === 1) { return; } var canvas = chart.canvas; var height = chart.height; var width = chart.width; canvas.height = height * pixelRatio; canvas.width = width * pixelRatio; chart.ctx.scale(pixelRatio, pixelRatio); // If no style has been set on the canvas, the render size is used as display size, // making the chart visually bigger, so let's enforce it to the "correct" values. // See https://github.com/chartjs/Chart.js/issues/3575 canvas.style.height = height + 'px'; canvas.style.width = width + 'px'; }; // -- Canvas methods helpers.fontString = function(pixelSize, fontStyle, fontFamily) { return fontStyle + ' ' + pixelSize + 'px ' + fontFamily; }; helpers.longestText = function(ctx, font, arrayOfThings, cache) { cache = cache || {}; var data = cache.data = cache.data || {}; var gc = cache.garbageCollect = cache.garbageCollect || []; if (cache.font !== font) { data = cache.data = {}; gc = cache.garbageCollect = []; cache.font = font; } ctx.font = font; var longest = 0; helpers.each(arrayOfThings, function(thing) { // Undefined strings and arrays should not be measured if (thing !== undefined && thing !== null && helpers.isArray(thing) !== true) { longest = helpers.measureText(ctx, data, gc, longest, thing); } else if (helpers.isArray(thing)) { // if it is an array lets measure each element // to do maybe simplify this function a bit so we can do this more recursively? helpers.each(thing, function(nestedThing) { // Undefined strings and arrays should not be measured if (nestedThing !== undefined && nestedThing !== null && !helpers.isArray(nestedThing)) { longest = helpers.measureText(ctx, data, gc, longest, nestedThing); } }); } }); var gcLen = gc.length / 2; if (gcLen > arrayOfThings.length) { for (var i = 0; i < gcLen; i++) { delete data[gc[i]]; } gc.splice(0, gcLen); } return longest; }; helpers.measureText = function(ctx, data, gc, longest, string) { var textWidth = data[string]; if (!textWidth) { textWidth = data[string] = ctx.measureText(string).width; gc.push(string); } if (textWidth > longest) { longest = textWidth; } return longest; }; helpers.numberOfLabelLines = function(arrayOfThings) { var numberOfLines = 1; helpers.each(arrayOfThings, function(thing) { if (helpers.isArray(thing)) { if (thing.length > numberOfLines) { numberOfLines = thing.length; } } }); return numberOfLines; }; helpers.color = !color ? function(value) { console.error('Color.js not found!'); return value; } : function(value) { /* global CanvasGradient */ if (value instanceof CanvasGradient) { value = defaults.global.defaultColor; } return color(value); }; helpers.getHoverColor = function(colorValue) { /* global CanvasPattern */ return (colorValue instanceof CanvasPattern) ? colorValue : helpers.color(colorValue).saturate(0.5).darken(0.1).rgbString(); }; }; },{"2":2,"25":25,"45":45}],28:[function(require,module,exports){ 'use strict'; var helpers = require(45); /** * Helper function to get relative position for an event * @param {Event|IEvent} event - The event to get the position for * @param {Chart} chart - The chart * @returns {Point} the event position */ function getRelativePosition(e, chart) { if (e.native) { return { x: e.x, y: e.y }; } return helpers.getRelativePosition(e, chart); } /** * Helper function to traverse all of the visible elements in the chart * @param chart {chart} the chart * @param handler {Function} the callback to execute for each visible item */ function parseVisibleItems(chart, handler) { var datasets = chart.data.datasets; var meta, i, j, ilen, jlen; for (i = 0, ilen = datasets.length; i < ilen; ++i) { if (!chart.isDatasetVisible(i)) { continue; } meta = chart.getDatasetMeta(i); for (j = 0, jlen = meta.data.length; j < jlen; ++j) { var element = meta.data[j]; if (!element._view.skip) { handler(element); } } } } /** * Helper function to get the items that intersect the event position * @param items {ChartElement[]} elements to filter * @param position {Point} the point to be nearest to * @return {ChartElement[]} the nearest items */ function getIntersectItems(chart, position) { var elements = []; parseVisibleItems(chart, function(element) { if (element.inRange(position.x, position.y)) { elements.push(element); } }); return elements; } /** * Helper function to get the items nearest to the event position considering all visible items in teh chart * @param chart {Chart} the chart to look at elements from * @param position {Point} the point to be nearest to * @param intersect {Boolean} if true, only consider items that intersect the position * @param distanceMetric {Function} function to provide the distance between points * @return {ChartElement[]} the nearest items */ function getNearestItems(chart, position, intersect, distanceMetric) { var minDistance = Number.POSITIVE_INFINITY; var nearestItems = []; parseVisibleItems(chart, function(element) { if (intersect && !element.inRange(position.x, position.y)) { return; } var center = element.getCenterPoint(); var distance = distanceMetric(position, center); if (distance < minDistance) { nearestItems = [element]; minDistance = distance; } else if (distance === minDistance) { // Can have multiple items at the same distance in which case we sort by size nearestItems.push(element); } }); return nearestItems; } /** * Get a distance metric function for two points based on the * axis mode setting * @param {String} axis the axis mode. x|y|xy */ function getDistanceMetricForAxis(axis) { var useX = axis.indexOf('x') !== -1; var useY = axis.indexOf('y') !== -1; return function(pt1, pt2) { var deltaX = useX ? Math.abs(pt1.x - pt2.x) : 0; var deltaY = useY ? Math.abs(pt1.y - pt2.y) : 0; return Math.sqrt(Math.pow(deltaX, 2) + Math.pow(deltaY, 2)); }; } function indexMode(chart, e, options) { var position = getRelativePosition(e, chart); // Default axis for index mode is 'x' to match old behaviour options.axis = options.axis || 'x'; var distanceMetric = getDistanceMetricForAxis(options.axis); var items = options.intersect ? getIntersectItems(chart, position) : getNearestItems(chart, position, false, distanceMetric); var elements = []; if (!items.length) { return []; } chart.data.datasets.forEach(function(dataset, datasetIndex) { if (chart.isDatasetVisible(datasetIndex)) { var meta = chart.getDatasetMeta(datasetIndex); var element = meta.data[items[0]._index]; // don't count items that are skipped (null data) if (element && !element._view.skip) { elements.push(element); } } }); return elements; } /** * @interface IInteractionOptions */ /** * If true, only consider items that intersect the point * @name IInterfaceOptions#boolean * @type Boolean */ /** * Contains interaction related functions * @namespace Chart.Interaction */ module.exports = { // Helper function for different modes modes: { single: function(chart, e) { var position = getRelativePosition(e, chart); var elements = []; parseVisibleItems(chart, function(element) { if (element.inRange(position.x, position.y)) { elements.push(element); return elements; } }); return elements.slice(0, 1); }, /** * @function Chart.Interaction.modes.label * @deprecated since version 2.4.0 * @todo remove at version 3 * @private */ label: indexMode, /** * Returns items at the same index. If the options.intersect parameter is true, we only return items if we intersect something * If the options.intersect mode is false, we find the nearest item and return the items at the same index as that item * @function Chart.Interaction.modes.index * @since v2.4.0 * @param chart {chart} the chart we are returning items from * @param e {Event} the event we are find things at * @param options {IInteractionOptions} options to use during interaction * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned */ index: indexMode, /** * Returns items in the same dataset. If the options.intersect parameter is true, we only return items if we intersect something * If the options.intersect is false, we find the nearest item and return the items in that dataset * @function Chart.Interaction.modes.dataset * @param chart {chart} the chart we are returning items from * @param e {Event} the event we are find things at * @param options {IInteractionOptions} options to use during interaction * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned */ dataset: function(chart, e, options) { var position = getRelativePosition(e, chart); options.axis = options.axis || 'xy'; var distanceMetric = getDistanceMetricForAxis(options.axis); var items = options.intersect ? getIntersectItems(chart, position) : getNearestItems(chart, position, false, distanceMetric); if (items.length > 0) { items = chart.getDatasetMeta(items[0]._datasetIndex).data; } return items; }, /** * @function Chart.Interaction.modes.x-axis * @deprecated since version 2.4.0. Use index mode and intersect == true * @todo remove at version 3 * @private */ 'x-axis': function(chart, e) { return indexMode(chart, e, {intersect: false}); }, /** * Point mode returns all elements that hit test based on the event position * of the event * @function Chart.Interaction.modes.intersect * @param chart {chart} the chart we are returning items from * @param e {Event} the event we are find things at * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned */ point: function(chart, e) { var position = getRelativePosition(e, chart); return getIntersectItems(chart, position); }, /** * nearest mode returns the element closest to the point * @function Chart.Interaction.modes.intersect * @param chart {chart} the chart we are returning items from * @param e {Event} the event we are find things at * @param options {IInteractionOptions} options to use * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned */ nearest: function(chart, e, options) { var position = getRelativePosition(e, chart); options.axis = options.axis || 'xy'; var distanceMetric = getDistanceMetricForAxis(options.axis); var nearestItems = getNearestItems(chart, position, options.intersect, distanceMetric); // We have multiple items at the same distance from the event. Now sort by smallest if (nearestItems.length > 1) { nearestItems.sort(function(a, b) { var sizeA = a.getArea(); var sizeB = b.getArea(); var ret = sizeA - sizeB; if (ret === 0) { // if equal sort by dataset index ret = a._datasetIndex - b._datasetIndex; } return ret; }); } // Return only 1 item return nearestItems.slice(0, 1); }, /** * x mode returns the elements that hit-test at the current x coordinate * @function Chart.Interaction.modes.x * @param chart {chart} the chart we are returning items from * @param e {Event} the event we are find things at * @param options {IInteractionOptions} options to use * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned */ x: function(chart, e, options) { var position = getRelativePosition(e, chart); var items = []; var intersectsItem = false; parseVisibleItems(chart, function(element) { if (element.inXRange(position.x)) { items.push(element); } if (element.inRange(position.x, position.y)) { intersectsItem = true; } }); // If we want to trigger on an intersect and we don't have any items // that intersect the position, return nothing if (options.intersect && !intersectsItem) { items = []; } return items; }, /** * y mode returns the elements that hit-test at the current y coordinate * @function Chart.Interaction.modes.y * @param chart {chart} the chart we are returning items from * @param e {Event} the event we are find things at * @param options {IInteractionOptions} options to use * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned */ y: function(chart, e, options) { var position = getRelativePosition(e, chart); var items = []; var intersectsItem = false; parseVisibleItems(chart, function(element) { if (element.inYRange(position.y)) { items.push(element); } if (element.inRange(position.x, position.y)) { intersectsItem = true; } }); // If we want to trigger on an intersect and we don't have any items // that intersect the position, return nothing if (options.intersect && !intersectsItem) { items = []; } return items; } } }; },{"45":45}],29:[function(require,module,exports){ 'use strict'; var defaults = require(25); defaults._set('global', { responsive: true, responsiveAnimationDuration: 0, maintainAspectRatio: true, events: ['mousemove', 'mouseout', 'click', 'touchstart', 'touchmove'], hover: { onHover: null, mode: 'nearest', intersect: true, animationDuration: 400 }, onClick: null, defaultColor: 'rgba(0,0,0,0.1)', defaultFontColor: '#666', defaultFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif", defaultFontSize: 12, defaultFontStyle: 'normal', showLines: true, // Element defaults defined in element extensions elements: {}, // Layout options such as padding layout: { padding: { top: 0, right: 0, bottom: 0, left: 0 } } }); module.exports = function() { // Occupy the global variable of Chart, and create a simple base class var Chart = function(item, config) { this.construct(item, config); return this; }; Chart.Chart = Chart; return Chart; }; },{"25":25}],30:[function(require,module,exports){ 'use strict'; var helpers = require(45); module.exports = function(Chart) { function filterByPosition(array, position) { return helpers.where(array, function(v) { return v.position === position; }); } function sortByWeight(array, reverse) { array.forEach(function(v, i) { v._tmpIndex_ = i; return v; }); array.sort(function(a, b) { var v0 = reverse ? b : a; var v1 = reverse ? a : b; return v0.weight === v1.weight ? v0._tmpIndex_ - v1._tmpIndex_ : v0.weight - v1.weight; }); array.forEach(function(v) { delete v._tmpIndex_; }); } /** * @interface ILayoutItem * @prop {String} position - The position of the item in the chart layout. Possible values are * 'left', 'top', 'right', 'bottom', and 'chartArea' * @prop {Number} weight - The weight used to sort the item. Higher weights are further away from the chart area * @prop {Boolean} fullWidth - if true, and the item is horizontal, then push vertical boxes down * @prop {Function} isHorizontal - returns true if the layout item is horizontal (ie. top or bottom) * @prop {Function} update - Takes two parameters: width and height. Returns size of item * @prop {Function} getPadding - Returns an object with padding on the edges * @prop {Number} width - Width of item. Must be valid after update() * @prop {Number} height - Height of item. Must be valid after update() * @prop {Number} left - Left edge of the item. Set by layout system and cannot be used in update * @prop {Number} top - Top edge of the item. Set by layout system and cannot be used in update * @prop {Number} right - Right edge of the item. Set by layout system and cannot be used in update * @prop {Number} bottom - Bottom edge of the item. Set by layout system and cannot be used in update */ // The layout service is very self explanatory. It's responsible for the layout within a chart. // Scales, Legends and Plugins all rely on the layout service and can easily register to be placed anywhere they need // It is this service's responsibility of carrying out that layout. Chart.layoutService = { defaults: {}, /** * Register a box to a chart. * A box is simply a reference to an object that requires layout. eg. Scales, Legend, Title. * @param {Chart} chart - the chart to use * @param {ILayoutItem} item - the item to add to be layed out */ addBox: function(chart, item) { if (!chart.boxes) { chart.boxes = []; } // initialize item with default values item.fullWidth = item.fullWidth || false; item.position = item.position || 'top'; item.weight = item.weight || 0; chart.boxes.push(item); }, /** * Remove a layoutItem from a chart * @param {Chart} chart - the chart to remove the box from * @param {Object} layoutItem - the item to remove from the layout */ removeBox: function(chart, layoutItem) { var index = chart.boxes ? chart.boxes.indexOf(layoutItem) : -1; if (index !== -1) { chart.boxes.splice(index, 1); } }, /** * Sets (or updates) options on the given `item`. * @param {Chart} chart - the chart in which the item lives (or will be added to) * @param {Object} item - the item to configure with the given options * @param {Object} options - the new item options. */ configure: function(chart, item, options) { var props = ['fullWidth', 'position', 'weight']; var ilen = props.length; var i = 0; var prop; for (; i < ilen; ++i) { prop = props[i]; if (options.hasOwnProperty(prop)) { item[prop] = options[prop]; } } }, /** * Fits boxes of the given chart into the given size by having each box measure itself * then running a fitting algorithm * @param {Chart} chart - the chart * @param {Number} width - the width to fit into * @param {Number} height - the height to fit into */ update: function(chart, width, height) { if (!chart) { return; } var layoutOptions = chart.options.layout || {}; var padding = helpers.options.toPadding(layoutOptions.padding); var leftPadding = padding.left; var rightPadding = padding.right; var topPadding = padding.top; var bottomPadding = padding.bottom; var leftBoxes = filterByPosition(chart.boxes, 'left'); var rightBoxes = filterByPosition(chart.boxes, 'right'); var topBoxes = filterByPosition(chart.boxes, 'top'); var bottomBoxes = filterByPosition(chart.boxes, 'bottom'); var chartAreaBoxes = filterByPosition(chart.boxes, 'chartArea'); // Sort boxes by weight. A higher weight is further away from the chart area sortByWeight(leftBoxes, true); sortByWeight(rightBoxes, false); sortByWeight(topBoxes, true); sortByWeight(bottomBoxes, false); // Essentially we now have any number of boxes on each of the 4 sides. // Our canvas looks like the following. // The areas L1 and L2 are the left axes. R1 is the right axis, T1 is the top axis and // B1 is the bottom axis // There are also 4 quadrant-like locations (left to right instead of clockwise) reserved for chart overlays // These locations are single-box locations only, when trying to register a chartArea location that is already taken, // an error will be thrown. // // |----------------------------------------------------| // | T1 (Full Width) | // |----------------------------------------------------| // | | | T2 | | // | |----|-------------------------------------|----| // | | | C1 | | C2 | | // | | |----| |----| | // | | | | | // | L1 | L2 | ChartArea (C0) | R1 | // | | | | | // | | |----| |----| | // | | | C3 | | C4 | | // | |----|-------------------------------------|----| // | | | B1 | | // |----------------------------------------------------| // | B2 (Full Width) | // |----------------------------------------------------| // // What we do to find the best sizing, we do the following // 1. Determine the minimum size of the chart area. // 2. Split the remaining width equally between each vertical axis // 3. Split the remaining height equally between each horizontal axis // 4. Give each layout the maximum size it can be. The layout will return it's minimum size // 5. Adjust the sizes of each axis based on it's minimum reported size. // 6. Refit each axis // 7. Position each axis in the final location // 8. Tell the chart the final location of the chart area // 9. Tell any axes that overlay the chart area the positions of the chart area // Step 1 var chartWidth = width - leftPadding - rightPadding; var chartHeight = height - topPadding - bottomPadding; var chartAreaWidth = chartWidth / 2; // min 50% var chartAreaHeight = chartHeight / 2; // min 50% // Step 2 var verticalBoxWidth = (width - chartAreaWidth) / (leftBoxes.length + rightBoxes.length); // Step 3 var horizontalBoxHeight = (height - chartAreaHeight) / (topBoxes.length + bottomBoxes.length); // Step 4 var maxChartAreaWidth = chartWidth; var maxChartAreaHeight = chartHeight; var minBoxSizes = []; function getMinimumBoxSize(box) { var minSize; var isHorizontal = box.isHorizontal(); if (isHorizontal) { minSize = box.update(box.fullWidth ? chartWidth : maxChartAreaWidth, horizontalBoxHeight); maxChartAreaHeight -= minSize.height; } else { minSize = box.update(verticalBoxWidth, chartAreaHeight); maxChartAreaWidth -= minSize.width; } minBoxSizes.push({ horizontal: isHorizontal, minSize: minSize, box: box, }); } helpers.each(leftBoxes.concat(rightBoxes, topBoxes, bottomBoxes), getMinimumBoxSize); // If a horizontal box has padding, we move the left boxes over to avoid ugly charts (see issue #2478) var maxHorizontalLeftPadding = 0; var maxHorizontalRightPadding = 0; var maxVerticalTopPadding = 0; var maxVerticalBottomPadding = 0; helpers.each(topBoxes.concat(bottomBoxes), function(horizontalBox) { if (horizontalBox.getPadding) { var boxPadding = horizontalBox.getPadding(); maxHorizontalLeftPadding = Math.max(maxHorizontalLeftPadding, boxPadding.left); maxHorizontalRightPadding = Math.max(maxHorizontalRightPadding, boxPadding.right); } }); helpers.each(leftBoxes.concat(rightBoxes), function(verticalBox) { if (verticalBox.getPadding) { var boxPadding = verticalBox.getPadding(); maxVerticalTopPadding = Math.max(maxVerticalTopPadding, boxPadding.top); maxVerticalBottomPadding = Math.max(maxVerticalBottomPadding, boxPadding.bottom); } }); // At this point, maxChartAreaHeight and maxChartAreaWidth are the size the chart area could // be if the axes are drawn at their minimum sizes. // Steps 5 & 6 var totalLeftBoxesWidth = leftPadding; var totalRightBoxesWidth = rightPadding; var totalTopBoxesHeight = topPadding; var totalBottomBoxesHeight = bottomPadding; // Function to fit a box function fitBox(box) { var minBoxSize = helpers.findNextWhere(minBoxSizes, function(minBox) { return minBox.box === box; }); if (minBoxSize) { if (box.isHorizontal()) { var scaleMargin = { left: Math.max(totalLeftBoxesWidth, maxHorizontalLeftPadding), right: Math.max(totalRightBoxesWidth, maxHorizontalRightPadding), top: 0, bottom: 0 }; // Don't use min size here because of label rotation. When the labels are rotated, their rotation highly depends // on the margin. Sometimes they need to increase in size slightly box.update(box.fullWidth ? chartWidth : maxChartAreaWidth, chartHeight / 2, scaleMargin); } else { box.update(minBoxSize.minSize.width, maxChartAreaHeight); } } } // Update, and calculate the left and right margins for the horizontal boxes helpers.each(leftBoxes.concat(rightBoxes), fitBox); helpers.each(leftBoxes, function(box) { totalLeftBoxesWidth += box.width; }); helpers.each(rightBoxes, function(box) { totalRightBoxesWidth += box.width; }); // Set the Left and Right margins for the horizontal boxes helpers.each(topBoxes.concat(bottomBoxes), fitBox); // Figure out how much margin is on the top and bottom of the vertical boxes helpers.each(topBoxes, function(box) { totalTopBoxesHeight += box.height; }); helpers.each(bottomBoxes, function(box) { totalBottomBoxesHeight += box.height; }); function finalFitVerticalBox(box) { var minBoxSize = helpers.findNextWhere(minBoxSizes, function(minSize) { return minSize.box === box; }); var scaleMargin = { left: 0, right: 0, top: totalTopBoxesHeight, bottom: totalBottomBoxesHeight }; if (minBoxSize) { box.update(minBoxSize.minSize.width, maxChartAreaHeight, scaleMargin); } } // Let the left layout know the final margin helpers.each(leftBoxes.concat(rightBoxes), finalFitVerticalBox); // Recalculate because the size of each layout might have changed slightly due to the margins (label rotation for instance) totalLeftBoxesWidth = leftPadding; totalRightBoxesWidth = rightPadding; totalTopBoxesHeight = topPadding; totalBottomBoxesHeight = bottomPadding; helpers.each(leftBoxes, function(box) { totalLeftBoxesWidth += box.width; }); helpers.each(rightBoxes, function(box) { totalRightBoxesWidth += box.width; }); helpers.each(topBoxes, function(box) { totalTopBoxesHeight += box.height; }); helpers.each(bottomBoxes, function(box) { totalBottomBoxesHeight += box.height; }); // We may be adding some padding to account for rotated x axis labels var leftPaddingAddition = Math.max(maxHorizontalLeftPadding - totalLeftBoxesWidth, 0); totalLeftBoxesWidth += leftPaddingAddition; totalRightBoxesWidth += Math.max(maxHorizontalRightPadding - totalRightBoxesWidth, 0); var topPaddingAddition = Math.max(maxVerticalTopPadding - totalTopBoxesHeight, 0); totalTopBoxesHeight += topPaddingAddition; totalBottomBoxesHeight += Math.max(maxVerticalBottomPadding - totalBottomBoxesHeight, 0); // Figure out if our chart area changed. This would occur if the dataset layout label rotation // changed due to the application of the margins in step 6. Since we can only get bigger, this is safe to do // without calling `fit` again var newMaxChartAreaHeight = height - totalTopBoxesHeight - totalBottomBoxesHeight; var newMaxChartAreaWidth = width - totalLeftBoxesWidth - totalRightBoxesWidth; if (newMaxChartAreaWidth !== maxChartAreaWidth || newMaxChartAreaHeight !== maxChartAreaHeight) { helpers.each(leftBoxes, function(box) { box.height = newMaxChartAreaHeight; }); helpers.each(rightBoxes, function(box) { box.height = newMaxChartAreaHeight; }); helpers.each(topBoxes, function(box) { if (!box.fullWidth) { box.width = newMaxChartAreaWidth; } }); helpers.each(bottomBoxes, function(box) { if (!box.fullWidth) { box.width = newMaxChartAreaWidth; } }); maxChartAreaHeight = newMaxChartAreaHeight; maxChartAreaWidth = newMaxChartAreaWidth; } // Step 7 - Position the boxes var left = leftPadding + leftPaddingAddition; var top = topPadding + topPaddingAddition; function placeBox(box) { if (box.isHorizontal()) { box.left = box.fullWidth ? leftPadding : totalLeftBoxesWidth; box.right = box.fullWidth ? width - rightPadding : totalLeftBoxesWidth + maxChartAreaWidth; box.top = top; box.bottom = top + box.height; // Move to next point top = box.bottom; } else { box.left = left; box.right = left + box.width; box.top = totalTopBoxesHeight; box.bottom = totalTopBoxesHeight + maxChartAreaHeight; // Move to next point left = box.right; } } helpers.each(leftBoxes.concat(topBoxes), placeBox); // Account for chart width and height left += maxChartAreaWidth; top += maxChartAreaHeight; helpers.each(rightBoxes, placeBox); helpers.each(bottomBoxes, placeBox); // Step 8 chart.chartArea = { left: totalLeftBoxesWidth, top: totalTopBoxesHeight, right: totalLeftBoxesWidth + maxChartAreaWidth, bottom: totalTopBoxesHeight + maxChartAreaHeight }; // Step 9 helpers.each(chartAreaBoxes, function(box) { box.left = chart.chartArea.left; box.top = chart.chartArea.top; box.right = chart.chartArea.right; box.bottom = chart.chartArea.bottom; box.update(maxChartAreaWidth, maxChartAreaHeight); }); } }; }; },{"45":45}],31:[function(require,module,exports){ 'use strict'; var defaults = require(25); var Element = require(26); var helpers = require(45); defaults._set('global', { plugins: {} }); module.exports = function(Chart) { /** * The plugin service singleton * @namespace Chart.plugins * @since 2.1.0 */ Chart.plugins = { /** * Globally registered plugins. * @private */ _plugins: [], /** * This identifier is used to invalidate the descriptors cache attached to each chart * when a global plugin is registered or unregistered. In this case, the cache ID is * incremented and descriptors are regenerated during following API calls. * @private */ _cacheId: 0, /** * Registers the given plugin(s) if not already registered. * @param {Array|Object} plugins plugin instance(s). */ register: function(plugins) { var p = this._plugins; ([]).concat(plugins).forEach(function(plugin) { if (p.indexOf(plugin) === -1) { p.push(plugin); } }); this._cacheId++; }, /** * Unregisters the given plugin(s) only if registered. * @param {Array|Object} plugins plugin instance(s). */ unregister: function(plugins) { var p = this._plugins; ([]).concat(plugins).forEach(function(plugin) { var idx = p.indexOf(plugin); if (idx !== -1) { p.splice(idx, 1); } }); this._cacheId++; }, /** * Remove all registered plugins. * @since 2.1.5 */ clear: function() { this._plugins = []; this._cacheId++; }, /** * Returns the number of registered plugins? * @returns {Number} * @since 2.1.5 */ count: function() { return this._plugins.length; }, /** * Returns all registered plugin instances. * @returns {Array} array of plugin objects. * @since 2.1.5 */ getAll: function() { return this._plugins; }, /** * Calls enabled plugins for `chart` on the specified hook and with the given args. * This method immediately returns as soon as a plugin explicitly returns false. The * returned value can be used, for instance, to interrupt the current action. * @param {Object} chart - The chart instance for which plugins should be called. * @param {String} hook - The name of the plugin method to call (e.g. 'beforeUpdate'). * @param {Array} [args] - Extra arguments to apply to the hook call. * @returns {Boolean} false if any of the plugins return false, else returns true. */ notify: function(chart, hook, args) { var descriptors = this.descriptors(chart); var ilen = descriptors.length; var i, descriptor, plugin, params, method; for (i = 0; i < ilen; ++i) { descriptor = descriptors[i]; plugin = descriptor.plugin; method = plugin[hook]; if (typeof method === 'function') { params = [chart].concat(args || []); params.push(descriptor.options); if (method.apply(plugin, params) === false) { return false; } } } return true; }, /** * Returns descriptors of enabled plugins for the given chart. * @returns {Array} [{ plugin, options }] * @private */ descriptors: function(chart) { var cache = chart._plugins || (chart._plugins = {}); if (cache.id === this._cacheId) { return cache.descriptors; } var plugins = []; var descriptors = []; var config = (chart && chart.config) || {}; var options = (config.options && config.options.plugins) || {}; this._plugins.concat(config.plugins || []).forEach(function(plugin) { var idx = plugins.indexOf(plugin); if (idx !== -1) { return; } var id = plugin.id; var opts = options[id]; if (opts === false) { return; } if (opts === true) { opts = helpers.clone(defaults.global.plugins[id]); } plugins.push(plugin); descriptors.push({ plugin: plugin, options: opts || {} }); }); cache.descriptors = descriptors; cache.id = this._cacheId; return descriptors; } }; /** * Plugin extension hooks. * @interface IPlugin * @since 2.1.0 */ /** * @method IPlugin#beforeInit * @desc Called before initializing `chart`. * @param {Chart.Controller} chart - The chart instance. * @param {Object} options - The plugin options. */ /** * @method IPlugin#afterInit * @desc Called after `chart` has been initialized and before the first update. * @param {Chart.Controller} chart - The chart instance. * @param {Object} options - The plugin options. */ /** * @method IPlugin#beforeUpdate * @desc Called before updating `chart`. If any plugin returns `false`, the update * is cancelled (and thus subsequent render(s)) until another `update` is triggered. * @param {Chart.Controller} chart - The chart instance. * @param {Object} options - The plugin options. * @returns {Boolean} `false` to cancel the chart update. */ /** * @method IPlugin#afterUpdate * @desc Called after `chart` has been updated and before rendering. Note that this * hook will not be called if the chart update has been previously cancelled. * @param {Chart.Controller} chart - The chart instance. * @param {Object} options - The plugin options. */ /** * @method IPlugin#beforeDatasetsUpdate * @desc Called before updating the `chart` datasets. If any plugin returns `false`, * the datasets update is cancelled until another `update` is triggered. * @param {Chart.Controller} chart - The chart instance. * @param {Object} options - The plugin options. * @returns {Boolean} false to cancel the datasets update. * @since version 2.1.5 */ /** * @method IPlugin#afterDatasetsUpdate * @desc Called after the `chart` datasets have been updated. Note that this hook * will not be called if the datasets update has been previously cancelled. * @param {Chart.Controller} chart - The chart instance. * @param {Object} options - The plugin options. * @since version 2.1.5 */ /** * @method IPlugin#beforeDatasetUpdate * @desc Called before updating the `chart` dataset at the given `args.index`. If any plugin * returns `false`, the datasets update is cancelled until another `update` is triggered. * @param {Chart} chart - The chart instance. * @param {Object} args - The call arguments. * @param {Number} args.index - The dataset index. * @param {Object} args.meta - The dataset metadata. * @param {Object} options - The plugin options. * @returns {Boolean} `false` to cancel the chart datasets drawing. */ /** * @method IPlugin#afterDatasetUpdate * @desc Called after the `chart` datasets at the given `args.index` has been updated. Note * that this hook will not be called if the datasets update has been previously cancelled. * @param {Chart} chart - The chart instance. * @param {Object} args - The call arguments. * @param {Number} args.index - The dataset index. * @param {Object} args.meta - The dataset metadata. * @param {Object} options - The plugin options. */ /** * @method IPlugin#beforeLayout * @desc Called before laying out `chart`. If any plugin returns `false`, * the layout update is cancelled until another `update` is triggered. * @param {Chart.Controller} chart - The chart instance. * @param {Object} options - The plugin options. * @returns {Boolean} `false` to cancel the chart layout. */ /** * @method IPlugin#afterLayout * @desc Called after the `chart` has been layed out. Note that this hook will not * be called if the layout update has been previously cancelled. * @param {Chart.Controller} chart - The chart instance. * @param {Object} options - The plugin options. */ /** * @method IPlugin#beforeRender * @desc Called before rendering `chart`. If any plugin returns `false`, * the rendering is cancelled until another `render` is triggered. * @param {Chart.Controller} chart - The chart instance. * @param {Object} options - The plugin options. * @returns {Boolean} `false` to cancel the chart rendering. */ /** * @method IPlugin#afterRender * @desc Called after the `chart` has been fully rendered (and animation completed). Note * that this hook will not be called if the rendering has been previously cancelled. * @param {Chart.Controller} chart - The chart instance. * @param {Object} options - The plugin options. */ /** * @method IPlugin#beforeDraw * @desc Called before drawing `chart` at every animation frame specified by the given * easing value. If any plugin returns `false`, the frame drawing is cancelled until * another `render` is triggered. * @param {Chart.Controller} chart - The chart instance. * @param {Number} easingValue - The current animation value, between 0.0 and 1.0. * @param {Object} options - The plugin options. * @returns {Boolean} `false` to cancel the chart drawing. */ /** * @method IPlugin#afterDraw * @desc Called after the `chart` has been drawn for the specific easing value. Note * that this hook will not be called if the drawing has been previously cancelled. * @param {Chart.Controller} chart - The chart instance. * @param {Number} easingValue - The current animation value, between 0.0 and 1.0. * @param {Object} options - The plugin options. */ /** * @method IPlugin#beforeDatasetsDraw * @desc Called before drawing the `chart` datasets. If any plugin returns `false`, * the datasets drawing is cancelled until another `render` is triggered. * @param {Chart.Controller} chart - The chart instance. * @param {Number} easingValue - The current animation value, between 0.0 and 1.0. * @param {Object} options - The plugin options. * @returns {Boolean} `false` to cancel the chart datasets drawing. */ /** * @method IPlugin#afterDatasetsDraw * @desc Called after the `chart` datasets have been drawn. Note that this hook * will not be called if the datasets drawing has been previously cancelled. * @param {Chart.Controller} chart - The chart instance. * @param {Number} easingValue - The current animation value, between 0.0 and 1.0. * @param {Object} options - The plugin options. */ /** * @method IPlugin#beforeDatasetDraw * @desc Called before drawing the `chart` dataset at the given `args.index` (datasets * are drawn in the reverse order). If any plugin returns `false`, the datasets drawing * is cancelled until another `render` is triggered. * @param {Chart} chart - The chart instance. * @param {Object} args - The call arguments. * @param {Number} args.index - The dataset index. * @param {Object} args.meta - The dataset metadata. * @param {Number} args.easingValue - The current animation value, between 0.0 and 1.0. * @param {Object} options - The plugin options. * @returns {Boolean} `false` to cancel the chart datasets drawing. */ /** * @method IPlugin#afterDatasetDraw * @desc Called after the `chart` datasets at the given `args.index` have been drawn * (datasets are drawn in the reverse order). Note that this hook will not be called * if the datasets drawing has been previously cancelled. * @param {Chart} chart - The chart instance. * @param {Object} args - The call arguments. * @param {Number} args.index - The dataset index. * @param {Object} args.meta - The dataset metadata. * @param {Number} args.easingValue - The current animation value, between 0.0 and 1.0. * @param {Object} options - The plugin options. */ /** * @method IPlugin#beforeTooltipDraw * @desc Called before drawing the `tooltip`. If any plugin returns `false`, * the tooltip drawing is cancelled until another `render` is triggered. * @param {Chart} chart - The chart instance. * @param {Object} args - The call arguments. * @param {Object} args.tooltip - The tooltip. * @param {Number} args.easingValue - The current animation value, between 0.0 and 1.0. * @param {Object} options - The plugin options. * @returns {Boolean} `false` to cancel the chart tooltip drawing. */ /** * @method IPlugin#afterTooltipDraw * @desc Called after drawing the `tooltip`. Note that this hook will not * be called if the tooltip drawing has been previously cancelled. * @param {Chart} chart - The chart instance. * @param {Object} args - The call arguments. * @param {Object} args.tooltip - The tooltip. * @param {Number} args.easingValue - The current animation value, between 0.0 and 1.0. * @param {Object} options - The plugin options. */ /** * @method IPlugin#beforeEvent * @desc Called before processing the specified `event`. If any plugin returns `false`, * the event will be discarded. * @param {Chart.Controller} chart - The chart instance. * @param {IEvent} event - The event object. * @param {Object} options - The plugin options. */ /** * @method IPlugin#afterEvent * @desc Called after the `event` has been consumed. Note that this hook * will not be called if the `event` has been previously discarded. * @param {Chart.Controller} chart - The chart instance. * @param {IEvent} event - The event object. * @param {Object} options - The plugin options. */ /** * @method IPlugin#resize * @desc Called after the chart as been resized. * @param {Chart.Controller} chart - The chart instance. * @param {Number} size - The new canvas display size (eq. canvas.style width & height). * @param {Object} options - The plugin options. */ /** * @method IPlugin#destroy * @desc Called after the chart as been destroyed. * @param {Chart.Controller} chart - The chart instance. * @param {Object} options - The plugin options. */ /** * Provided for backward compatibility, use Chart.plugins instead * @namespace Chart.pluginService * @deprecated since version 2.1.5 * @todo remove at version 3 * @private */ Chart.pluginService = Chart.plugins; /** * Provided for backward compatibility, inheriting from Chart.PlugingBase has no * effect, instead simply create/register plugins via plain JavaScript objects. * @interface Chart.PluginBase * @deprecated since version 2.5.0 * @todo remove at version 3 * @private */ Chart.PluginBase = Element.extend({}); }; },{"25":25,"26":26,"45":45}],32:[function(require,module,exports){ 'use strict'; var defaults = require(25); var Element = require(26); var helpers = require(45); var Ticks = require(34); defaults._set('scale', { display: true, position: 'left', offset: false, // grid line settings gridLines: { display: true, color: 'rgba(0, 0, 0, 0.1)', lineWidth: 1, drawBorder: true, drawOnChartArea: true, drawTicks: true, tickMarkLength: 10, zeroLineWidth: 1, zeroLineColor: 'rgba(0,0,0,0.25)', zeroLineBorderDash: [], zeroLineBorderDashOffset: 0.0, offsetGridLines: false, borderDash: [], borderDashOffset: 0.0 }, // scale label scaleLabel: { // display property display: false, // actual label labelString: '', // line height lineHeight: 1.2, // top/bottom padding padding: { top: 4, bottom: 4 } }, // label settings ticks: { beginAtZero: false, minRotation: 0, maxRotation: 50, mirror: false, padding: 0, reverse: false, display: true, autoSkip: true, autoSkipPadding: 0, labelOffset: 0, // We pass through arrays to be rendered as multiline labels, we convert Others to strings here. callback: Ticks.formatters.values, minor: {}, major: {} } }); function labelsFromTicks(ticks) { var labels = []; var i, ilen; for (i = 0, ilen = ticks.length; i < ilen; ++i) { labels.push(ticks[i].label); } return labels; } function getLineValue(scale, index, offsetGridLines) { var lineValue = scale.getPixelForTick(index); if (offsetGridLines) { if (index === 0) { lineValue -= (scale.getPixelForTick(1) - lineValue) / 2; } else { lineValue -= (lineValue - scale.getPixelForTick(index - 1)) / 2; } } return lineValue; } module.exports = function(Chart) { function computeTextSize(context, tick, font) { return helpers.isArray(tick) ? helpers.longestText(context, font, tick) : context.measureText(tick).width; } function parseFontOptions(options) { var valueOrDefault = helpers.valueOrDefault; var globalDefaults = defaults.global; var size = valueOrDefault(options.fontSize, globalDefaults.defaultFontSize); var style = valueOrDefault(options.fontStyle, globalDefaults.defaultFontStyle); var family = valueOrDefault(options.fontFamily, globalDefaults.defaultFontFamily); return { size: size, style: style, family: family, font: helpers.fontString(size, style, family) }; } function parseLineHeight(options) { return helpers.options.toLineHeight( helpers.valueOrDefault(options.lineHeight, 1.2), helpers.valueOrDefault(options.fontSize, defaults.global.defaultFontSize)); } Chart.Scale = Element.extend({ /** * Get the padding needed for the scale * @method getPadding * @private * @returns {Padding} the necessary padding */ getPadding: function() { var me = this; return { left: me.paddingLeft || 0, top: me.paddingTop || 0, right: me.paddingRight || 0, bottom: me.paddingBottom || 0 }; }, /** * Returns the scale tick objects ({label, major}) * @since 2.7 */ getTicks: function() { return this._ticks; }, // These methods are ordered by lifecyle. Utilities then follow. // Any function defined here is inherited by all scale types. // Any function can be extended by the scale type mergeTicksOptions: function() { var ticks = this.options.ticks; if (ticks.minor === false) { ticks.minor = { display: false }; } if (ticks.major === false) { ticks.major = { display: false }; } for (var key in ticks) { if (key !== 'major' && key !== 'minor') { if (typeof ticks.minor[key] === 'undefined') { ticks.minor[key] = ticks[key]; } if (typeof ticks.major[key] === 'undefined') { ticks.major[key] = ticks[key]; } } } }, beforeUpdate: function() { helpers.callback(this.options.beforeUpdate, [this]); }, update: function(maxWidth, maxHeight, margins) { var me = this; var i, ilen, labels, label, ticks, tick; // Update Lifecycle - Probably don't want to ever extend or overwrite this function ;) me.beforeUpdate(); // Absorb the master measurements me.maxWidth = maxWidth; me.maxHeight = maxHeight; me.margins = helpers.extend({ left: 0, right: 0, top: 0, bottom: 0 }, margins); me.longestTextCache = me.longestTextCache || {}; // Dimensions me.beforeSetDimensions(); me.setDimensions(); me.afterSetDimensions(); // Data min/max me.beforeDataLimits(); me.determineDataLimits(); me.afterDataLimits(); // Ticks - `this.ticks` is now DEPRECATED! // Internal ticks are now stored as objects in the PRIVATE `this._ticks` member // and must not be accessed directly from outside this class. `this.ticks` being // around for long time and not marked as private, we can't change its structure // without unexpected breaking changes. If you need to access the scale ticks, // use scale.getTicks() instead. me.beforeBuildTicks(); // New implementations should return an array of objects but for BACKWARD COMPAT, // we still support no return (`this.ticks` internally set by calling this method). ticks = me.buildTicks() || []; me.afterBuildTicks(); me.beforeTickToLabelConversion(); // New implementations should return the formatted tick labels but for BACKWARD // COMPAT, we still support no return (`this.ticks` internally changed by calling // this method and supposed to contain only string values). labels = me.convertTicksToLabels(ticks) || me.ticks; me.afterTickToLabelConversion(); me.ticks = labels; // BACKWARD COMPATIBILITY // IMPORTANT: from this point, we consider that `this.ticks` will NEVER change! // BACKWARD COMPAT: synchronize `_ticks` with labels (so potentially `this.ticks`) for (i = 0, ilen = labels.length; i < ilen; ++i) { label = labels[i]; tick = ticks[i]; if (!tick) { ticks.push(tick = { label: label, major: false }); } else { tick.label = label; } } me._ticks = ticks; // Tick Rotation me.beforeCalculateTickRotation(); me.calculateTickRotation(); me.afterCalculateTickRotation(); // Fit me.beforeFit(); me.fit(); me.afterFit(); // me.afterUpdate(); return me.minSize; }, afterUpdate: function() { helpers.callback(this.options.afterUpdate, [this]); }, // beforeSetDimensions: function() { helpers.callback(this.options.beforeSetDimensions, [this]); }, setDimensions: function() { var me = this; // Set the unconstrained dimension before label rotation if (me.isHorizontal()) { // Reset position before calculating rotation me.width = me.maxWidth; me.left = 0; me.right = me.width; } else { me.height = me.maxHeight; // Reset position before calculating rotation me.top = 0; me.bottom = me.height; } // Reset padding me.paddingLeft = 0; me.paddingTop = 0; me.paddingRight = 0; me.paddingBottom = 0; }, afterSetDimensions: function() { helpers.callback(this.options.afterSetDimensions, [this]); }, // Data limits beforeDataLimits: function() { helpers.callback(this.options.beforeDataLimits, [this]); }, determineDataLimits: helpers.noop, afterDataLimits: function() { helpers.callback(this.options.afterDataLimits, [this]); }, // beforeBuildTicks: function() { helpers.callback(this.options.beforeBuildTicks, [this]); }, buildTicks: helpers.noop, afterBuildTicks: function() { helpers.callback(this.options.afterBuildTicks, [this]); }, beforeTickToLabelConversion: function() { helpers.callback(this.options.beforeTickToLabelConversion, [this]); }, convertTicksToLabels: function() { var me = this; // Convert ticks to strings var tickOpts = me.options.ticks; me.ticks = me.ticks.map(tickOpts.userCallback || tickOpts.callback, this); }, afterTickToLabelConversion: function() { helpers.callback(this.options.afterTickToLabelConversion, [this]); }, // beforeCalculateTickRotation: function() { helpers.callback(this.options.beforeCalculateTickRotation, [this]); }, calculateTickRotation: function() { var me = this; var context = me.ctx; var tickOpts = me.options.ticks; var labels = labelsFromTicks(me._ticks); // Get the width of each grid by calculating the difference // between x offsets between 0 and 1. var tickFont = parseFontOptions(tickOpts); context.font = tickFont.font; var labelRotation = tickOpts.minRotation || 0; if (labels.length && me.options.display && me.isHorizontal()) { var originalLabelWidth = helpers.longestText(context, tickFont.font, labels, me.longestTextCache); var labelWidth = originalLabelWidth; var cosRotation, sinRotation; // Allow 3 pixels x2 padding either side for label readability var tickWidth = me.getPixelForTick(1) - me.getPixelForTick(0) - 6; // Max label rotation can be set or default to 90 - also act as a loop counter while (labelWidth > tickWidth && labelRotation < tickOpts.maxRotation) { var angleRadians = helpers.toRadians(labelRotation); cosRotation = Math.cos(angleRadians); sinRotation = Math.sin(angleRadians); if (sinRotation * originalLabelWidth > me.maxHeight) { // go back one step labelRotation--; break; } labelRotation++; labelWidth = cosRotation * originalLabelWidth; } } me.labelRotation = labelRotation; }, afterCalculateTickRotation: function() { helpers.callback(this.options.afterCalculateTickRotation, [this]); }, // beforeFit: function() { helpers.callback(this.options.beforeFit, [this]); }, fit: function() { var me = this; // Reset var minSize = me.minSize = { width: 0, height: 0 }; var labels = labelsFromTicks(me._ticks); var opts = me.options; var tickOpts = opts.ticks; var scaleLabelOpts = opts.scaleLabel; var gridLineOpts = opts.gridLines; var display = opts.display; var isHorizontal = me.isHorizontal(); var tickFont = parseFontOptions(tickOpts); var tickMarkLength = opts.gridLines.tickMarkLength; // Width if (isHorizontal) { // subtract the margins to line up with the chartArea if we are a full width scale minSize.width = me.isFullWidth() ? me.maxWidth - me.margins.left - me.margins.right : me.maxWidth; } else { minSize.width = display && gridLineOpts.drawTicks ? tickMarkLength : 0; } // height if (isHorizontal) { minSize.height = display && gridLineOpts.drawTicks ? tickMarkLength : 0; } else { minSize.height = me.maxHeight; // fill all the height } // Are we showing a title for the scale? if (scaleLabelOpts.display && display) { var scaleLabelLineHeight = parseLineHeight(scaleLabelOpts); var scaleLabelPadding = helpers.options.toPadding(scaleLabelOpts.padding); var deltaHeight = scaleLabelLineHeight + scaleLabelPadding.height; if (isHorizontal) { minSize.height += deltaHeight; } else { minSize.width += deltaHeight; } } // Don't bother fitting the ticks if we are not showing them if (tickOpts.display && display) { var largestTextWidth = helpers.longestText(me.ctx, tickFont.font, labels, me.longestTextCache); var tallestLabelHeightInLines = helpers.numberOfLabelLines(labels); var lineSpace = tickFont.size * 0.5; var tickPadding = me.options.ticks.padding; if (isHorizontal) { // A horizontal axis is more constrained by the height. me.longestLabelWidth = largestTextWidth; var angleRadians = helpers.toRadians(me.labelRotation); var cosRotation = Math.cos(angleRadians); var sinRotation = Math.sin(angleRadians); // TODO - improve this calculation var labelHeight = (sinRotation * largestTextWidth) + (tickFont.size * tallestLabelHeightInLines) + (lineSpace * (tallestLabelHeightInLines - 1)) + lineSpace; // padding minSize.height = Math.min(me.maxHeight, minSize.height + labelHeight + tickPadding); me.ctx.font = tickFont.font; var firstLabelWidth = computeTextSize(me.ctx, labels[0], tickFont.font); var lastLabelWidth = computeTextSize(me.ctx, labels[labels.length - 1], tickFont.font); // Ensure that our ticks are always inside the canvas. When rotated, ticks are right aligned // which means that the right padding is dominated by the font height if (me.labelRotation !== 0) { me.paddingLeft = opts.position === 'bottom' ? (cosRotation * firstLabelWidth) + 3 : (cosRotation * lineSpace) + 3; // add 3 px to move away from canvas edges me.paddingRight = opts.position === 'bottom' ? (cosRotation * lineSpace) + 3 : (cosRotation * lastLabelWidth) + 3; } else { me.paddingLeft = firstLabelWidth / 2 + 3; // add 3 px to move away from canvas edges me.paddingRight = lastLabelWidth / 2 + 3; } } else { // A vertical axis is more constrained by the width. Labels are the // dominant factor here, so get that length first and account for padding if (tickOpts.mirror) { largestTextWidth = 0; } else { // use lineSpace for consistency with horizontal axis // tickPadding is not implemented for horizontal largestTextWidth += tickPadding + lineSpace; } minSize.width = Math.min(me.maxWidth, minSize.width + largestTextWidth); me.paddingTop = tickFont.size / 2; me.paddingBottom = tickFont.size / 2; } } me.handleMargins(); me.width = minSize.width; me.height = minSize.height; }, /** * Handle margins and padding interactions * @private */ handleMargins: function() { var me = this; if (me.margins) { me.paddingLeft = Math.max(me.paddingLeft - me.margins.left, 0); me.paddingTop = Math.max(me.paddingTop - me.margins.top, 0); me.paddingRight = Math.max(me.paddingRight - me.margins.right, 0); me.paddingBottom = Math.max(me.paddingBottom - me.margins.bottom, 0); } }, afterFit: function() { helpers.callback(this.options.afterFit, [this]); }, // Shared Methods isHorizontal: function() { return this.options.position === 'top' || this.options.position === 'bottom'; }, isFullWidth: function() { return (this.options.fullWidth); }, // Get the correct value. NaN bad inputs, If the value type is object get the x or y based on whether we are horizontal or not getRightValue: function(rawValue) { // Null and undefined values first if (helpers.isNullOrUndef(rawValue)) { return NaN; } // isNaN(object) returns true, so make sure NaN is checking for a number; Discard Infinite values if (typeof rawValue === 'number' && !isFinite(rawValue)) { return NaN; } // If it is in fact an object, dive in one more level if (rawValue) { if (this.isHorizontal()) { if (rawValue.x !== undefined) { return this.getRightValue(rawValue.x); } } else if (rawValue.y !== undefined) { return this.getRightValue(rawValue.y); } } // Value is good, return it return rawValue; }, /** * Used to get the value to display in the tooltip for the data at the given index * @param index * @param datasetIndex */ getLabelForIndex: helpers.noop, /** * Returns the location of the given data point. Value can either be an index or a numerical value * The coordinate (0, 0) is at the upper-left corner of the canvas * @param value * @param index * @param datasetIndex */ getPixelForValue: helpers.noop, /** * Used to get the data value from a given pixel. This is the inverse of getPixelForValue * The coordinate (0, 0) is at the upper-left corner of the canvas * @param pixel */ getValueForPixel: helpers.noop, /** * Returns the location of the tick at the given index * The coordinate (0, 0) is at the upper-left corner of the canvas */ getPixelForTick: function(index) { var me = this; var offset = me.options.offset; if (me.isHorizontal()) { var innerWidth = me.width - (me.paddingLeft + me.paddingRight); var tickWidth = innerWidth / Math.max((me._ticks.length - (offset ? 0 : 1)), 1); var pixel = (tickWidth * index) + me.paddingLeft; if (offset) { pixel += tickWidth / 2; } var finalVal = me.left + Math.round(pixel); finalVal += me.isFullWidth() ? me.margins.left : 0; return finalVal; } var innerHeight = me.height - (me.paddingTop + me.paddingBottom); return me.top + (index * (innerHeight / (me._ticks.length - 1))); }, /** * Utility for getting the pixel location of a percentage of scale * The coordinate (0, 0) is at the upper-left corner of the canvas */ getPixelForDecimal: function(decimal) { var me = this; if (me.isHorizontal()) { var innerWidth = me.width - (me.paddingLeft + me.paddingRight); var valueOffset = (innerWidth * decimal) + me.paddingLeft; var finalVal = me.left + Math.round(valueOffset); finalVal += me.isFullWidth() ? me.margins.left : 0; return finalVal; } return me.top + (decimal * me.height); }, /** * Returns the pixel for the minimum chart value * The coordinate (0, 0) is at the upper-left corner of the canvas */ getBasePixel: function() { return this.getPixelForValue(this.getBaseValue()); }, getBaseValue: function() { var me = this; var min = me.min; var max = me.max; return me.beginAtZero ? 0 : min < 0 && max < 0 ? max : min > 0 && max > 0 ? min : 0; }, /** * Returns a subset of ticks to be plotted to avoid overlapping labels. * @private */ _autoSkip: function(ticks) { var skipRatio; var me = this; var isHorizontal = me.isHorizontal(); var optionTicks = me.options.ticks.minor; var tickCount = ticks.length; var labelRotationRadians = helpers.toRadians(me.labelRotation); var cosRotation = Math.cos(labelRotationRadians); var longestRotatedLabel = me.longestLabelWidth * cosRotation; var result = []; var i, tick, shouldSkip; // figure out the maximum number of gridlines to show var maxTicks; if (optionTicks.maxTicksLimit) { maxTicks = optionTicks.maxTicksLimit; } if (isHorizontal) { skipRatio = false; if ((longestRotatedLabel + optionTicks.autoSkipPadding) * tickCount > (me.width - (me.paddingLeft + me.paddingRight))) { skipRatio = 1 + Math.floor(((longestRotatedLabel + optionTicks.autoSkipPadding) * tickCount) / (me.width - (me.paddingLeft + me.paddingRight))); } // if they defined a max number of optionTicks, // increase skipRatio until that number is met if (maxTicks && tickCount > maxTicks) { skipRatio = Math.max(skipRatio, Math.floor(tickCount / maxTicks)); } } for (i = 0; i < tickCount; i++) { tick = ticks[i]; // Since we always show the last tick,we need may need to hide the last shown one before shouldSkip = (skipRatio > 1 && i % skipRatio > 0) || (i % skipRatio === 0 && i + skipRatio >= tickCount); if (shouldSkip && i !== tickCount - 1) { // leave tick in place but make sure it's not displayed (#4635) delete tick.label; } result.push(tick); } return result; }, // Actually draw the scale on the canvas // @param {rectangle} chartArea : the area of the chart to draw full grid lines on draw: function(chartArea) { var me = this; var options = me.options; if (!options.display) { return; } var context = me.ctx; var globalDefaults = defaults.global; var optionTicks = options.ticks.minor; var optionMajorTicks = options.ticks.major || optionTicks; var gridLines = options.gridLines; var scaleLabel = options.scaleLabel; var isRotated = me.labelRotation !== 0; var isHorizontal = me.isHorizontal(); var ticks = optionTicks.autoSkip ? me._autoSkip(me.getTicks()) : me.getTicks(); var tickFontColor = helpers.valueOrDefault(optionTicks.fontColor, globalDefaults.defaultFontColor); var tickFont = parseFontOptions(optionTicks); var majorTickFontColor = helpers.valueOrDefault(optionMajorTicks.fontColor, globalDefaults.defaultFontColor); var majorTickFont = parseFontOptions(optionMajorTicks); var tl = gridLines.drawTicks ? gridLines.tickMarkLength : 0; var scaleLabelFontColor = helpers.valueOrDefault(scaleLabel.fontColor, globalDefaults.defaultFontColor); var scaleLabelFont = parseFontOptions(scaleLabel); var scaleLabelPadding = helpers.options.toPadding(scaleLabel.padding); var labelRotationRadians = helpers.toRadians(me.labelRotation); var itemsToDraw = []; var xTickStart = options.position === 'right' ? me.left : me.right - tl; var xTickEnd = options.position === 'right' ? me.left + tl : me.right; var yTickStart = options.position === 'bottom' ? me.top : me.bottom - tl; var yTickEnd = options.position === 'bottom' ? me.top + tl : me.bottom; helpers.each(ticks, function(tick, index) { // autoskipper skipped this tick (#4635) if (helpers.isNullOrUndef(tick.label)) { return; } var label = tick.label; var lineWidth, lineColor, borderDash, borderDashOffset; if (index === me.zeroLineIndex && options.offset === gridLines.offsetGridLines) { // Draw the first index specially lineWidth = gridLines.zeroLineWidth; lineColor = gridLines.zeroLineColor; borderDash = gridLines.zeroLineBorderDash; borderDashOffset = gridLines.zeroLineBorderDashOffset; } else { lineWidth = helpers.valueAtIndexOrDefault(gridLines.lineWidth, index); lineColor = helpers.valueAtIndexOrDefault(gridLines.color, index); borderDash = helpers.valueOrDefault(gridLines.borderDash, globalDefaults.borderDash); borderDashOffset = helpers.valueOrDefault(gridLines.borderDashOffset, globalDefaults.borderDashOffset); } // Common properties var tx1, ty1, tx2, ty2, x1, y1, x2, y2, labelX, labelY; var textAlign = 'middle'; var textBaseline = 'middle'; var tickPadding = optionTicks.padding; if (isHorizontal) { var labelYOffset = tl + tickPadding; if (options.position === 'bottom') { // bottom textBaseline = !isRotated ? 'top' : 'middle'; textAlign = !isRotated ? 'center' : 'right'; labelY = me.top + labelYOffset; } else { // top textBaseline = !isRotated ? 'bottom' : 'middle'; textAlign = !isRotated ? 'center' : 'left'; labelY = me.bottom - labelYOffset; } var xLineValue = getLineValue(me, index, gridLines.offsetGridLines && ticks.length > 1); if (xLineValue < me.left) { lineColor = 'rgba(0,0,0,0)'; } xLineValue += helpers.aliasPixel(lineWidth); labelX = me.getPixelForTick(index) + optionTicks.labelOffset; // x values for optionTicks (need to consider offsetLabel option) tx1 = tx2 = x1 = x2 = xLineValue; ty1 = yTickStart; ty2 = yTickEnd; y1 = chartArea.top; y2 = chartArea.bottom; } else { var isLeft = options.position === 'left'; var labelXOffset; if (optionTicks.mirror) { textAlign = isLeft ? 'left' : 'right'; labelXOffset = tickPadding; } else { textAlign = isLeft ? 'right' : 'left'; labelXOffset = tl + tickPadding; } labelX = isLeft ? me.right - labelXOffset : me.left + labelXOffset; var yLineValue = getLineValue(me, index, gridLines.offsetGridLines && ticks.length > 1); if (yLineValue < me.top) { lineColor = 'rgba(0,0,0,0)'; } yLineValue += helpers.aliasPixel(lineWidth); labelY = me.getPixelForTick(index) + optionTicks.labelOffset; tx1 = xTickStart; tx2 = xTickEnd; x1 = chartArea.left; x2 = chartArea.right; ty1 = ty2 = y1 = y2 = yLineValue; } itemsToDraw.push({ tx1: tx1, ty1: ty1, tx2: tx2, ty2: ty2, x1: x1, y1: y1, x2: x2, y2: y2, labelX: labelX, labelY: labelY, glWidth: lineWidth, glColor: lineColor, glBorderDash: borderDash, glBorderDashOffset: borderDashOffset, rotation: -1 * labelRotationRadians, label: label, major: tick.major, textBaseline: textBaseline, textAlign: textAlign }); }); // Draw all of the tick labels, tick marks, and grid lines at the correct places helpers.each(itemsToDraw, function(itemToDraw) { if (gridLines.display) { context.save(); context.lineWidth = itemToDraw.glWidth; context.strokeStyle = itemToDraw.glColor; if (context.setLineDash) { context.setLineDash(itemToDraw.glBorderDash); context.lineDashOffset = itemToDraw.glBorderDashOffset; } context.beginPath(); if (gridLines.drawTicks) { context.moveTo(itemToDraw.tx1, itemToDraw.ty1); context.lineTo(itemToDraw.tx2, itemToDraw.ty2); } if (gridLines.drawOnChartArea) { context.moveTo(itemToDraw.x1, itemToDraw.y1); context.lineTo(itemToDraw.x2, itemToDraw.y2); } context.stroke(); context.restore(); } if (optionTicks.display) { // Make sure we draw text in the correct color and font context.save(); context.translate(itemToDraw.labelX, itemToDraw.labelY); context.rotate(itemToDraw.rotation); context.font = itemToDraw.major ? majorTickFont.font : tickFont.font; context.fillStyle = itemToDraw.major ? majorTickFontColor : tickFontColor; context.textBaseline = itemToDraw.textBaseline; context.textAlign = itemToDraw.textAlign; var label = itemToDraw.label; if (helpers.isArray(label)) { for (var i = 0, y = 0; i < label.length; ++i) { // We just make sure the multiline element is a string here.. context.fillText('' + label[i], 0, y); // apply same lineSpacing as calculated @ L#320 y += (tickFont.size * 1.5); } } else { context.fillText(label, 0, 0); } context.restore(); } }); if (scaleLabel.display) { // Draw the scale label var scaleLabelX; var scaleLabelY; var rotation = 0; var halfLineHeight = parseLineHeight(scaleLabel) / 2; if (isHorizontal) { scaleLabelX = me.left + ((me.right - me.left) / 2); // midpoint of the width scaleLabelY = options.position === 'bottom' ? me.bottom - halfLineHeight - scaleLabelPadding.bottom : me.top + halfLineHeight + scaleLabelPadding.top; } else { var isLeft = options.position === 'left'; scaleLabelX = isLeft ? me.left + halfLineHeight + scaleLabelPadding.top : me.right - halfLineHeight - scaleLabelPadding.top; scaleLabelY = me.top + ((me.bottom - me.top) / 2); rotation = isLeft ? -0.5 * Math.PI : 0.5 * Math.PI; } context.save(); context.translate(scaleLabelX, scaleLabelY); context.rotate(rotation); context.textAlign = 'center'; context.textBaseline = 'middle'; context.fillStyle = scaleLabelFontColor; // render in correct colour context.font = scaleLabelFont.font; context.fillText(scaleLabel.labelString, 0, 0); context.restore(); } if (gridLines.drawBorder) { // Draw the line at the edge of the axis context.lineWidth = helpers.valueAtIndexOrDefault(gridLines.lineWidth, 0); context.strokeStyle = helpers.valueAtIndexOrDefault(gridLines.color, 0); var x1 = me.left; var x2 = me.right; var y1 = me.top; var y2 = me.bottom; var aliasPixel = helpers.aliasPixel(context.lineWidth); if (isHorizontal) { y1 = y2 = options.position === 'top' ? me.bottom : me.top; y1 += aliasPixel; y2 += aliasPixel; } else { x1 = x2 = options.position === 'left' ? me.right : me.left; x1 += aliasPixel; x2 += aliasPixel; } context.beginPath(); context.moveTo(x1, y1); context.lineTo(x2, y2); context.stroke(); } } }); }; },{"25":25,"26":26,"34":34,"45":45}],33:[function(require,module,exports){ 'use strict'; var defaults = require(25); var helpers = require(45); module.exports = function(Chart) { Chart.scaleService = { // Scale registration object. Extensions can register new scale types (such as log or DB scales) and then // use the new chart options to grab the correct scale constructors: {}, // Use a registration function so that we can move to an ES6 map when we no longer need to support // old browsers // Scale config defaults defaults: {}, registerScaleType: function(type, scaleConstructor, scaleDefaults) { this.constructors[type] = scaleConstructor; this.defaults[type] = helpers.clone(scaleDefaults); }, getScaleConstructor: function(type) { return this.constructors.hasOwnProperty(type) ? this.constructors[type] : undefined; }, getScaleDefaults: function(type) { // Return the scale defaults merged with the global settings so that we always use the latest ones return this.defaults.hasOwnProperty(type) ? helpers.merge({}, [defaults.scale, this.defaults[type]]) : {}; }, updateScaleDefaults: function(type, additions) { var me = this; if (me.defaults.hasOwnProperty(type)) { me.defaults[type] = helpers.extend(me.defaults[type], additions); } }, addScalesToLayout: function(chart) { // Adds each scale to the chart.boxes array to be sized accordingly helpers.each(chart.scales, function(scale) { // Set ILayoutItem parameters for backwards compatibility scale.fullWidth = scale.options.fullWidth; scale.position = scale.options.position; scale.weight = scale.options.weight; Chart.layoutService.addBox(chart, scale); }); } }; }; },{"25":25,"45":45}],34:[function(require,module,exports){ 'use strict'; var helpers = require(45); /** * Namespace to hold static tick generation functions * @namespace Chart.Ticks */ module.exports = { /** * Namespace to hold generators for different types of ticks * @namespace Chart.Ticks.generators */ generators: { /** * Interface for the options provided to the numeric tick generator * @interface INumericTickGenerationOptions */ /** * The maximum number of ticks to display * @name INumericTickGenerationOptions#maxTicks * @type Number */ /** * The distance between each tick. * @name INumericTickGenerationOptions#stepSize * @type Number * @optional */ /** * Forced minimum for the ticks. If not specified, the minimum of the data range is used to calculate the tick minimum * @name INumericTickGenerationOptions#min * @type Number * @optional */ /** * The maximum value of the ticks. If not specified, the maximum of the data range is used to calculate the tick maximum * @name INumericTickGenerationOptions#max * @type Number * @optional */ /** * Generate a set of linear ticks * @method Chart.Ticks.generators.linear * @param generationOptions {INumericTickGenerationOptions} the options used to generate the ticks * @param dataRange {IRange} the range of the data * @returns {Array} array of tick values */ linear: function(generationOptions, dataRange) { var ticks = []; // To get a "nice" value for the tick spacing, we will use the appropriately named // "nice number" algorithm. See http://stackoverflow.com/questions/8506881/nice-label-algorithm-for-charts-with-minimum-ticks // for details. var spacing; if (generationOptions.stepSize && generationOptions.stepSize > 0) { spacing = generationOptions.stepSize; } else { var niceRange = helpers.niceNum(dataRange.max - dataRange.min, false); spacing = helpers.niceNum(niceRange / (generationOptions.maxTicks - 1), true); } var niceMin = Math.floor(dataRange.min / spacing) * spacing; var niceMax = Math.ceil(dataRange.max / spacing) * spacing; // If min, max and stepSize is set and they make an evenly spaced scale use it. if (generationOptions.min && generationOptions.max && generationOptions.stepSize) { // If very close to our whole number, use it. if (helpers.almostWhole((generationOptions.max - generationOptions.min) / generationOptions.stepSize, spacing / 1000)) { niceMin = generationOptions.min; niceMax = generationOptions.max; } } var numSpaces = (niceMax - niceMin) / spacing; // If very close to our rounded value, use it. if (helpers.almostEquals(numSpaces, Math.round(numSpaces), spacing / 1000)) { numSpaces = Math.round(numSpaces); } else { numSpaces = Math.ceil(numSpaces); } // Put the values into the ticks array ticks.push(generationOptions.min !== undefined ? generationOptions.min : niceMin); for (var j = 1; j < numSpaces; ++j) { ticks.push(niceMin + (j * spacing)); } ticks.push(generationOptions.max !== undefined ? generationOptions.max : niceMax); return ticks; }, /** * Generate a set of logarithmic ticks * @method Chart.Ticks.generators.logarithmic * @param generationOptions {INumericTickGenerationOptions} the options used to generate the ticks * @param dataRange {IRange} the range of the data * @returns {Array} array of tick values */ logarithmic: function(generationOptions, dataRange) { var ticks = []; var valueOrDefault = helpers.valueOrDefault; // Figure out what the max number of ticks we can support it is based on the size of // the axis area. For now, we say that the minimum tick spacing in pixels must be 50 // We also limit the maximum number of ticks to 11 which gives a nice 10 squares on // the graph var tickVal = valueOrDefault(generationOptions.min, Math.pow(10, Math.floor(helpers.log10(dataRange.min)))); var endExp = Math.floor(helpers.log10(dataRange.max)); var endSignificand = Math.ceil(dataRange.max / Math.pow(10, endExp)); var exp, significand; if (tickVal === 0) { exp = Math.floor(helpers.log10(dataRange.minNotZero)); significand = Math.floor(dataRange.minNotZero / Math.pow(10, exp)); ticks.push(tickVal); tickVal = significand * Math.pow(10, exp); } else { exp = Math.floor(helpers.log10(tickVal)); significand = Math.floor(tickVal / Math.pow(10, exp)); } do { ticks.push(tickVal); ++significand; if (significand === 10) { significand = 1; ++exp; } tickVal = significand * Math.pow(10, exp); } while (exp < endExp || (exp === endExp && significand < endSignificand)); var lastTick = valueOrDefault(generationOptions.max, tickVal); ticks.push(lastTick); return ticks; } }, /** * Namespace to hold formatters for different types of ticks * @namespace Chart.Ticks.formatters */ formatters: { /** * Formatter for value labels * @method Chart.Ticks.formatters.values * @param value the value to display * @return {String|Array} the label to display */ values: function(value) { return helpers.isArray(value) ? value : '' + value; }, /** * Formatter for linear numeric ticks * @method Chart.Ticks.formatters.linear * @param tickValue {Number} the value to be formatted * @param index {Number} the position of the tickValue parameter in the ticks array * @param ticks {Array} the list of ticks being converted * @return {String} string representation of the tickValue parameter */ linear: function(tickValue, index, ticks) { // If we have lots of ticks, don't use the ones var delta = ticks.length > 3 ? ticks[2] - ticks[1] : ticks[1] - ticks[0]; // If we have a number like 2.5 as the delta, figure out how many decimal places we need if (Math.abs(delta) > 1) { if (tickValue !== Math.floor(tickValue)) { // not an integer delta = tickValue - Math.floor(tickValue); } } var logDelta = helpers.log10(Math.abs(delta)); var tickString = ''; if (tickValue !== 0) { var numDecimal = -1 * Math.floor(logDelta); numDecimal = Math.max(Math.min(numDecimal, 20), 0); // toFixed has a max of 20 decimal places tickString = tickValue.toFixed(numDecimal); } else { tickString = '0'; // never show decimal places for 0 } return tickString; }, logarithmic: function(tickValue, index, ticks) { var remain = tickValue / (Math.pow(10, Math.floor(helpers.log10(tickValue)))); if (tickValue === 0) { return '0'; } else if (remain === 1 || remain === 2 || remain === 5 || index === 0 || index === ticks.length - 1) { return tickValue.toExponential(); } return ''; } } }; },{"45":45}],35:[function(require,module,exports){ 'use strict'; var defaults = require(25); var Element = require(26); var helpers = require(45); defaults._set('global', { tooltips: { enabled: true, custom: null, mode: 'nearest', position: 'average', intersect: true, backgroundColor: 'rgba(0,0,0,0.8)', titleFontStyle: 'bold', titleSpacing: 2, titleMarginBottom: 6, titleFontColor: '#fff', titleAlign: 'left', bodySpacing: 2, bodyFontColor: '#fff', bodyAlign: 'left', footerFontStyle: 'bold', footerSpacing: 2, footerMarginTop: 6, footerFontColor: '#fff', footerAlign: 'left', yPadding: 6, xPadding: 6, caretPadding: 2, caretSize: 5, cornerRadius: 6, multiKeyBackground: '#fff', displayColors: true, borderColor: 'rgba(0,0,0,0)', borderWidth: 0, callbacks: { // Args are: (tooltipItems, data) beforeTitle: helpers.noop, title: function(tooltipItems, data) { // Pick first xLabel for now var title = ''; var labels = data.labels; var labelCount = labels ? labels.length : 0; if (tooltipItems.length > 0) { var item = tooltipItems[0]; if (item.xLabel) { title = item.xLabel; } else if (labelCount > 0 && item.index < labelCount) { title = labels[item.index]; } } return title; }, afterTitle: helpers.noop, // Args are: (tooltipItems, data) beforeBody: helpers.noop, // Args are: (tooltipItem, data) beforeLabel: helpers.noop, label: function(tooltipItem, data) { var label = data.datasets[tooltipItem.datasetIndex].label || ''; if (label) { label += ': '; } label += tooltipItem.yLabel; return label; }, labelColor: function(tooltipItem, chart) { var meta = chart.getDatasetMeta(tooltipItem.datasetIndex); var activeElement = meta.data[tooltipItem.index]; var view = activeElement._view; return { borderColor: view.borderColor, backgroundColor: view.backgroundColor }; }, labelTextColor: function() { return this._options.bodyFontColor; }, afterLabel: helpers.noop, // Args are: (tooltipItems, data) afterBody: helpers.noop, // Args are: (tooltipItems, data) beforeFooter: helpers.noop, footer: helpers.noop, afterFooter: helpers.noop } } }); module.exports = function(Chart) { /** * Helper method to merge the opacity into a color */ function mergeOpacity(colorString, opacity) { var color = helpers.color(colorString); return color.alpha(opacity * color.alpha()).rgbaString(); } // Helper to push or concat based on if the 2nd parameter is an array or not function pushOrConcat(base, toPush) { if (toPush) { if (helpers.isArray(toPush)) { // base = base.concat(toPush); Array.prototype.push.apply(base, toPush); } else { base.push(toPush); } } return base; } // Private helper to create a tooltip item model // @param element : the chart element (point, arc, bar) to create the tooltip item for // @return : new tooltip item function createTooltipItem(element) { var xScale = element._xScale; var yScale = element._yScale || element._scale; // handle radar || polarArea charts var index = element._index; var datasetIndex = element._datasetIndex; return { xLabel: xScale ? xScale.getLabelForIndex(index, datasetIndex) : '', yLabel: yScale ? yScale.getLabelForIndex(index, datasetIndex) : '', index: index, datasetIndex: datasetIndex, x: element._model.x, y: element._model.y }; } /** * Helper to get the reset model for the tooltip * @param tooltipOpts {Object} the tooltip options */ function getBaseModel(tooltipOpts) { var globalDefaults = defaults.global; var valueOrDefault = helpers.valueOrDefault; return { // Positioning xPadding: tooltipOpts.xPadding, yPadding: tooltipOpts.yPadding, xAlign: tooltipOpts.xAlign, yAlign: tooltipOpts.yAlign, // Body bodyFontColor: tooltipOpts.bodyFontColor, _bodyFontFamily: valueOrDefault(tooltipOpts.bodyFontFamily, globalDefaults.defaultFontFamily), _bodyFontStyle: valueOrDefault(tooltipOpts.bodyFontStyle, globalDefaults.defaultFontStyle), _bodyAlign: tooltipOpts.bodyAlign, bodyFontSize: valueOrDefault(tooltipOpts.bodyFontSize, globalDefaults.defaultFontSize), bodySpacing: tooltipOpts.bodySpacing, // Title titleFontColor: tooltipOpts.titleFontColor, _titleFontFamily: valueOrDefault(tooltipOpts.titleFontFamily, globalDefaults.defaultFontFamily), _titleFontStyle: valueOrDefault(tooltipOpts.titleFontStyle, globalDefaults.defaultFontStyle), titleFontSize: valueOrDefault(tooltipOpts.titleFontSize, globalDefaults.defaultFontSize), _titleAlign: tooltipOpts.titleAlign, titleSpacing: tooltipOpts.titleSpacing, titleMarginBottom: tooltipOpts.titleMarginBottom, // Footer footerFontColor: tooltipOpts.footerFontColor, _footerFontFamily: valueOrDefault(tooltipOpts.footerFontFamily, globalDefaults.defaultFontFamily), _footerFontStyle: valueOrDefault(tooltipOpts.footerFontStyle, globalDefaults.defaultFontStyle), footerFontSize: valueOrDefault(tooltipOpts.footerFontSize, globalDefaults.defaultFontSize), _footerAlign: tooltipOpts.footerAlign, footerSpacing: tooltipOpts.footerSpacing, footerMarginTop: tooltipOpts.footerMarginTop, // Appearance caretSize: tooltipOpts.caretSize, cornerRadius: tooltipOpts.cornerRadius, backgroundColor: tooltipOpts.backgroundColor, opacity: 0, legendColorBackground: tooltipOpts.multiKeyBackground, displayColors: tooltipOpts.displayColors, borderColor: tooltipOpts.borderColor, borderWidth: tooltipOpts.borderWidth }; } /** * Get the size of the tooltip */ function getTooltipSize(tooltip, model) { var ctx = tooltip._chart.ctx; var height = model.yPadding * 2; // Tooltip Padding var width = 0; // Count of all lines in the body var body = model.body; var combinedBodyLength = body.reduce(function(count, bodyItem) { return count + bodyItem.before.length + bodyItem.lines.length + bodyItem.after.length; }, 0); combinedBodyLength += model.beforeBody.length + model.afterBody.length; var titleLineCount = model.title.length; var footerLineCount = model.footer.length; var titleFontSize = model.titleFontSize; var bodyFontSize = model.bodyFontSize; var footerFontSize = model.footerFontSize; height += titleLineCount * titleFontSize; // Title Lines height += titleLineCount ? (titleLineCount - 1) * model.titleSpacing : 0; // Title Line Spacing height += titleLineCount ? model.titleMarginBottom : 0; // Title's bottom Margin height += combinedBodyLength * bodyFontSize; // Body Lines height += combinedBodyLength ? (combinedBodyLength - 1) * model.bodySpacing : 0; // Body Line Spacing height += footerLineCount ? model.footerMarginTop : 0; // Footer Margin height += footerLineCount * (footerFontSize); // Footer Lines height += footerLineCount ? (footerLineCount - 1) * model.footerSpacing : 0; // Footer Line Spacing // Title width var widthPadding = 0; var maxLineWidth = function(line) { width = Math.max(width, ctx.measureText(line).width + widthPadding); }; ctx.font = helpers.fontString(titleFontSize, model._titleFontStyle, model._titleFontFamily); helpers.each(model.title, maxLineWidth); // Body width ctx.font = helpers.fontString(bodyFontSize, model._bodyFontStyle, model._bodyFontFamily); helpers.each(model.beforeBody.concat(model.afterBody), maxLineWidth); // Body lines may include some extra width due to the color box widthPadding = model.displayColors ? (bodyFontSize + 2) : 0; helpers.each(body, function(bodyItem) { helpers.each(bodyItem.before, maxLineWidth); helpers.each(bodyItem.lines, maxLineWidth); helpers.each(bodyItem.after, maxLineWidth); }); // Reset back to 0 widthPadding = 0; // Footer width ctx.font = helpers.fontString(footerFontSize, model._footerFontStyle, model._footerFontFamily); helpers.each(model.footer, maxLineWidth); // Add padding width += 2 * model.xPadding; return { width: width, height: height }; } /** * Helper to get the alignment of a tooltip given the size */ function determineAlignment(tooltip, size) { var model = tooltip._model; var chart = tooltip._chart; var chartArea = tooltip._chart.chartArea; var xAlign = 'center'; var yAlign = 'center'; if (model.y < size.height) { yAlign = 'top'; } else if (model.y > (chart.height - size.height)) { yAlign = 'bottom'; } var lf, rf; // functions to determine left, right alignment var olf, orf; // functions to determine if left/right alignment causes tooltip to go outside chart var yf; // function to get the y alignment if the tooltip goes outside of the left or right edges var midX = (chartArea.left + chartArea.right) / 2; var midY = (chartArea.top + chartArea.bottom) / 2; if (yAlign === 'center') { lf = function(x) { return x <= midX; }; rf = function(x) { return x > midX; }; } else { lf = function(x) { return x <= (size.width / 2); }; rf = function(x) { return x >= (chart.width - (size.width / 2)); }; } olf = function(x) { return x + size.width > chart.width; }; orf = function(x) { return x - size.width < 0; }; yf = function(y) { return y <= midY ? 'top' : 'bottom'; }; if (lf(model.x)) { xAlign = 'left'; // Is tooltip too wide and goes over the right side of the chart.? if (olf(model.x)) { xAlign = 'center'; yAlign = yf(model.y); } } else if (rf(model.x)) { xAlign = 'right'; // Is tooltip too wide and goes outside left edge of canvas? if (orf(model.x)) { xAlign = 'center'; yAlign = yf(model.y); } } var opts = tooltip._options; return { xAlign: opts.xAlign ? opts.xAlign : xAlign, yAlign: opts.yAlign ? opts.yAlign : yAlign }; } /** * @Helper to get the location a tooltip needs to be placed at given the initial position (via the vm) and the size and alignment */ function getBackgroundPoint(vm, size, alignment) { // Background Position var x = vm.x; var y = vm.y; var caretSize = vm.caretSize; var caretPadding = vm.caretPadding; var cornerRadius = vm.cornerRadius; var xAlign = alignment.xAlign; var yAlign = alignment.yAlign; var paddingAndSize = caretSize + caretPadding; var radiusAndPadding = cornerRadius + caretPadding; if (xAlign === 'right') { x -= size.width; } else if (xAlign === 'center') { x -= (size.width / 2); } if (yAlign === 'top') { y += paddingAndSize; } else if (yAlign === 'bottom') { y -= size.height + paddingAndSize; } else { y -= (size.height / 2); } if (yAlign === 'center') { if (xAlign === 'left') { x += paddingAndSize; } else if (xAlign === 'right') { x -= paddingAndSize; } } else if (xAlign === 'left') { x -= radiusAndPadding; } else if (xAlign === 'right') { x += radiusAndPadding; } return { x: x, y: y }; } Chart.Tooltip = Element.extend({ initialize: function() { this._model = getBaseModel(this._options); this._lastActive = []; }, // Get the title // Args are: (tooltipItem, data) getTitle: function() { var me = this; var opts = me._options; var callbacks = opts.callbacks; var beforeTitle = callbacks.beforeTitle.apply(me, arguments); var title = callbacks.title.apply(me, arguments); var afterTitle = callbacks.afterTitle.apply(me, arguments); var lines = []; lines = pushOrConcat(lines, beforeTitle); lines = pushOrConcat(lines, title); lines = pushOrConcat(lines, afterTitle); return lines; }, // Args are: (tooltipItem, data) getBeforeBody: function() { var lines = this._options.callbacks.beforeBody.apply(this, arguments); return helpers.isArray(lines) ? lines : lines !== undefined ? [lines] : []; }, // Args are: (tooltipItem, data) getBody: function(tooltipItems, data) { var me = this; var callbacks = me._options.callbacks; var bodyItems = []; helpers.each(tooltipItems, function(tooltipItem) { var bodyItem = { before: [], lines: [], after: [] }; pushOrConcat(bodyItem.before, callbacks.beforeLabel.call(me, tooltipItem, data)); pushOrConcat(bodyItem.lines, callbacks.label.call(me, tooltipItem, data)); pushOrConcat(bodyItem.after, callbacks.afterLabel.call(me, tooltipItem, data)); bodyItems.push(bodyItem); }); return bodyItems; }, // Args are: (tooltipItem, data) getAfterBody: function() { var lines = this._options.callbacks.afterBody.apply(this, arguments); return helpers.isArray(lines) ? lines : lines !== undefined ? [lines] : []; }, // Get the footer and beforeFooter and afterFooter lines // Args are: (tooltipItem, data) getFooter: function() { var me = this; var callbacks = me._options.callbacks; var beforeFooter = callbacks.beforeFooter.apply(me, arguments); var footer = callbacks.footer.apply(me, arguments); var afterFooter = callbacks.afterFooter.apply(me, arguments); var lines = []; lines = pushOrConcat(lines, beforeFooter); lines = pushOrConcat(lines, footer); lines = pushOrConcat(lines, afterFooter); return lines; }, update: function(changed) { var me = this; var opts = me._options; // Need to regenerate the model because its faster than using extend and it is necessary due to the optimization in Chart.Element.transition // that does _view = _model if ease === 1. This causes the 2nd tooltip update to set properties in both the view and model at the same time // which breaks any animations. var existingModel = me._model; var model = me._model = getBaseModel(opts); var active = me._active; var data = me._data; // In the case where active.length === 0 we need to keep these at existing values for good animations var alignment = { xAlign: existingModel.xAlign, yAlign: existingModel.yAlign }; var backgroundPoint = { x: existingModel.x, y: existingModel.y }; var tooltipSize = { width: existingModel.width, height: existingModel.height }; var tooltipPosition = { x: existingModel.caretX, y: existingModel.caretY }; var i, len; if (active.length) { model.opacity = 1; var labelColors = []; var labelTextColors = []; tooltipPosition = Chart.Tooltip.positioners[opts.position].call(me, active, me._eventPosition); var tooltipItems = []; for (i = 0, len = active.length; i < len; ++i) { tooltipItems.push(createTooltipItem(active[i])); } // If the user provided a filter function, use it to modify the tooltip items if (opts.filter) { tooltipItems = tooltipItems.filter(function(a) { return opts.filter(a, data); }); } // If the user provided a sorting function, use it to modify the tooltip items if (opts.itemSort) { tooltipItems = tooltipItems.sort(function(a, b) { return opts.itemSort(a, b, data); }); } // Determine colors for boxes helpers.each(tooltipItems, function(tooltipItem) { labelColors.push(opts.callbacks.labelColor.call(me, tooltipItem, me._chart)); labelTextColors.push(opts.callbacks.labelTextColor.call(me, tooltipItem, me._chart)); }); // Build the Text Lines model.title = me.getTitle(tooltipItems, data); model.beforeBody = me.getBeforeBody(tooltipItems, data); model.body = me.getBody(tooltipItems, data); model.afterBody = me.getAfterBody(tooltipItems, data); model.footer = me.getFooter(tooltipItems, data); // Initial positioning and colors model.x = Math.round(tooltipPosition.x); model.y = Math.round(tooltipPosition.y); model.caretPadding = opts.caretPadding; model.labelColors = labelColors; model.labelTextColors = labelTextColors; // data points model.dataPoints = tooltipItems; // We need to determine alignment of the tooltip tooltipSize = getTooltipSize(this, model); alignment = determineAlignment(this, tooltipSize); // Final Size and Position backgroundPoint = getBackgroundPoint(model, tooltipSize, alignment); } else { model.opacity = 0; } model.xAlign = alignment.xAlign; model.yAlign = alignment.yAlign; model.x = backgroundPoint.x; model.y = backgroundPoint.y; model.width = tooltipSize.width; model.height = tooltipSize.height; // Point where the caret on the tooltip points to model.caretX = tooltipPosition.x; model.caretY = tooltipPosition.y; me._model = model; if (changed && opts.custom) { opts.custom.call(me, model); } return me; }, drawCaret: function(tooltipPoint, size) { var ctx = this._chart.ctx; var vm = this._view; var caretPosition = this.getCaretPosition(tooltipPoint, size, vm); ctx.lineTo(caretPosition.x1, caretPosition.y1); ctx.lineTo(caretPosition.x2, caretPosition.y2); ctx.lineTo(caretPosition.x3, caretPosition.y3); }, getCaretPosition: function(tooltipPoint, size, vm) { var x1, x2, x3, y1, y2, y3; var caretSize = vm.caretSize; var cornerRadius = vm.cornerRadius; var xAlign = vm.xAlign; var yAlign = vm.yAlign; var ptX = tooltipPoint.x; var ptY = tooltipPoint.y; var width = size.width; var height = size.height; if (yAlign === 'center') { y2 = ptY + (height / 2); if (xAlign === 'left') { x1 = ptX; x2 = x1 - caretSize; x3 = x1; y1 = y2 + caretSize; y3 = y2 - caretSize; } else { x1 = ptX + width; x2 = x1 + caretSize; x3 = x1; y1 = y2 - caretSize; y3 = y2 + caretSize; } } else { if (xAlign === 'left') { x2 = ptX + cornerRadius + (caretSize); x1 = x2 - caretSize; x3 = x2 + caretSize; } else if (xAlign === 'right') { x2 = ptX + width - cornerRadius - caretSize; x1 = x2 - caretSize; x3 = x2 + caretSize; } else { x2 = ptX + (width / 2); x1 = x2 - caretSize; x3 = x2 + caretSize; } if (yAlign === 'top') { y1 = ptY; y2 = y1 - caretSize; y3 = y1; } else { y1 = ptY + height; y2 = y1 + caretSize; y3 = y1; // invert drawing order var tmp = x3; x3 = x1; x1 = tmp; } } return {x1: x1, x2: x2, x3: x3, y1: y1, y2: y2, y3: y3}; }, drawTitle: function(pt, vm, ctx, opacity) { var title = vm.title; if (title.length) { ctx.textAlign = vm._titleAlign; ctx.textBaseline = 'top'; var titleFontSize = vm.titleFontSize; var titleSpacing = vm.titleSpacing; ctx.fillStyle = mergeOpacity(vm.titleFontColor, opacity); ctx.font = helpers.fontString(titleFontSize, vm._titleFontStyle, vm._titleFontFamily); var i, len; for (i = 0, len = title.length; i < len; ++i) { ctx.fillText(title[i], pt.x, pt.y); pt.y += titleFontSize + titleSpacing; // Line Height and spacing if (i + 1 === title.length) { pt.y += vm.titleMarginBottom - titleSpacing; // If Last, add margin, remove spacing } } } }, drawBody: function(pt, vm, ctx, opacity) { var bodyFontSize = vm.bodyFontSize; var bodySpacing = vm.bodySpacing; var body = vm.body; ctx.textAlign = vm._bodyAlign; ctx.textBaseline = 'top'; ctx.font = helpers.fontString(bodyFontSize, vm._bodyFontStyle, vm._bodyFontFamily); // Before Body var xLinePadding = 0; var fillLineOfText = function(line) { ctx.fillText(line, pt.x + xLinePadding, pt.y); pt.y += bodyFontSize + bodySpacing; }; // Before body lines ctx.fillStyle = mergeOpacity(vm.bodyFontColor, opacity); helpers.each(vm.beforeBody, fillLineOfText); var drawColorBoxes = vm.displayColors; xLinePadding = drawColorBoxes ? (bodyFontSize + 2) : 0; // Draw body lines now helpers.each(body, function(bodyItem, i) { var textColor = mergeOpacity(vm.labelTextColors[i], opacity); ctx.fillStyle = textColor; helpers.each(bodyItem.before, fillLineOfText); helpers.each(bodyItem.lines, function(line) { // Draw Legend-like boxes if needed if (drawColorBoxes) { // Fill a white rect so that colours merge nicely if the opacity is < 1 ctx.fillStyle = mergeOpacity(vm.legendColorBackground, opacity); ctx.fillRect(pt.x, pt.y, bodyFontSize, bodyFontSize); // Border ctx.lineWidth = 1; ctx.strokeStyle = mergeOpacity(vm.labelColors[i].borderColor, opacity); ctx.strokeRect(pt.x, pt.y, bodyFontSize, bodyFontSize); // Inner square ctx.fillStyle = mergeOpacity(vm.labelColors[i].backgroundColor, opacity); ctx.fillRect(pt.x + 1, pt.y + 1, bodyFontSize - 2, bodyFontSize - 2); ctx.fillStyle = textColor; } fillLineOfText(line); }); helpers.each(bodyItem.after, fillLineOfText); }); // Reset back to 0 for after body xLinePadding = 0; // After body lines helpers.each(vm.afterBody, fillLineOfText); pt.y -= bodySpacing; // Remove last body spacing }, drawFooter: function(pt, vm, ctx, opacity) { var footer = vm.footer; if (footer.length) { pt.y += vm.footerMarginTop; ctx.textAlign = vm._footerAlign; ctx.textBaseline = 'top'; ctx.fillStyle = mergeOpacity(vm.footerFontColor, opacity); ctx.font = helpers.fontString(vm.footerFontSize, vm._footerFontStyle, vm._footerFontFamily); helpers.each(footer, function(line) { ctx.fillText(line, pt.x, pt.y); pt.y += vm.footerFontSize + vm.footerSpacing; }); } }, drawBackground: function(pt, vm, ctx, tooltipSize, opacity) { ctx.fillStyle = mergeOpacity(vm.backgroundColor, opacity); ctx.strokeStyle = mergeOpacity(vm.borderColor, opacity); ctx.lineWidth = vm.borderWidth; var xAlign = vm.xAlign; var yAlign = vm.yAlign; var x = pt.x; var y = pt.y; var width = tooltipSize.width; var height = tooltipSize.height; var radius = vm.cornerRadius; ctx.beginPath(); ctx.moveTo(x + radius, y); if (yAlign === 'top') { this.drawCaret(pt, tooltipSize); } ctx.lineTo(x + width - radius, y); ctx.quadraticCurveTo(x + width, y, x + width, y + radius); if (yAlign === 'center' && xAlign === 'right') { this.drawCaret(pt, tooltipSize); } ctx.lineTo(x + width, y + height - radius); ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height); if (yAlign === 'bottom') { this.drawCaret(pt, tooltipSize); } ctx.lineTo(x + radius, y + height); ctx.quadraticCurveTo(x, y + height, x, y + height - radius); if (yAlign === 'center' && xAlign === 'left') { this.drawCaret(pt, tooltipSize); } ctx.lineTo(x, y + radius); ctx.quadraticCurveTo(x, y, x + radius, y); ctx.closePath(); ctx.fill(); if (vm.borderWidth > 0) { ctx.stroke(); } }, draw: function() { var ctx = this._chart.ctx; var vm = this._view; if (vm.opacity === 0) { return; } var tooltipSize = { width: vm.width, height: vm.height }; var pt = { x: vm.x, y: vm.y }; // IE11/Edge does not like very small opacities, so snap to 0 var opacity = Math.abs(vm.opacity < 1e-3) ? 0 : vm.opacity; // Truthy/falsey value for empty tooltip var hasTooltipContent = vm.title.length || vm.beforeBody.length || vm.body.length || vm.afterBody.length || vm.footer.length; if (this._options.enabled && hasTooltipContent) { // Draw Background this.drawBackground(pt, vm, ctx, tooltipSize, opacity); // Draw Title, Body, and Footer pt.x += vm.xPadding; pt.y += vm.yPadding; // Titles this.drawTitle(pt, vm, ctx, opacity); // Body this.drawBody(pt, vm, ctx, opacity); // Footer this.drawFooter(pt, vm, ctx, opacity); } }, /** * Handle an event * @private * @param {IEvent} event - The event to handle * @returns {Boolean} true if the tooltip changed */ handleEvent: function(e) { var me = this; var options = me._options; var changed = false; me._lastActive = me._lastActive || []; // Find Active Elements for tooltips if (e.type === 'mouseout') { me._active = []; } else { me._active = me._chart.getElementsAtEventForMode(e, options.mode, options); } // Remember Last Actives changed = !helpers.arrayEquals(me._active, me._lastActive); // If tooltip didn't change, do not handle the target event if (!changed) { return false; } me._lastActive = me._active; if (options.enabled || options.custom) { me._eventPosition = { x: e.x, y: e.y }; var model = me._model; me.update(true); me.pivot(); // See if our tooltip position changed changed |= (model.x !== me._model.x) || (model.y !== me._model.y); } return changed; } }); /** * @namespace Chart.Tooltip.positioners */ Chart.Tooltip.positioners = { /** * Average mode places the tooltip at the average position of the elements shown * @function Chart.Tooltip.positioners.average * @param elements {ChartElement[]} the elements being displayed in the tooltip * @returns {Point} tooltip position */ average: function(elements) { if (!elements.length) { return false; } var i, len; var x = 0; var y = 0; var count = 0; for (i = 0, len = elements.length; i < len; ++i) { var el = elements[i]; if (el && el.hasValue()) { var pos = el.tooltipPosition(); x += pos.x; y += pos.y; ++count; } } return { x: Math.round(x / count), y: Math.round(y / count) }; }, /** * Gets the tooltip position nearest of the item nearest to the event position * @function Chart.Tooltip.positioners.nearest * @param elements {Chart.Element[]} the tooltip elements * @param eventPosition {Point} the position of the event in canvas coordinates * @returns {Point} the tooltip position */ nearest: function(elements, eventPosition) { var x = eventPosition.x; var y = eventPosition.y; var minDistance = Number.POSITIVE_INFINITY; var i, len, nearestElement; for (i = 0, len = elements.length; i < len; ++i) { var el = elements[i]; if (el && el.hasValue()) { var center = el.getCenterPoint(); var d = helpers.distanceBetweenPoints(eventPosition, center); if (d < minDistance) { minDistance = d; nearestElement = el; } } } if (nearestElement) { var tp = nearestElement.tooltipPosition(); x = tp.x; y = tp.y; } return { x: x, y: y }; } }; }; },{"25":25,"26":26,"45":45}],36:[function(require,module,exports){ 'use strict'; var defaults = require(25); var Element = require(26); var helpers = require(45); defaults._set('global', { elements: { arc: { backgroundColor: defaults.global.defaultColor, borderColor: '#fff', borderWidth: 2 } } }); module.exports = Element.extend({ inLabelRange: function(mouseX) { var vm = this._view; if (vm) { return (Math.pow(mouseX - vm.x, 2) < Math.pow(vm.radius + vm.hoverRadius, 2)); } return false; }, inRange: function(chartX, chartY) { var vm = this._view; if (vm) { var pointRelativePosition = helpers.getAngleFromPoint(vm, {x: chartX, y: chartY}); var angle = pointRelativePosition.angle; var distance = pointRelativePosition.distance; // Sanitise angle range var startAngle = vm.startAngle; var endAngle = vm.endAngle; while (endAngle < startAngle) { endAngle += 2.0 * Math.PI; } while (angle > endAngle) { angle -= 2.0 * Math.PI; } while (angle < startAngle) { angle += 2.0 * Math.PI; } // Check if within the range of the open/close angle var betweenAngles = (angle >= startAngle && angle <= endAngle); var withinRadius = (distance >= vm.innerRadius && distance <= vm.outerRadius); return (betweenAngles && withinRadius); } return false; }, getCenterPoint: function() { var vm = this._view; var halfAngle = (vm.startAngle + vm.endAngle) / 2; var halfRadius = (vm.innerRadius + vm.outerRadius) / 2; return { x: vm.x + Math.cos(halfAngle) * halfRadius, y: vm.y + Math.sin(halfAngle) * halfRadius }; }, getArea: function() { var vm = this._view; return Math.PI * ((vm.endAngle - vm.startAngle) / (2 * Math.PI)) * (Math.pow(vm.outerRadius, 2) - Math.pow(vm.innerRadius, 2)); }, tooltipPosition: function() { var vm = this._view; var centreAngle = vm.startAngle + ((vm.endAngle - vm.startAngle) / 2); var rangeFromCentre = (vm.outerRadius - vm.innerRadius) / 2 + vm.innerRadius; return { x: vm.x + (Math.cos(centreAngle) * rangeFromCentre), y: vm.y + (Math.sin(centreAngle) * rangeFromCentre) }; }, draw: function() { var ctx = this._chart.ctx; var vm = this._view; var sA = vm.startAngle; var eA = vm.endAngle; ctx.beginPath(); ctx.arc(vm.x, vm.y, vm.outerRadius, sA, eA); ctx.arc(vm.x, vm.y, vm.innerRadius, eA, sA, true); ctx.closePath(); ctx.strokeStyle = vm.borderColor; ctx.lineWidth = vm.borderWidth; ctx.fillStyle = vm.backgroundColor; ctx.fill(); ctx.lineJoin = 'bevel'; if (vm.borderWidth) { ctx.stroke(); } } }); },{"25":25,"26":26,"45":45}],37:[function(require,module,exports){ 'use strict'; var defaults = require(25); var Element = require(26); var helpers = require(45); var globalDefaults = defaults.global; defaults._set('global', { elements: { line: { tension: 0.4, backgroundColor: globalDefaults.defaultColor, borderWidth: 3, borderColor: globalDefaults.defaultColor, borderCapStyle: 'butt', borderDash: [], borderDashOffset: 0.0, borderJoinStyle: 'miter', capBezierPoints: true, fill: true, // do we fill in the area between the line and its base axis } } }); module.exports = Element.extend({ draw: function() { var me = this; var vm = me._view; var ctx = me._chart.ctx; var spanGaps = vm.spanGaps; var points = me._children.slice(); // clone array var globalOptionLineElements = globalDefaults.elements.line; var lastDrawnIndex = -1; var index, current, previous, currentVM; // If we are looping, adding the first point again if (me._loop && points.length) { points.push(points[0]); } ctx.save(); // Stroke Line Options ctx.lineCap = vm.borderCapStyle || globalOptionLineElements.borderCapStyle; // IE 9 and 10 do not support line dash if (ctx.setLineDash) { ctx.setLineDash(vm.borderDash || globalOptionLineElements.borderDash); } ctx.lineDashOffset = vm.borderDashOffset || globalOptionLineElements.borderDashOffset; ctx.lineJoin = vm.borderJoinStyle || globalOptionLineElements.borderJoinStyle; ctx.lineWidth = vm.borderWidth || globalOptionLineElements.borderWidth; ctx.strokeStyle = vm.borderColor || globalDefaults.defaultColor; // Stroke Line ctx.beginPath(); lastDrawnIndex = -1; for (index = 0; index < points.length; ++index) { current = points[index]; previous = helpers.previousItem(points, index); currentVM = current._view; // First point moves to it's starting position no matter what if (index === 0) { if (!currentVM.skip) { ctx.moveTo(currentVM.x, currentVM.y); lastDrawnIndex = index; } } else { previous = lastDrawnIndex === -1 ? previous : points[lastDrawnIndex]; if (!currentVM.skip) { if ((lastDrawnIndex !== (index - 1) && !spanGaps) || lastDrawnIndex === -1) { // There was a gap and this is the first point after the gap ctx.moveTo(currentVM.x, currentVM.y); } else { // Line to next point helpers.canvas.lineTo(ctx, previous._view, current._view); } lastDrawnIndex = index; } } } ctx.stroke(); ctx.restore(); } }); },{"25":25,"26":26,"45":45}],38:[function(require,module,exports){ 'use strict'; var defaults = require(25); var Element = require(26); var helpers = require(45); var defaultColor = defaults.global.defaultColor; defaults._set('global', { elements: { point: { radius: 3, pointStyle: 'circle', backgroundColor: defaultColor, borderColor: defaultColor, borderWidth: 1, // Hover hitRadius: 1, hoverRadius: 4, hoverBorderWidth: 1 } } }); function xRange(mouseX) { var vm = this._view; return vm ? (Math.pow(mouseX - vm.x, 2) < Math.pow(vm.radius + vm.hitRadius, 2)) : false; } function yRange(mouseY) { var vm = this._view; return vm ? (Math.pow(mouseY - vm.y, 2) < Math.pow(vm.radius + vm.hitRadius, 2)) : false; } module.exports = Element.extend({ inRange: function(mouseX, mouseY) { var vm = this._view; return vm ? ((Math.pow(mouseX - vm.x, 2) + Math.pow(mouseY - vm.y, 2)) < Math.pow(vm.hitRadius + vm.radius, 2)) : false; }, inLabelRange: xRange, inXRange: xRange, inYRange: yRange, getCenterPoint: function() { var vm = this._view; return { x: vm.x, y: vm.y }; }, getArea: function() { return Math.PI * Math.pow(this._view.radius, 2); }, tooltipPosition: function() { var vm = this._view; return { x: vm.x, y: vm.y, padding: vm.radius + vm.borderWidth }; }, draw: function(chartArea) { var vm = this._view; var model = this._model; var ctx = this._chart.ctx; var pointStyle = vm.pointStyle; var radius = vm.radius; var x = vm.x; var y = vm.y; var color = helpers.color; var errMargin = 1.01; // 1.01 is margin for Accumulated error. (Especially Edge, IE.) var ratio = 0; if (vm.skip) { return; } ctx.strokeStyle = vm.borderColor || defaultColor; ctx.lineWidth = helpers.valueOrDefault(vm.borderWidth, defaults.global.elements.point.borderWidth); ctx.fillStyle = vm.backgroundColor || defaultColor; // Cliping for Points. // going out from inner charArea? if ((chartArea !== undefined) && ((model.x < chartArea.left) || (chartArea.right * errMargin < model.x) || (model.y < chartArea.top) || (chartArea.bottom * errMargin < model.y))) { // Point fade out if (model.x < chartArea.left) { ratio = (x - model.x) / (chartArea.left - model.x); } else if (chartArea.right * errMargin < model.x) { ratio = (model.x - x) / (model.x - chartArea.right); } else if (model.y < chartArea.top) { ratio = (y - model.y) / (chartArea.top - model.y); } else if (chartArea.bottom * errMargin < model.y) { ratio = (model.y - y) / (model.y - chartArea.bottom); } ratio = Math.round(ratio * 100) / 100; ctx.strokeStyle = color(ctx.strokeStyle).alpha(ratio).rgbString(); ctx.fillStyle = color(ctx.fillStyle).alpha(ratio).rgbString(); } helpers.canvas.drawPoint(ctx, pointStyle, radius, x, y); } }); },{"25":25,"26":26,"45":45}],39:[function(require,module,exports){ 'use strict'; var defaults = require(25); var Element = require(26); defaults._set('global', { elements: { rectangle: { backgroundColor: defaults.global.defaultColor, borderColor: defaults.global.defaultColor, borderSkipped: 'bottom', borderWidth: 0 } } }); function isVertical(bar) { return bar._view.width !== undefined; } /** * Helper function to get the bounds of the bar regardless of the orientation * @param bar {Chart.Element.Rectangle} the bar * @return {Bounds} bounds of the bar * @private */ function getBarBounds(bar) { var vm = bar._view; var x1, x2, y1, y2; if (isVertical(bar)) { // vertical var halfWidth = vm.width / 2; x1 = vm.x - halfWidth; x2 = vm.x + halfWidth; y1 = Math.min(vm.y, vm.base); y2 = Math.max(vm.y, vm.base); } else { // horizontal bar var halfHeight = vm.height / 2; x1 = Math.min(vm.x, vm.base); x2 = Math.max(vm.x, vm.base); y1 = vm.y - halfHeight; y2 = vm.y + halfHeight; } return { left: x1, top: y1, right: x2, bottom: y2 }; } module.exports = Element.extend({ draw: function() { var ctx = this._chart.ctx; var vm = this._view; var left, right, top, bottom, signX, signY, borderSkipped; var borderWidth = vm.borderWidth; if (!vm.horizontal) { // bar left = vm.x - vm.width / 2; right = vm.x + vm.width / 2; top = vm.y; bottom = vm.base; signX = 1; signY = bottom > top ? 1 : -1; borderSkipped = vm.borderSkipped || 'bottom'; } else { // horizontal bar left = vm.base; right = vm.x; top = vm.y - vm.height / 2; bottom = vm.y + vm.height / 2; signX = right > left ? 1 : -1; signY = 1; borderSkipped = vm.borderSkipped || 'left'; } // Canvas doesn't allow us to stroke inside the width so we can // adjust the sizes to fit if we're setting a stroke on the line if (borderWidth) { // borderWidth shold be less than bar width and bar height. var barSize = Math.min(Math.abs(left - right), Math.abs(top - bottom)); borderWidth = borderWidth > barSize ? barSize : borderWidth; var halfStroke = borderWidth / 2; // Adjust borderWidth when bar top position is near vm.base(zero). var borderLeft = left + (borderSkipped !== 'left' ? halfStroke * signX : 0); var borderRight = right + (borderSkipped !== 'right' ? -halfStroke * signX : 0); var borderTop = top + (borderSkipped !== 'top' ? halfStroke * signY : 0); var borderBottom = bottom + (borderSkipped !== 'bottom' ? -halfStroke * signY : 0); // not become a vertical line? if (borderLeft !== borderRight) { top = borderTop; bottom = borderBottom; } // not become a horizontal line? if (borderTop !== borderBottom) { left = borderLeft; right = borderRight; } } ctx.beginPath(); ctx.fillStyle = vm.backgroundColor; ctx.strokeStyle = vm.borderColor; ctx.lineWidth = borderWidth; // Corner points, from bottom-left to bottom-right clockwise // | 1 2 | // | 0 3 | var corners = [ [left, bottom], [left, top], [right, top], [right, bottom] ]; // Find first (starting) corner with fallback to 'bottom' var borders = ['bottom', 'left', 'top', 'right']; var startCorner = borders.indexOf(borderSkipped, 0); if (startCorner === -1) { startCorner = 0; } function cornerAt(index) { return corners[(startCorner + index) % 4]; } // Draw rectangle from 'startCorner' var corner = cornerAt(0); ctx.moveTo(corner[0], corner[1]); for (var i = 1; i < 4; i++) { corner = cornerAt(i); ctx.lineTo(corner[0], corner[1]); } ctx.fill(); if (borderWidth) { ctx.stroke(); } }, height: function() { var vm = this._view; return vm.base - vm.y; }, inRange: function(mouseX, mouseY) { var inRange = false; if (this._view) { var bounds = getBarBounds(this); inRange = mouseX >= bounds.left && mouseX <= bounds.right && mouseY >= bounds.top && mouseY <= bounds.bottom; } return inRange; }, inLabelRange: function(mouseX, mouseY) { var me = this; if (!me._view) { return false; } var inRange = false; var bounds = getBarBounds(me); if (isVertical(me)) { inRange = mouseX >= bounds.left && mouseX <= bounds.right; } else { inRange = mouseY >= bounds.top && mouseY <= bounds.bottom; } return inRange; }, inXRange: function(mouseX) { var bounds = getBarBounds(this); return mouseX >= bounds.left && mouseX <= bounds.right; }, inYRange: function(mouseY) { var bounds = getBarBounds(this); return mouseY >= bounds.top && mouseY <= bounds.bottom; }, getCenterPoint: function() { var vm = this._view; var x, y; if (isVertical(this)) { x = vm.x; y = (vm.y + vm.base) / 2; } else { x = (vm.x + vm.base) / 2; y = vm.y; } return {x: x, y: y}; }, getArea: function() { var vm = this._view; return vm.width * Math.abs(vm.y - vm.base); }, tooltipPosition: function() { var vm = this._view; return { x: vm.x, y: vm.y }; } }); },{"25":25,"26":26}],40:[function(require,module,exports){ 'use strict'; module.exports = {}; module.exports.Arc = require(36); module.exports.Line = require(37); module.exports.Point = require(38); module.exports.Rectangle = require(39); },{"36":36,"37":37,"38":38,"39":39}],41:[function(require,module,exports){ 'use strict'; var helpers = require(42); /** * @namespace Chart.helpers.canvas */ var exports = module.exports = { /** * Clears the entire canvas associated to the given `chart`. * @param {Chart} chart - The chart for which to clear the canvas. */ clear: function(chart) { chart.ctx.clearRect(0, 0, chart.width, chart.height); }, /** * Creates a "path" for a rectangle with rounded corners at position (x, y) with a * given size (width, height) and the same `radius` for all corners. * @param {CanvasRenderingContext2D} ctx - The canvas 2D Context. * @param {Number} x - The x axis of the coordinate for the rectangle starting point. * @param {Number} y - The y axis of the coordinate for the rectangle starting point. * @param {Number} width - The rectangle's width. * @param {Number} height - The rectangle's height. * @param {Number} radius - The rounded amount (in pixels) for the four corners. * @todo handle `radius` as top-left, top-right, bottom-right, bottom-left array/object? */ roundedRect: function(ctx, x, y, width, height, radius) { if (radius) { var rx = Math.min(radius, width / 2); var ry = Math.min(radius, height / 2); ctx.moveTo(x + rx, y); ctx.lineTo(x + width - rx, y); ctx.quadraticCurveTo(x + width, y, x + width, y + ry); ctx.lineTo(x + width, y + height - ry); ctx.quadraticCurveTo(x + width, y + height, x + width - rx, y + height); ctx.lineTo(x + rx, y + height); ctx.quadraticCurveTo(x, y + height, x, y + height - ry); ctx.lineTo(x, y + ry); ctx.quadraticCurveTo(x, y, x + rx, y); } else { ctx.rect(x, y, width, height); } }, drawPoint: function(ctx, style, radius, x, y) { var type, edgeLength, xOffset, yOffset, height, size; if (style && typeof style === 'object') { type = style.toString(); if (type === '[object HTMLImageElement]' || type === '[object HTMLCanvasElement]') { ctx.drawImage(style, x - style.width / 2, y - style.height / 2, style.width, style.height); return; } } if (isNaN(radius) || radius <= 0) { return; } switch (style) { // Default includes circle default: ctx.beginPath(); ctx.arc(x, y, radius, 0, Math.PI * 2); ctx.closePath(); ctx.fill(); break; case 'triangle': ctx.beginPath(); edgeLength = 3 * radius / Math.sqrt(3); height = edgeLength * Math.sqrt(3) / 2; ctx.moveTo(x - edgeLength / 2, y + height / 3); ctx.lineTo(x + edgeLength / 2, y + height / 3); ctx.lineTo(x, y - 2 * height / 3); ctx.closePath(); ctx.fill(); break; case 'rect': size = 1 / Math.SQRT2 * radius; ctx.beginPath(); ctx.fillRect(x - size, y - size, 2 * size, 2 * size); ctx.strokeRect(x - size, y - size, 2 * size, 2 * size); break; case 'rectRounded': var offset = radius / Math.SQRT2; var leftX = x - offset; var topY = y - offset; var sideSize = Math.SQRT2 * radius; ctx.beginPath(); this.roundedRect(ctx, leftX, topY, sideSize, sideSize, radius / 2); ctx.closePath(); ctx.fill(); break; case 'rectRot': size = 1 / Math.SQRT2 * radius; ctx.beginPath(); ctx.moveTo(x - size, y); ctx.lineTo(x, y + size); ctx.lineTo(x + size, y); ctx.lineTo(x, y - size); ctx.closePath(); ctx.fill(); break; case 'cross': ctx.beginPath(); ctx.moveTo(x, y + radius); ctx.lineTo(x, y - radius); ctx.moveTo(x - radius, y); ctx.lineTo(x + radius, y); ctx.closePath(); break; case 'crossRot': ctx.beginPath(); xOffset = Math.cos(Math.PI / 4) * radius; yOffset = Math.sin(Math.PI / 4) * radius; ctx.moveTo(x - xOffset, y - yOffset); ctx.lineTo(x + xOffset, y + yOffset); ctx.moveTo(x - xOffset, y + yOffset); ctx.lineTo(x + xOffset, y - yOffset); ctx.closePath(); break; case 'star': ctx.beginPath(); ctx.moveTo(x, y + radius); ctx.lineTo(x, y - radius); ctx.moveTo(x - radius, y); ctx.lineTo(x + radius, y); xOffset = Math.cos(Math.PI / 4) * radius; yOffset = Math.sin(Math.PI / 4) * radius; ctx.moveTo(x - xOffset, y - yOffset); ctx.lineTo(x + xOffset, y + yOffset); ctx.moveTo(x - xOffset, y + yOffset); ctx.lineTo(x + xOffset, y - yOffset); ctx.closePath(); break; case 'line': ctx.beginPath(); ctx.moveTo(x - radius, y); ctx.lineTo(x + radius, y); ctx.closePath(); break; case 'dash': ctx.beginPath(); ctx.moveTo(x, y); ctx.lineTo(x + radius, y); ctx.closePath(); break; } ctx.stroke(); }, clipArea: function(ctx, area) { ctx.save(); ctx.beginPath(); ctx.rect(area.left, area.top, area.right - area.left, area.bottom - area.top); ctx.clip(); }, unclipArea: function(ctx) { ctx.restore(); }, lineTo: function(ctx, previous, target, flip) { if (target.steppedLine) { if ((target.steppedLine === 'after' && !flip) || (target.steppedLine !== 'after' && flip)) { ctx.lineTo(previous.x, target.y); } else { ctx.lineTo(target.x, previous.y); } ctx.lineTo(target.x, target.y); return; } if (!target.tension) { ctx.lineTo(target.x, target.y); return; } ctx.bezierCurveTo( flip ? previous.controlPointPreviousX : previous.controlPointNextX, flip ? previous.controlPointPreviousY : previous.controlPointNextY, flip ? target.controlPointNextX : target.controlPointPreviousX, flip ? target.controlPointNextY : target.controlPointPreviousY, target.x, target.y); } }; // DEPRECATIONS /** * Provided for backward compatibility, use Chart.helpers.canvas.clear instead. * @namespace Chart.helpers.clear * @deprecated since version 2.7.0 * @todo remove at version 3 * @private */ helpers.clear = exports.clear; /** * Provided for backward compatibility, use Chart.helpers.canvas.roundedRect instead. * @namespace Chart.helpers.drawRoundedRectangle * @deprecated since version 2.7.0 * @todo remove at version 3 * @private */ helpers.drawRoundedRectangle = function(ctx) { ctx.beginPath(); exports.roundedRect.apply(exports, arguments); ctx.closePath(); }; },{"42":42}],42:[function(require,module,exports){ 'use strict'; /** * @namespace Chart.helpers */ var helpers = { /** * An empty function that can be used, for example, for optional callback. */ noop: function() {}, /** * Returns a unique id, sequentially generated from a global variable. * @returns {Number} * @function */ uid: (function() { var id = 0; return function() { return id++; }; }()), /** * Returns true if `value` is neither null nor undefined, else returns false. * @param {*} value - The value to test. * @returns {Boolean} * @since 2.7.0 */ isNullOrUndef: function(value) { return value === null || typeof value === 'undefined'; }, /** * Returns true if `value` is an array, else returns false. * @param {*} value - The value to test. * @returns {Boolean} * @function */ isArray: Array.isArray ? Array.isArray : function(value) { return Object.prototype.toString.call(value) === '[object Array]'; }, /** * Returns true if `value` is an object (excluding null), else returns false. * @param {*} value - The value to test. * @returns {Boolean} * @since 2.7.0 */ isObject: function(value) { return value !== null && Object.prototype.toString.call(value) === '[object Object]'; }, /** * Returns `value` if defined, else returns `defaultValue`. * @param {*} value - The value to return if defined. * @param {*} defaultValue - The value to return if `value` is undefined. * @returns {*} */ valueOrDefault: function(value, defaultValue) { return typeof value === 'undefined' ? defaultValue : value; }, /** * Returns value at the given `index` in array if defined, else returns `defaultValue`. * @param {Array} value - The array to lookup for value at `index`. * @param {Number} index - The index in `value` to lookup for value. * @param {*} defaultValue - The value to return if `value[index]` is undefined. * @returns {*} */ valueAtIndexOrDefault: function(value, index, defaultValue) { return helpers.valueOrDefault(helpers.isArray(value) ? value[index] : value, defaultValue); }, /** * Calls `fn` with the given `args` in the scope defined by `thisArg` and returns the * value returned by `fn`. If `fn` is not a function, this method returns undefined. * @param {Function} fn - The function to call. * @param {Array|undefined|null} args - The arguments with which `fn` should be called. * @param {Object} [thisArg] - The value of `this` provided for the call to `fn`. * @returns {*} */ callback: function(fn, args, thisArg) { if (fn && typeof fn.call === 'function') { return fn.apply(thisArg, args); } }, /** * Note(SB) for performance sake, this method should only be used when loopable type * is unknown or in none intensive code (not called often and small loopable). Else * it's preferable to use a regular for() loop and save extra function calls. * @param {Object|Array} loopable - The object or array to be iterated. * @param {Function} fn - The function to call for each item. * @param {Object} [thisArg] - The value of `this` provided for the call to `fn`. * @param {Boolean} [reverse] - If true, iterates backward on the loopable. */ each: function(loopable, fn, thisArg, reverse) { var i, len, keys; if (helpers.isArray(loopable)) { len = loopable.length; if (reverse) { for (i = len - 1; i >= 0; i--) { fn.call(thisArg, loopable[i], i); } } else { for (i = 0; i < len; i++) { fn.call(thisArg, loopable[i], i); } } } else if (helpers.isObject(loopable)) { keys = Object.keys(loopable); len = keys.length; for (i = 0; i < len; i++) { fn.call(thisArg, loopable[keys[i]], keys[i]); } } }, /** * Returns true if the `a0` and `a1` arrays have the same content, else returns false. * @see http://stackoverflow.com/a/14853974 * @param {Array} a0 - The array to compare * @param {Array} a1 - The array to compare * @returns {Boolean} */ arrayEquals: function(a0, a1) { var i, ilen, v0, v1; if (!a0 || !a1 || a0.length !== a1.length) { return false; } for (i = 0, ilen = a0.length; i < ilen; ++i) { v0 = a0[i]; v1 = a1[i]; if (v0 instanceof Array && v1 instanceof Array) { if (!helpers.arrayEquals(v0, v1)) { return false; } } else if (v0 !== v1) { // NOTE: two different object instances will never be equal: {x:20} != {x:20} return false; } } return true; }, /** * Returns a deep copy of `source` without keeping references on objects and arrays. * @param {*} source - The value to clone. * @returns {*} */ clone: function(source) { if (helpers.isArray(source)) { return source.map(helpers.clone); } if (helpers.isObject(source)) { var target = {}; var keys = Object.keys(source); var klen = keys.length; var k = 0; for (; k < klen; ++k) { target[keys[k]] = helpers.clone(source[keys[k]]); } return target; } return source; }, /** * The default merger when Chart.helpers.merge is called without merger option. * Note(SB): this method is also used by configMerge and scaleMerge as fallback. * @private */ _merger: function(key, target, source, options) { var tval = target[key]; var sval = source[key]; if (helpers.isObject(tval) && helpers.isObject(sval)) { helpers.merge(tval, sval, options); } else { target[key] = helpers.clone(sval); } }, /** * Merges source[key] in target[key] only if target[key] is undefined. * @private */ _mergerIf: function(key, target, source) { var tval = target[key]; var sval = source[key]; if (helpers.isObject(tval) && helpers.isObject(sval)) { helpers.mergeIf(tval, sval); } else if (!target.hasOwnProperty(key)) { target[key] = helpers.clone(sval); } }, /** * Recursively deep copies `source` properties into `target` with the given `options`. * IMPORTANT: `target` is not cloned and will be updated with `source` properties. * @param {Object} target - The target object in which all sources are merged into. * @param {Object|Array(Object)} source - Object(s) to merge into `target`. * @param {Object} [options] - Merging options: * @param {Function} [options.merger] - The merge method (key, target, source, options) * @returns {Object} The `target` object. */ merge: function(target, source, options) { var sources = helpers.isArray(source) ? source : [source]; var ilen = sources.length; var merge, i, keys, klen, k; if (!helpers.isObject(target)) { return target; } options = options || {}; merge = options.merger || helpers._merger; for (i = 0; i < ilen; ++i) { source = sources[i]; if (!helpers.isObject(source)) { continue; } keys = Object.keys(source); for (k = 0, klen = keys.length; k < klen; ++k) { merge(keys[k], target, source, options); } } return target; }, /** * Recursively deep copies `source` properties into `target` *only* if not defined in target. * IMPORTANT: `target` is not cloned and will be updated with `source` properties. * @param {Object} target - The target object in which all sources are merged into. * @param {Object|Array(Object)} source - Object(s) to merge into `target`. * @returns {Object} The `target` object. */ mergeIf: function(target, source) { return helpers.merge(target, source, {merger: helpers._mergerIf}); }, /** * Applies the contents of two or more objects together into the first object. * @param {Object} target - The target object in which all objects are merged into. * @param {Object} arg1 - Object containing additional properties to merge in target. * @param {Object} argN - Additional objects containing properties to merge in target. * @returns {Object} The `target` object. */ extend: function(target) { var setFn = function(value, key) { target[key] = value; }; for (var i = 1, ilen = arguments.length; i < ilen; ++i) { helpers.each(arguments[i], setFn); } return target; }, /** * Basic javascript inheritance based on the model created in Backbone.js */ inherits: function(extensions) { var me = this; var ChartElement = (extensions && extensions.hasOwnProperty('constructor')) ? extensions.constructor : function() { return me.apply(this, arguments); }; var Surrogate = function() { this.constructor = ChartElement; }; Surrogate.prototype = me.prototype; ChartElement.prototype = new Surrogate(); ChartElement.extend = helpers.inherits; if (extensions) { helpers.extend(ChartElement.prototype, extensions); } ChartElement.__super__ = me.prototype; return ChartElement; } }; module.exports = helpers; // DEPRECATIONS /** * Provided for backward compatibility, use Chart.helpers.callback instead. * @function Chart.helpers.callCallback * @deprecated since version 2.6.0 * @todo remove at version 3 * @private */ helpers.callCallback = helpers.callback; /** * Provided for backward compatibility, use Array.prototype.indexOf instead. * Array.prototype.indexOf compatibility: Chrome, Opera, Safari, FF1.5+, IE9+ * @function Chart.helpers.indexOf * @deprecated since version 2.7.0 * @todo remove at version 3 * @private */ helpers.indexOf = function(array, item, fromIndex) { return Array.prototype.indexOf.call(array, item, fromIndex); }; /** * Provided for backward compatibility, use Chart.helpers.valueOrDefault instead. * @function Chart.helpers.getValueOrDefault * @deprecated since version 2.7.0 * @todo remove at version 3 * @private */ helpers.getValueOrDefault = helpers.valueOrDefault; /** * Provided for backward compatibility, use Chart.helpers.valueAtIndexOrDefault instead. * @function Chart.helpers.getValueAtIndexOrDefault * @deprecated since version 2.7.0 * @todo remove at version 3 * @private */ helpers.getValueAtIndexOrDefault = helpers.valueAtIndexOrDefault; },{}],43:[function(require,module,exports){ 'use strict'; var helpers = require(42); /** * Easing functions adapted from Robert Penner's easing equations. * @namespace Chart.helpers.easingEffects * @see http://www.robertpenner.com/easing/ */ var effects = { linear: function(t) { return t; }, easeInQuad: function(t) { return t * t; }, easeOutQuad: function(t) { return -t * (t - 2); }, easeInOutQuad: function(t) { if ((t /= 0.5) < 1) { return 0.5 * t * t; } return -0.5 * ((--t) * (t - 2) - 1); }, easeInCubic: function(t) { return t * t * t; }, easeOutCubic: function(t) { return (t = t - 1) * t * t + 1; }, easeInOutCubic: function(t) { if ((t /= 0.5) < 1) { return 0.5 * t * t * t; } return 0.5 * ((t -= 2) * t * t + 2); }, easeInQuart: function(t) { return t * t * t * t; }, easeOutQuart: function(t) { return -((t = t - 1) * t * t * t - 1); }, easeInOutQuart: function(t) { if ((t /= 0.5) < 1) { return 0.5 * t * t * t * t; } return -0.5 * ((t -= 2) * t * t * t - 2); }, easeInQuint: function(t) { return t * t * t * t * t; }, easeOutQuint: function(t) { return (t = t - 1) * t * t * t * t + 1; }, easeInOutQuint: function(t) { if ((t /= 0.5) < 1) { return 0.5 * t * t * t * t * t; } return 0.5 * ((t -= 2) * t * t * t * t + 2); }, easeInSine: function(t) { return -Math.cos(t * (Math.PI / 2)) + 1; }, easeOutSine: function(t) { return Math.sin(t * (Math.PI / 2)); }, easeInOutSine: function(t) { return -0.5 * (Math.cos(Math.PI * t) - 1); }, easeInExpo: function(t) { return (t === 0) ? 0 : Math.pow(2, 10 * (t - 1)); }, easeOutExpo: function(t) { return (t === 1) ? 1 : -Math.pow(2, -10 * t) + 1; }, easeInOutExpo: function(t) { if (t === 0) { return 0; } if (t === 1) { return 1; } if ((t /= 0.5) < 1) { return 0.5 * Math.pow(2, 10 * (t - 1)); } return 0.5 * (-Math.pow(2, -10 * --t) + 2); }, easeInCirc: function(t) { if (t >= 1) { return t; } return -(Math.sqrt(1 - t * t) - 1); }, easeOutCirc: function(t) { return Math.sqrt(1 - (t = t - 1) * t); }, easeInOutCirc: function(t) { if ((t /= 0.5) < 1) { return -0.5 * (Math.sqrt(1 - t * t) - 1); } return 0.5 * (Math.sqrt(1 - (t -= 2) * t) + 1); }, easeInElastic: function(t) { var s = 1.70158; var p = 0; var a = 1; if (t === 0) { return 0; } if (t === 1) { return 1; } if (!p) { p = 0.3; } if (a < 1) { a = 1; s = p / 4; } else { s = p / (2 * Math.PI) * Math.asin(1 / a); } return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p)); }, easeOutElastic: function(t) { var s = 1.70158; var p = 0; var a = 1; if (t === 0) { return 0; } if (t === 1) { return 1; } if (!p) { p = 0.3; } if (a < 1) { a = 1; s = p / 4; } else { s = p / (2 * Math.PI) * Math.asin(1 / a); } return a * Math.pow(2, -10 * t) * Math.sin((t - s) * (2 * Math.PI) / p) + 1; }, easeInOutElastic: function(t) { var s = 1.70158; var p = 0; var a = 1; if (t === 0) { return 0; } if ((t /= 0.5) === 2) { return 1; } if (!p) { p = 0.45; } if (a < 1) { a = 1; s = p / 4; } else { s = p / (2 * Math.PI) * Math.asin(1 / a); } if (t < 1) { return -0.5 * (a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p)); } return a * Math.pow(2, -10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p) * 0.5 + 1; }, easeInBack: function(t) { var s = 1.70158; return t * t * ((s + 1) * t - s); }, easeOutBack: function(t) { var s = 1.70158; return (t = t - 1) * t * ((s + 1) * t + s) + 1; }, easeInOutBack: function(t) { var s = 1.70158; if ((t /= 0.5) < 1) { return 0.5 * (t * t * (((s *= (1.525)) + 1) * t - s)); } return 0.5 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2); }, easeInBounce: function(t) { return 1 - effects.easeOutBounce(1 - t); }, easeOutBounce: function(t) { if (t < (1 / 2.75)) { return 7.5625 * t * t; } if (t < (2 / 2.75)) { return 7.5625 * (t -= (1.5 / 2.75)) * t + 0.75; } if (t < (2.5 / 2.75)) { return 7.5625 * (t -= (2.25 / 2.75)) * t + 0.9375; } return 7.5625 * (t -= (2.625 / 2.75)) * t + 0.984375; }, easeInOutBounce: function(t) { if (t < 0.5) { return effects.easeInBounce(t * 2) * 0.5; } return effects.easeOutBounce(t * 2 - 1) * 0.5 + 0.5; } }; module.exports = { effects: effects }; // DEPRECATIONS /** * Provided for backward compatibility, use Chart.helpers.easing.effects instead. * @function Chart.helpers.easingEffects * @deprecated since version 2.7.0 * @todo remove at version 3 * @private */ helpers.easingEffects = effects; },{"42":42}],44:[function(require,module,exports){ 'use strict'; var helpers = require(42); /** * @alias Chart.helpers.options * @namespace */ module.exports = { /** * Converts the given line height `value` in pixels for a specific font `size`. * @param {Number|String} value - The lineHeight to parse (eg. 1.6, '14px', '75%', '1.6em'). * @param {Number} size - The font size (in pixels) used to resolve relative `value`. * @returns {Number} The effective line height in pixels (size * 1.2 if value is invalid). * @see https://developer.mozilla.org/en-US/docs/Web/CSS/line-height * @since 2.7.0 */ toLineHeight: function(value, size) { var matches = ('' + value).match(/^(normal|(\d+(?:\.\d+)?)(px|em|%)?)$/); if (!matches || matches[1] === 'normal') { return size * 1.2; } value = +matches[2]; switch (matches[3]) { case 'px': return value; case '%': value /= 100; break; default: break; } return size * value; }, /** * Converts the given value into a padding object with pre-computed width/height. * @param {Number|Object} value - If a number, set the value to all TRBL component, * else, if and object, use defined properties and sets undefined ones to 0. * @returns {Object} The padding values (top, right, bottom, left, width, height) * @since 2.7.0 */ toPadding: function(value) { var t, r, b, l; if (helpers.isObject(value)) { t = +value.top || 0; r = +value.right || 0; b = +value.bottom || 0; l = +value.left || 0; } else { t = r = b = l = +value || 0; } return { top: t, right: r, bottom: b, left: l, height: t + b, width: l + r }; }, /** * Evaluates the given `inputs` sequentially and returns the first defined value. * @param {Array[]} inputs - An array of values, falling back to the last value. * @param {Object} [context] - If defined and the current value is a function, the value * is called with `context` as first argument and the result becomes the new input. * @param {Number} [index] - If defined and the current value is an array, the value * at `index` become the new input. * @since 2.7.0 */ resolve: function(inputs, context, index) { var i, ilen, value; for (i = 0, ilen = inputs.length; i < ilen; ++i) { value = inputs[i]; if (value === undefined) { continue; } if (context !== undefined && typeof value === 'function') { value = value(context); } if (index !== undefined && helpers.isArray(value)) { value = value[index]; } if (value !== undefined) { return value; } } } }; },{"42":42}],45:[function(require,module,exports){ 'use strict'; module.exports = require(42); module.exports.easing = require(43); module.exports.canvas = require(41); module.exports.options = require(44); },{"41":41,"42":42,"43":43,"44":44}],46:[function(require,module,exports){ /** * Platform fallback implementation (minimal). * @see https://github.com/chartjs/Chart.js/pull/4591#issuecomment-319575939 */ module.exports = { acquireContext: function(item) { if (item && item.canvas) { // Support for any object associated to a canvas (including a context2d) item = item.canvas; } return item && item.getContext('2d') || null; } }; },{}],47:[function(require,module,exports){ /** * Chart.Platform implementation for targeting a web browser */ 'use strict'; var helpers = require(45); var EXPANDO_KEY = '$chartjs'; var CSS_PREFIX = 'chartjs-'; var CSS_RENDER_MONITOR = CSS_PREFIX + 'render-monitor'; var CSS_RENDER_ANIMATION = CSS_PREFIX + 'render-animation'; var ANIMATION_START_EVENTS = ['animationstart', 'webkitAnimationStart']; /** * DOM event types -> Chart.js event types. * Note: only events with different types are mapped. * @see https://developer.mozilla.org/en-US/docs/Web/Events */ var EVENT_TYPES = { touchstart: 'mousedown', touchmove: 'mousemove', touchend: 'mouseup', pointerenter: 'mouseenter', pointerdown: 'mousedown', pointermove: 'mousemove', pointerup: 'mouseup', pointerleave: 'mouseout', pointerout: 'mouseout' }; /** * The "used" size is the final value of a dimension property after all calculations have * been performed. This method uses the computed style of `element` but returns undefined * if the computed style is not expressed in pixels. That can happen in some cases where * `element` has a size relative to its parent and this last one is not yet displayed, * for example because of `display: none` on a parent node. * @see https://developer.mozilla.org/en-US/docs/Web/CSS/used_value * @returns {Number} Size in pixels or undefined if unknown. */ function readUsedSize(element, property) { var value = helpers.getStyle(element, property); var matches = value && value.match(/^(\d+)(\.\d+)?px$/); return matches ? Number(matches[1]) : undefined; } /** * Initializes the canvas style and render size without modifying the canvas display size, * since responsiveness is handled by the controller.resize() method. The config is used * to determine the aspect ratio to apply in case no explicit height has been specified. */ function initCanvas(canvas, config) { var style = canvas.style; // NOTE(SB) canvas.getAttribute('width') !== canvas.width: in the first case it // returns null or '' if no explicit value has been set to the canvas attribute. var renderHeight = canvas.getAttribute('height'); var renderWidth = canvas.getAttribute('width'); // Chart.js modifies some canvas values that we want to restore on destroy canvas[EXPANDO_KEY] = { initial: { height: renderHeight, width: renderWidth, style: { display: style.display, height: style.height, width: style.width } } }; // Force canvas to display as block to avoid extra space caused by inline // elements, which would interfere with the responsive resize process. // https://github.com/chartjs/Chart.js/issues/2538 style.display = style.display || 'block'; if (renderWidth === null || renderWidth === '') { var displayWidth = readUsedSize(canvas, 'width'); if (displayWidth !== undefined) { canvas.width = displayWidth; } } if (renderHeight === null || renderHeight === '') { if (canvas.style.height === '') { // If no explicit render height and style height, let's apply the aspect ratio, // which one can be specified by the user but also by charts as default option // (i.e. options.aspectRatio). If not specified, use canvas aspect ratio of 2. canvas.height = canvas.width / (config.options.aspectRatio || 2); } else { var displayHeight = readUsedSize(canvas, 'height'); if (displayWidth !== undefined) { canvas.height = displayHeight; } } } return canvas; } /** * Detects support for options object argument in addEventListener. * https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Safely_detecting_option_support * @private */ var supportsEventListenerOptions = (function() { var supports = false; try { var options = Object.defineProperty({}, 'passive', { get: function() { supports = true; } }); window.addEventListener('e', null, options); } catch (e) { // continue regardless of error } return supports; }()); // Default passive to true as expected by Chrome for 'touchstart' and 'touchend' events. // https://github.com/chartjs/Chart.js/issues/4287 var eventListenerOptions = supportsEventListenerOptions ? {passive: true} : false; function addEventListener(node, type, listener) { node.addEventListener(type, listener, eventListenerOptions); } function removeEventListener(node, type, listener) { node.removeEventListener(type, listener, eventListenerOptions); } function createEvent(type, chart, x, y, nativeEvent) { return { type: type, chart: chart, native: nativeEvent || null, x: x !== undefined ? x : null, y: y !== undefined ? y : null, }; } function fromNativeEvent(event, chart) { var type = EVENT_TYPES[event.type] || event.type; var pos = helpers.getRelativePosition(event, chart); return createEvent(type, chart, pos.x, pos.y, event); } function throttled(fn, thisArg) { var ticking = false; var args = []; return function() { args = Array.prototype.slice.call(arguments); thisArg = thisArg || this; if (!ticking) { ticking = true; helpers.requestAnimFrame.call(window, function() { ticking = false; fn.apply(thisArg, args); }); } }; } // Implementation based on https://github.com/marcj/css-element-queries function createResizer(handler) { var resizer = document.createElement('div'); var cls = CSS_PREFIX + 'size-monitor'; var maxSize = 1000000; var style = 'position:absolute;' + 'left:0;' + 'top:0;' + 'right:0;' + 'bottom:0;' + 'overflow:hidden;' + 'pointer-events:none;' + 'visibility:hidden;' + 'z-index:-1;'; resizer.style.cssText = style; resizer.className = cls; resizer.innerHTML = '
' + '
' + '
' + '
' + '
' + '
' + '
' + '
'; var expand = resizer.childNodes[0]; var shrink = resizer.childNodes[1]; resizer._reset = function() { expand.scrollLeft = maxSize; expand.scrollTop = maxSize; shrink.scrollLeft = maxSize; shrink.scrollTop = maxSize; }; var onScroll = function() { resizer._reset(); handler(); }; addEventListener(expand, 'scroll', onScroll.bind(expand, 'expand')); addEventListener(shrink, 'scroll', onScroll.bind(shrink, 'shrink')); return resizer; } // https://davidwalsh.name/detect-node-insertion function watchForRender(node, handler) { var expando = node[EXPANDO_KEY] || (node[EXPANDO_KEY] = {}); var proxy = expando.renderProxy = function(e) { if (e.animationName === CSS_RENDER_ANIMATION) { handler(); } }; helpers.each(ANIMATION_START_EVENTS, function(type) { addEventListener(node, type, proxy); }); // #4737: Chrome might skip the CSS animation when the CSS_RENDER_MONITOR class // is removed then added back immediately (same animation frame?). Accessing the // `offsetParent` property will force a reflow and re-evaluate the CSS animation. // https://gist.github.com/paulirish/5d52fb081b3570c81e3a#box-metrics // https://github.com/chartjs/Chart.js/issues/4737 expando.reflow = !!node.offsetParent; node.classList.add(CSS_RENDER_MONITOR); } function unwatchForRender(node) { var expando = node[EXPANDO_KEY] || {}; var proxy = expando.renderProxy; if (proxy) { helpers.each(ANIMATION_START_EVENTS, function(type) { removeEventListener(node, type, proxy); }); delete expando.renderProxy; } node.classList.remove(CSS_RENDER_MONITOR); } function addResizeListener(node, listener, chart) { var expando = node[EXPANDO_KEY] || (node[EXPANDO_KEY] = {}); // Let's keep track of this added resizer and thus avoid DOM query when removing it. var resizer = expando.resizer = createResizer(throttled(function() { if (expando.resizer) { return listener(createEvent('resize', chart)); } })); // The resizer needs to be attached to the node parent, so we first need to be // sure that `node` is attached to the DOM before injecting the resizer element. watchForRender(node, function() { if (expando.resizer) { var container = node.parentNode; if (container && container !== resizer.parentNode) { container.insertBefore(resizer, container.firstChild); } // The container size might have changed, let's reset the resizer state. resizer._reset(); } }); } function removeResizeListener(node) { var expando = node[EXPANDO_KEY] || {}; var resizer = expando.resizer; delete expando.resizer; unwatchForRender(node); if (resizer && resizer.parentNode) { resizer.parentNode.removeChild(resizer); } } function injectCSS(platform, css) { // http://stackoverflow.com/q/3922139 var style = platform._style || document.createElement('style'); if (!platform._style) { platform._style = style; css = '/* Chart.js */\n' + css; style.setAttribute('type', 'text/css'); document.getElementsByTagName('head')[0].appendChild(style); } style.appendChild(document.createTextNode(css)); } module.exports = { /** * This property holds whether this platform is enabled for the current environment. * Currently used by platform.js to select the proper implementation. * @private */ _enabled: typeof window !== 'undefined' && typeof document !== 'undefined', initialize: function() { var keyframes = 'from{opacity:0.99}to{opacity:1}'; injectCSS(this, // DOM rendering detection // https://davidwalsh.name/detect-node-insertion '@-webkit-keyframes ' + CSS_RENDER_ANIMATION + '{' + keyframes + '}' + '@keyframes ' + CSS_RENDER_ANIMATION + '{' + keyframes + '}' + '.' + CSS_RENDER_MONITOR + '{' + '-webkit-animation:' + CSS_RENDER_ANIMATION + ' 0.001s;' + 'animation:' + CSS_RENDER_ANIMATION + ' 0.001s;' + '}' ); }, acquireContext: function(item, config) { if (typeof item === 'string') { item = document.getElementById(item); } else if (item.length) { // Support for array based queries (such as jQuery) item = item[0]; } if (item && item.canvas) { // Support for any object associated to a canvas (including a context2d) item = item.canvas; } // To prevent canvas fingerprinting, some add-ons undefine the getContext // method, for example: https://github.com/kkapsner/CanvasBlocker // https://github.com/chartjs/Chart.js/issues/2807 var context = item && item.getContext && item.getContext('2d'); // `instanceof HTMLCanvasElement/CanvasRenderingContext2D` fails when the item is // inside an iframe or when running in a protected environment. We could guess the // types from their toString() value but let's keep things flexible and assume it's // a sufficient condition if the item has a context2D which has item as `canvas`. // https://github.com/chartjs/Chart.js/issues/3887 // https://github.com/chartjs/Chart.js/issues/4102 // https://github.com/chartjs/Chart.js/issues/4152 if (context && context.canvas === item) { initCanvas(item, config); return context; } return null; }, releaseContext: function(context) { var canvas = context.canvas; if (!canvas[EXPANDO_KEY]) { return; } var initial = canvas[EXPANDO_KEY].initial; ['height', 'width'].forEach(function(prop) { var value = initial[prop]; if (helpers.isNullOrUndef(value)) { canvas.removeAttribute(prop); } else { canvas.setAttribute(prop, value); } }); helpers.each(initial.style || {}, function(value, key) { canvas.style[key] = value; }); // The canvas render size might have been changed (and thus the state stack discarded), // we can't use save() and restore() to restore the initial state. So make sure that at // least the canvas context is reset to the default state by setting the canvas width. // https://www.w3.org/TR/2011/WD-html5-20110525/the-canvas-element.html canvas.width = canvas.width; delete canvas[EXPANDO_KEY]; }, addEventListener: function(chart, type, listener) { var canvas = chart.canvas; if (type === 'resize') { // Note: the resize event is not supported on all browsers. addResizeListener(canvas, listener, chart); return; } var expando = listener[EXPANDO_KEY] || (listener[EXPANDO_KEY] = {}); var proxies = expando.proxies || (expando.proxies = {}); var proxy = proxies[chart.id + '_' + type] = function(event) { listener(fromNativeEvent(event, chart)); }; addEventListener(canvas, type, proxy); }, removeEventListener: function(chart, type, listener) { var canvas = chart.canvas; if (type === 'resize') { // Note: the resize event is not supported on all browsers. removeResizeListener(canvas, listener); return; } var expando = listener[EXPANDO_KEY] || {}; var proxies = expando.proxies || {}; var proxy = proxies[chart.id + '_' + type]; if (!proxy) { return; } removeEventListener(canvas, type, proxy); } }; // DEPRECATIONS /** * Provided for backward compatibility, use EventTarget.addEventListener instead. * EventTarget.addEventListener compatibility: Chrome, Opera 7, Safari, FF1.5+, IE9+ * @see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener * @function Chart.helpers.addEvent * @deprecated since version 2.7.0 * @todo remove at version 3 * @private */ helpers.addEvent = addEventListener; /** * Provided for backward compatibility, use EventTarget.removeEventListener instead. * EventTarget.removeEventListener compatibility: Chrome, Opera 7, Safari, FF1.5+, IE9+ * @see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener * @function Chart.helpers.removeEvent * @deprecated since version 2.7.0 * @todo remove at version 3 * @private */ helpers.removeEvent = removeEventListener; },{"45":45}],48:[function(require,module,exports){ 'use strict'; var helpers = require(45); var basic = require(46); var dom = require(47); // @TODO Make possible to select another platform at build time. var implementation = dom._enabled ? dom : basic; /** * @namespace Chart.platform * @see https://chartjs.gitbooks.io/proposals/content/Platform.html * @since 2.4.0 */ module.exports = helpers.extend({ /** * @since 2.7.0 */ initialize: function() {}, /** * Called at chart construction time, returns a context2d instance implementing * the [W3C Canvas 2D Context API standard]{@link https://www.w3.org/TR/2dcontext/}. * @param {*} item - The native item from which to acquire context (platform specific) * @param {Object} options - The chart options * @returns {CanvasRenderingContext2D} context2d instance */ acquireContext: function() {}, /** * Called at chart destruction time, releases any resources associated to the context * previously returned by the acquireContext() method. * @param {CanvasRenderingContext2D} context - The context2d instance * @returns {Boolean} true if the method succeeded, else false */ releaseContext: function() {}, /** * Registers the specified listener on the given chart. * @param {Chart} chart - Chart from which to listen for event * @param {String} type - The ({@link IEvent}) type to listen for * @param {Function} listener - Receives a notification (an object that implements * the {@link IEvent} interface) when an event of the specified type occurs. */ addEventListener: function() {}, /** * Removes the specified listener previously registered with addEventListener. * @param {Chart} chart -Chart from which to remove the listener * @param {String} type - The ({@link IEvent}) type to remove * @param {Function} listener - The listener function to remove from the event target. */ removeEventListener: function() {} }, implementation); /** * @interface IPlatform * Allows abstracting platform dependencies away from the chart * @borrows Chart.platform.acquireContext as acquireContext * @borrows Chart.platform.releaseContext as releaseContext * @borrows Chart.platform.addEventListener as addEventListener * @borrows Chart.platform.removeEventListener as removeEventListener */ /** * @interface IEvent * @prop {String} type - The event type name, possible values are: * 'contextmenu', 'mouseenter', 'mousedown', 'mousemove', 'mouseup', 'mouseout', * 'click', 'dblclick', 'keydown', 'keypress', 'keyup' and 'resize' * @prop {*} native - The original native event (null for emulated events, e.g. 'resize') * @prop {Number} x - The mouse x position, relative to the canvas (null for incompatible events) * @prop {Number} y - The mouse y position, relative to the canvas (null for incompatible events) */ },{"45":45,"46":46,"47":47}],49:[function(require,module,exports){ /** * Plugin based on discussion from the following Chart.js issues: * @see https://github.com/chartjs/Chart.js/issues/2380#issuecomment-279961569 * @see https://github.com/chartjs/Chart.js/issues/2440#issuecomment-256461897 */ 'use strict'; var defaults = require(25); var elements = require(40); var helpers = require(45); defaults._set('global', { plugins: { filler: { propagate: true } } }); module.exports = function() { var mappers = { dataset: function(source) { var index = source.fill; var chart = source.chart; var meta = chart.getDatasetMeta(index); var visible = meta && chart.isDatasetVisible(index); var points = (visible && meta.dataset._children) || []; var length = points.length || 0; return !length ? null : function(point, i) { return (i < length && points[i]._view) || null; }; }, boundary: function(source) { var boundary = source.boundary; var x = boundary ? boundary.x : null; var y = boundary ? boundary.y : null; return function(point) { return { x: x === null ? point.x : x, y: y === null ? point.y : y, }; }; } }; // @todo if (fill[0] === '#') function decodeFill(el, index, count) { var model = el._model || {}; var fill = model.fill; var target; if (fill === undefined) { fill = !!model.backgroundColor; } if (fill === false || fill === null) { return false; } if (fill === true) { return 'origin'; } target = parseFloat(fill, 10); if (isFinite(target) && Math.floor(target) === target) { if (fill[0] === '-' || fill[0] === '+') { target = index + target; } if (target === index || target < 0 || target >= count) { return false; } return target; } switch (fill) { // compatibility case 'bottom': return 'start'; case 'top': return 'end'; case 'zero': return 'origin'; // supported boundaries case 'origin': case 'start': case 'end': return fill; // invalid fill values default: return false; } } function computeBoundary(source) { var model = source.el._model || {}; var scale = source.el._scale || {}; var fill = source.fill; var target = null; var horizontal; if (isFinite(fill)) { return null; } // Backward compatibility: until v3, we still need to support boundary values set on // the model (scaleTop, scaleBottom and scaleZero) because some external plugins and // controllers might still use it (e.g. the Smith chart). if (fill === 'start') { target = model.scaleBottom === undefined ? scale.bottom : model.scaleBottom; } else if (fill === 'end') { target = model.scaleTop === undefined ? scale.top : model.scaleTop; } else if (model.scaleZero !== undefined) { target = model.scaleZero; } else if (scale.getBasePosition) { target = scale.getBasePosition(); } else if (scale.getBasePixel) { target = scale.getBasePixel(); } if (target !== undefined && target !== null) { if (target.x !== undefined && target.y !== undefined) { return target; } if (typeof target === 'number' && isFinite(target)) { horizontal = scale.isHorizontal(); return { x: horizontal ? target : null, y: horizontal ? null : target }; } } return null; } function resolveTarget(sources, index, propagate) { var source = sources[index]; var fill = source.fill; var visited = [index]; var target; if (!propagate) { return fill; } while (fill !== false && visited.indexOf(fill) === -1) { if (!isFinite(fill)) { return fill; } target = sources[fill]; if (!target) { return false; } if (target.visible) { return fill; } visited.push(fill); fill = target.fill; } return false; } function createMapper(source) { var fill = source.fill; var type = 'dataset'; if (fill === false) { return null; } if (!isFinite(fill)) { type = 'boundary'; } return mappers[type](source); } function isDrawable(point) { return point && !point.skip; } function drawArea(ctx, curve0, curve1, len0, len1) { var i; if (!len0 || !len1) { return; } // building first area curve (normal) ctx.moveTo(curve0[0].x, curve0[0].y); for (i = 1; i < len0; ++i) { helpers.canvas.lineTo(ctx, curve0[i - 1], curve0[i]); } // joining the two area curves ctx.lineTo(curve1[len1 - 1].x, curve1[len1 - 1].y); // building opposite area curve (reverse) for (i = len1 - 1; i > 0; --i) { helpers.canvas.lineTo(ctx, curve1[i], curve1[i - 1], true); } } function doFill(ctx, points, mapper, view, color, loop) { var count = points.length; var span = view.spanGaps; var curve0 = []; var curve1 = []; var len0 = 0; var len1 = 0; var i, ilen, index, p0, p1, d0, d1; ctx.beginPath(); for (i = 0, ilen = (count + !!loop); i < ilen; ++i) { index = i % count; p0 = points[index]._view; p1 = mapper(p0, index, view); d0 = isDrawable(p0); d1 = isDrawable(p1); if (d0 && d1) { len0 = curve0.push(p0); len1 = curve1.push(p1); } else if (len0 && len1) { if (!span) { drawArea(ctx, curve0, curve1, len0, len1); len0 = len1 = 0; curve0 = []; curve1 = []; } else { if (d0) { curve0.push(p0); } if (d1) { curve1.push(p1); } } } } drawArea(ctx, curve0, curve1, len0, len1); ctx.closePath(); ctx.fillStyle = color; ctx.fill(); } return { id: 'filler', afterDatasetsUpdate: function(chart, options) { var count = (chart.data.datasets || []).length; var propagate = options.propagate; var sources = []; var meta, i, el, source; for (i = 0; i < count; ++i) { meta = chart.getDatasetMeta(i); el = meta.dataset; source = null; if (el && el._model && el instanceof elements.Line) { source = { visible: chart.isDatasetVisible(i), fill: decodeFill(el, i, count), chart: chart, el: el }; } meta.$filler = source; sources.push(source); } for (i = 0; i < count; ++i) { source = sources[i]; if (!source) { continue; } source.fill = resolveTarget(sources, i, propagate); source.boundary = computeBoundary(source); source.mapper = createMapper(source); } }, beforeDatasetDraw: function(chart, args) { var meta = args.meta.$filler; if (!meta) { return; } var ctx = chart.ctx; var el = meta.el; var view = el._view; var points = el._children || []; var mapper = meta.mapper; var color = view.backgroundColor || defaults.global.defaultColor; if (mapper && color && points.length) { helpers.canvas.clipArea(ctx, chart.chartArea); doFill(ctx, points, mapper, view, color, el._loop); helpers.canvas.unclipArea(ctx); } } }; }; },{"25":25,"40":40,"45":45}],50:[function(require,module,exports){ 'use strict'; var defaults = require(25); var Element = require(26); var helpers = require(45); defaults._set('global', { legend: { display: true, position: 'top', fullWidth: true, reverse: false, weight: 1000, // a callback that will handle onClick: function(e, legendItem) { var index = legendItem.datasetIndex; var ci = this.chart; var meta = ci.getDatasetMeta(index); // See controller.isDatasetVisible comment meta.hidden = meta.hidden === null ? !ci.data.datasets[index].hidden : null; // We hid a dataset ... rerender the chart ci.update(); }, onHover: null, labels: { boxWidth: 40, padding: 10, // Generates labels shown in the legend // Valid properties to return: // text : text to display // fillStyle : fill of coloured box // strokeStyle: stroke of coloured box // hidden : if this legend item refers to a hidden item // lineCap : cap style for line // lineDash // lineDashOffset : // lineJoin : // lineWidth : generateLabels: function(chart) { var data = chart.data; return helpers.isArray(data.datasets) ? data.datasets.map(function(dataset, i) { return { text: dataset.label, fillStyle: (!helpers.isArray(dataset.backgroundColor) ? dataset.backgroundColor : dataset.backgroundColor[0]), hidden: !chart.isDatasetVisible(i), lineCap: dataset.borderCapStyle, lineDash: dataset.borderDash, lineDashOffset: dataset.borderDashOffset, lineJoin: dataset.borderJoinStyle, lineWidth: dataset.borderWidth, strokeStyle: dataset.borderColor, pointStyle: dataset.pointStyle, // Below is extra data used for toggling the datasets datasetIndex: i }; }, this) : []; } } }, legendCallback: function(chart) { var text = []; text.push('
    '); for (var i = 0; i < chart.data.datasets.length; i++) { text.push('
  • '); if (chart.data.datasets[i].label) { text.push(chart.data.datasets[i].label); } text.push('
  • '); } text.push('
'); return text.join(''); } }); module.exports = function(Chart) { var layout = Chart.layoutService; var noop = helpers.noop; /** * Helper function to get the box width based on the usePointStyle option * @param labelopts {Object} the label options on the legend * @param fontSize {Number} the label font size * @return {Number} width of the color box area */ function getBoxWidth(labelOpts, fontSize) { return labelOpts.usePointStyle ? fontSize * Math.SQRT2 : labelOpts.boxWidth; } Chart.Legend = Element.extend({ initialize: function(config) { helpers.extend(this, config); // Contains hit boxes for each dataset (in dataset order) this.legendHitBoxes = []; // Are we in doughnut mode which has a different data type this.doughnutMode = false; }, // These methods are ordered by lifecycle. Utilities then follow. // Any function defined here is inherited by all legend types. // Any function can be extended by the legend type beforeUpdate: noop, update: function(maxWidth, maxHeight, margins) { var me = this; // Update Lifecycle - Probably don't want to ever extend or overwrite this function ;) me.beforeUpdate(); // Absorb the master measurements me.maxWidth = maxWidth; me.maxHeight = maxHeight; me.margins = margins; // Dimensions me.beforeSetDimensions(); me.setDimensions(); me.afterSetDimensions(); // Labels me.beforeBuildLabels(); me.buildLabels(); me.afterBuildLabels(); // Fit me.beforeFit(); me.fit(); me.afterFit(); // me.afterUpdate(); return me.minSize; }, afterUpdate: noop, // beforeSetDimensions: noop, setDimensions: function() { var me = this; // Set the unconstrained dimension before label rotation if (me.isHorizontal()) { // Reset position before calculating rotation me.width = me.maxWidth; me.left = 0; me.right = me.width; } else { me.height = me.maxHeight; // Reset position before calculating rotation me.top = 0; me.bottom = me.height; } // Reset padding me.paddingLeft = 0; me.paddingTop = 0; me.paddingRight = 0; me.paddingBottom = 0; // Reset minSize me.minSize = { width: 0, height: 0 }; }, afterSetDimensions: noop, // beforeBuildLabels: noop, buildLabels: function() { var me = this; var labelOpts = me.options.labels || {}; var legendItems = helpers.callback(labelOpts.generateLabels, [me.chart], me) || []; if (labelOpts.filter) { legendItems = legendItems.filter(function(item) { return labelOpts.filter(item, me.chart.data); }); } if (me.options.reverse) { legendItems.reverse(); } me.legendItems = legendItems; }, afterBuildLabels: noop, // beforeFit: noop, fit: function() { var me = this; var opts = me.options; var labelOpts = opts.labels; var display = opts.display; var ctx = me.ctx; var globalDefault = defaults.global; var valueOrDefault = helpers.valueOrDefault; var fontSize = valueOrDefault(labelOpts.fontSize, globalDefault.defaultFontSize); var fontStyle = valueOrDefault(labelOpts.fontStyle, globalDefault.defaultFontStyle); var fontFamily = valueOrDefault(labelOpts.fontFamily, globalDefault.defaultFontFamily); var labelFont = helpers.fontString(fontSize, fontStyle, fontFamily); // Reset hit boxes var hitboxes = me.legendHitBoxes = []; var minSize = me.minSize; var isHorizontal = me.isHorizontal(); if (isHorizontal) { minSize.width = me.maxWidth; // fill all the width minSize.height = display ? 10 : 0; } else { minSize.width = display ? 10 : 0; minSize.height = me.maxHeight; // fill all the height } // Increase sizes here if (display) { ctx.font = labelFont; if (isHorizontal) { // Labels // Width of each line of legend boxes. Labels wrap onto multiple lines when there are too many to fit on one var lineWidths = me.lineWidths = [0]; var totalHeight = me.legendItems.length ? fontSize + (labelOpts.padding) : 0; ctx.textAlign = 'left'; ctx.textBaseline = 'top'; helpers.each(me.legendItems, function(legendItem, i) { var boxWidth = getBoxWidth(labelOpts, fontSize); var width = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width; if (lineWidths[lineWidths.length - 1] + width + labelOpts.padding >= me.width) { totalHeight += fontSize + (labelOpts.padding); lineWidths[lineWidths.length] = me.left; } // Store the hitbox width and height here. Final position will be updated in `draw` hitboxes[i] = { left: 0, top: 0, width: width, height: fontSize }; lineWidths[lineWidths.length - 1] += width + labelOpts.padding; }); minSize.height += totalHeight; } else { var vPadding = labelOpts.padding; var columnWidths = me.columnWidths = []; var totalWidth = labelOpts.padding; var currentColWidth = 0; var currentColHeight = 0; var itemHeight = fontSize + vPadding; helpers.each(me.legendItems, function(legendItem, i) { var boxWidth = getBoxWidth(labelOpts, fontSize); var itemWidth = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width; // If too tall, go to new column if (currentColHeight + itemHeight > minSize.height) { totalWidth += currentColWidth + labelOpts.padding; columnWidths.push(currentColWidth); // previous column width currentColWidth = 0; currentColHeight = 0; } // Get max width currentColWidth = Math.max(currentColWidth, itemWidth); currentColHeight += itemHeight; // Store the hitbox width and height here. Final position will be updated in `draw` hitboxes[i] = { left: 0, top: 0, width: itemWidth, height: fontSize }; }); totalWidth += currentColWidth; columnWidths.push(currentColWidth); minSize.width += totalWidth; } } me.width = minSize.width; me.height = minSize.height; }, afterFit: noop, // Shared Methods isHorizontal: function() { return this.options.position === 'top' || this.options.position === 'bottom'; }, // Actually draw the legend on the canvas draw: function() { var me = this; var opts = me.options; var labelOpts = opts.labels; var globalDefault = defaults.global; var lineDefault = globalDefault.elements.line; var legendWidth = me.width; var lineWidths = me.lineWidths; if (opts.display) { var ctx = me.ctx; var valueOrDefault = helpers.valueOrDefault; var fontColor = valueOrDefault(labelOpts.fontColor, globalDefault.defaultFontColor); var fontSize = valueOrDefault(labelOpts.fontSize, globalDefault.defaultFontSize); var fontStyle = valueOrDefault(labelOpts.fontStyle, globalDefault.defaultFontStyle); var fontFamily = valueOrDefault(labelOpts.fontFamily, globalDefault.defaultFontFamily); var labelFont = helpers.fontString(fontSize, fontStyle, fontFamily); var cursor; // Canvas setup ctx.textAlign = 'left'; ctx.textBaseline = 'middle'; ctx.lineWidth = 0.5; ctx.strokeStyle = fontColor; // for strikethrough effect ctx.fillStyle = fontColor; // render in correct colour ctx.font = labelFont; var boxWidth = getBoxWidth(labelOpts, fontSize); var hitboxes = me.legendHitBoxes; // current position var drawLegendBox = function(x, y, legendItem) { if (isNaN(boxWidth) || boxWidth <= 0) { return; } // Set the ctx for the box ctx.save(); ctx.fillStyle = valueOrDefault(legendItem.fillStyle, globalDefault.defaultColor); ctx.lineCap = valueOrDefault(legendItem.lineCap, lineDefault.borderCapStyle); ctx.lineDashOffset = valueOrDefault(legendItem.lineDashOffset, lineDefault.borderDashOffset); ctx.lineJoin = valueOrDefault(legendItem.lineJoin, lineDefault.borderJoinStyle); ctx.lineWidth = valueOrDefault(legendItem.lineWidth, lineDefault.borderWidth); ctx.strokeStyle = valueOrDefault(legendItem.strokeStyle, globalDefault.defaultColor); var isLineWidthZero = (valueOrDefault(legendItem.lineWidth, lineDefault.borderWidth) === 0); if (ctx.setLineDash) { // IE 9 and 10 do not support line dash ctx.setLineDash(valueOrDefault(legendItem.lineDash, lineDefault.borderDash)); } if (opts.labels && opts.labels.usePointStyle) { // Recalculate x and y for drawPoint() because its expecting // x and y to be center of figure (instead of top left) var radius = fontSize * Math.SQRT2 / 2; var offSet = radius / Math.SQRT2; var centerX = x + offSet; var centerY = y + offSet; // Draw pointStyle as legend symbol helpers.canvas.drawPoint(ctx, legendItem.pointStyle, radius, centerX, centerY); } else { // Draw box as legend symbol if (!isLineWidthZero) { ctx.strokeRect(x, y, boxWidth, fontSize); } ctx.fillRect(x, y, boxWidth, fontSize); } ctx.restore(); }; var fillText = function(x, y, legendItem, textWidth) { var halfFontSize = fontSize / 2; var xLeft = boxWidth + halfFontSize + x; var yMiddle = y + halfFontSize; ctx.fillText(legendItem.text, xLeft, yMiddle); if (legendItem.hidden) { // Strikethrough the text if hidden ctx.beginPath(); ctx.lineWidth = 2; ctx.moveTo(xLeft, yMiddle); ctx.lineTo(xLeft + textWidth, yMiddle); ctx.stroke(); } }; // Horizontal var isHorizontal = me.isHorizontal(); if (isHorizontal) { cursor = { x: me.left + ((legendWidth - lineWidths[0]) / 2), y: me.top + labelOpts.padding, line: 0 }; } else { cursor = { x: me.left + labelOpts.padding, y: me.top + labelOpts.padding, line: 0 }; } var itemHeight = fontSize + labelOpts.padding; helpers.each(me.legendItems, function(legendItem, i) { var textWidth = ctx.measureText(legendItem.text).width; var width = boxWidth + (fontSize / 2) + textWidth; var x = cursor.x; var y = cursor.y; if (isHorizontal) { if (x + width >= legendWidth) { y = cursor.y += itemHeight; cursor.line++; x = cursor.x = me.left + ((legendWidth - lineWidths[cursor.line]) / 2); } } else if (y + itemHeight > me.bottom) { x = cursor.x = x + me.columnWidths[cursor.line] + labelOpts.padding; y = cursor.y = me.top + labelOpts.padding; cursor.line++; } drawLegendBox(x, y, legendItem); hitboxes[i].left = x; hitboxes[i].top = y; // Fill the actual label fillText(x, y, legendItem, textWidth); if (isHorizontal) { cursor.x += width + (labelOpts.padding); } else { cursor.y += itemHeight; } }); } }, /** * Handle an event * @private * @param {IEvent} event - The event to handle * @return {Boolean} true if a change occured */ handleEvent: function(e) { var me = this; var opts = me.options; var type = e.type === 'mouseup' ? 'click' : e.type; var changed = false; if (type === 'mousemove') { if (!opts.onHover) { return; } } else if (type === 'click') { if (!opts.onClick) { return; } } else { return; } // Chart event already has relative position in it var x = e.x; var y = e.y; if (x >= me.left && x <= me.right && y >= me.top && y <= me.bottom) { // See if we are touching one of the dataset boxes var lh = me.legendHitBoxes; for (var i = 0; i < lh.length; ++i) { var hitBox = lh[i]; if (x >= hitBox.left && x <= hitBox.left + hitBox.width && y >= hitBox.top && y <= hitBox.top + hitBox.height) { // Touching an element if (type === 'click') { // use e.native for backwards compatibility opts.onClick.call(me, e.native, me.legendItems[i]); changed = true; break; } else if (type === 'mousemove') { // use e.native for backwards compatibility opts.onHover.call(me, e.native, me.legendItems[i]); changed = true; break; } } } } return changed; } }); function createNewLegendAndAttach(chart, legendOpts) { var legend = new Chart.Legend({ ctx: chart.ctx, options: legendOpts, chart: chart }); layout.configure(chart, legend, legendOpts); layout.addBox(chart, legend); chart.legend = legend; } return { id: 'legend', beforeInit: function(chart) { var legendOpts = chart.options.legend; if (legendOpts) { createNewLegendAndAttach(chart, legendOpts); } }, beforeUpdate: function(chart) { var legendOpts = chart.options.legend; var legend = chart.legend; if (legendOpts) { helpers.mergeIf(legendOpts, defaults.global.legend); if (legend) { layout.configure(chart, legend, legendOpts); legend.options = legendOpts; } else { createNewLegendAndAttach(chart, legendOpts); } } else if (legend) { layout.removeBox(chart, legend); delete chart.legend; } }, afterEvent: function(chart, e) { var legend = chart.legend; if (legend) { legend.handleEvent(e); } } }; }; },{"25":25,"26":26,"45":45}],51:[function(require,module,exports){ 'use strict'; var defaults = require(25); var Element = require(26); var helpers = require(45); defaults._set('global', { title: { display: false, fontStyle: 'bold', fullWidth: true, lineHeight: 1.2, padding: 10, position: 'top', text: '', weight: 2000 // by default greater than legend (1000) to be above } }); module.exports = function(Chart) { var layout = Chart.layoutService; var noop = helpers.noop; Chart.Title = Element.extend({ initialize: function(config) { var me = this; helpers.extend(me, config); // Contains hit boxes for each dataset (in dataset order) me.legendHitBoxes = []; }, // These methods are ordered by lifecycle. Utilities then follow. beforeUpdate: noop, update: function(maxWidth, maxHeight, margins) { var me = this; // Update Lifecycle - Probably don't want to ever extend or overwrite this function ;) me.beforeUpdate(); // Absorb the master measurements me.maxWidth = maxWidth; me.maxHeight = maxHeight; me.margins = margins; // Dimensions me.beforeSetDimensions(); me.setDimensions(); me.afterSetDimensions(); // Labels me.beforeBuildLabels(); me.buildLabels(); me.afterBuildLabels(); // Fit me.beforeFit(); me.fit(); me.afterFit(); // me.afterUpdate(); return me.minSize; }, afterUpdate: noop, // beforeSetDimensions: noop, setDimensions: function() { var me = this; // Set the unconstrained dimension before label rotation if (me.isHorizontal()) { // Reset position before calculating rotation me.width = me.maxWidth; me.left = 0; me.right = me.width; } else { me.height = me.maxHeight; // Reset position before calculating rotation me.top = 0; me.bottom = me.height; } // Reset padding me.paddingLeft = 0; me.paddingTop = 0; me.paddingRight = 0; me.paddingBottom = 0; // Reset minSize me.minSize = { width: 0, height: 0 }; }, afterSetDimensions: noop, // beforeBuildLabels: noop, buildLabels: noop, afterBuildLabels: noop, // beforeFit: noop, fit: function() { var me = this; var valueOrDefault = helpers.valueOrDefault; var opts = me.options; var display = opts.display; var fontSize = valueOrDefault(opts.fontSize, defaults.global.defaultFontSize); var minSize = me.minSize; var lineCount = helpers.isArray(opts.text) ? opts.text.length : 1; var lineHeight = helpers.options.toLineHeight(opts.lineHeight, fontSize); var textSize = display ? (lineCount * lineHeight) + (opts.padding * 2) : 0; if (me.isHorizontal()) { minSize.width = me.maxWidth; // fill all the width minSize.height = textSize; } else { minSize.width = textSize; minSize.height = me.maxHeight; // fill all the height } me.width = minSize.width; me.height = minSize.height; }, afterFit: noop, // Shared Methods isHorizontal: function() { var pos = this.options.position; return pos === 'top' || pos === 'bottom'; }, // Actually draw the title block on the canvas draw: function() { var me = this; var ctx = me.ctx; var valueOrDefault = helpers.valueOrDefault; var opts = me.options; var globalDefaults = defaults.global; if (opts.display) { var fontSize = valueOrDefault(opts.fontSize, globalDefaults.defaultFontSize); var fontStyle = valueOrDefault(opts.fontStyle, globalDefaults.defaultFontStyle); var fontFamily = valueOrDefault(opts.fontFamily, globalDefaults.defaultFontFamily); var titleFont = helpers.fontString(fontSize, fontStyle, fontFamily); var lineHeight = helpers.options.toLineHeight(opts.lineHeight, fontSize); var offset = lineHeight / 2 + opts.padding; var rotation = 0; var top = me.top; var left = me.left; var bottom = me.bottom; var right = me.right; var maxWidth, titleX, titleY; ctx.fillStyle = valueOrDefault(opts.fontColor, globalDefaults.defaultFontColor); // render in correct colour ctx.font = titleFont; // Horizontal if (me.isHorizontal()) { titleX = left + ((right - left) / 2); // midpoint of the width titleY = top + offset; maxWidth = right - left; } else { titleX = opts.position === 'left' ? left + offset : right - offset; titleY = top + ((bottom - top) / 2); maxWidth = bottom - top; rotation = Math.PI * (opts.position === 'left' ? -0.5 : 0.5); } ctx.save(); ctx.translate(titleX, titleY); ctx.rotate(rotation); ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; var text = opts.text; if (helpers.isArray(text)) { var y = 0; for (var i = 0; i < text.length; ++i) { ctx.fillText(text[i], 0, y, maxWidth); y += lineHeight; } } else { ctx.fillText(text, 0, 0, maxWidth); } ctx.restore(); } } }); function createNewTitleBlockAndAttach(chart, titleOpts) { var title = new Chart.Title({ ctx: chart.ctx, options: titleOpts, chart: chart }); layout.configure(chart, title, titleOpts); layout.addBox(chart, title); chart.titleBlock = title; } return { id: 'title', beforeInit: function(chart) { var titleOpts = chart.options.title; if (titleOpts) { createNewTitleBlockAndAttach(chart, titleOpts); } }, beforeUpdate: function(chart) { var titleOpts = chart.options.title; var titleBlock = chart.titleBlock; if (titleOpts) { helpers.mergeIf(titleOpts, defaults.global.title); if (titleBlock) { layout.configure(chart, titleBlock, titleOpts); titleBlock.options = titleOpts; } else { createNewTitleBlockAndAttach(chart, titleOpts); } } else if (titleBlock) { Chart.layoutService.removeBox(chart, titleBlock); delete chart.titleBlock; } } }; }; },{"25":25,"26":26,"45":45}],52:[function(require,module,exports){ 'use strict'; module.exports = function(Chart) { // Default config for a category scale var defaultConfig = { position: 'bottom' }; var DatasetScale = Chart.Scale.extend({ /** * Internal function to get the correct labels. If data.xLabels or data.yLabels are defined, use those * else fall back to data.labels * @private */ getLabels: function() { var data = this.chart.data; return this.options.labels || (this.isHorizontal() ? data.xLabels : data.yLabels) || data.labels; }, determineDataLimits: function() { var me = this; var labels = me.getLabels(); me.minIndex = 0; me.maxIndex = labels.length - 1; var findIndex; if (me.options.ticks.min !== undefined) { // user specified min value findIndex = labels.indexOf(me.options.ticks.min); me.minIndex = findIndex !== -1 ? findIndex : me.minIndex; } if (me.options.ticks.max !== undefined) { // user specified max value findIndex = labels.indexOf(me.options.ticks.max); me.maxIndex = findIndex !== -1 ? findIndex : me.maxIndex; } me.min = labels[me.minIndex]; me.max = labels[me.maxIndex]; }, buildTicks: function() { var me = this; var labels = me.getLabels(); // If we are viewing some subset of labels, slice the original array me.ticks = (me.minIndex === 0 && me.maxIndex === labels.length - 1) ? labels : labels.slice(me.minIndex, me.maxIndex + 1); }, getLabelForIndex: function(index, datasetIndex) { var me = this; var data = me.chart.data; var isHorizontal = me.isHorizontal(); if (data.yLabels && !isHorizontal) { return me.getRightValue(data.datasets[datasetIndex].data[index]); } return me.ticks[index - me.minIndex]; }, // Used to get data value locations. Value can either be an index or a numerical value getPixelForValue: function(value, index) { var me = this; var offset = me.options.offset; // 1 is added because we need the length but we have the indexes var offsetAmt = Math.max((me.maxIndex + 1 - me.minIndex - (offset ? 0 : 1)), 1); // If value is a data object, then index is the index in the data array, // not the index of the scale. We need to change that. var valueCategory; if (value !== undefined && value !== null) { valueCategory = me.isHorizontal() ? value.x : value.y; } if (valueCategory !== undefined || (value !== undefined && isNaN(index))) { var labels = me.getLabels(); value = valueCategory || value; var idx = labels.indexOf(value); index = idx !== -1 ? idx : index; } if (me.isHorizontal()) { var valueWidth = me.width / offsetAmt; var widthOffset = (valueWidth * (index - me.minIndex)); if (offset) { widthOffset += (valueWidth / 2); } return me.left + Math.round(widthOffset); } var valueHeight = me.height / offsetAmt; var heightOffset = (valueHeight * (index - me.minIndex)); if (offset) { heightOffset += (valueHeight / 2); } return me.top + Math.round(heightOffset); }, getPixelForTick: function(index) { return this.getPixelForValue(this.ticks[index], index + this.minIndex, null); }, getValueForPixel: function(pixel) { var me = this; var offset = me.options.offset; var value; var offsetAmt = Math.max((me._ticks.length - (offset ? 0 : 1)), 1); var horz = me.isHorizontal(); var valueDimension = (horz ? me.width : me.height) / offsetAmt; pixel -= horz ? me.left : me.top; if (offset) { pixel -= (valueDimension / 2); } if (pixel <= 0) { value = 0; } else { value = Math.round(pixel / valueDimension); } return value + me.minIndex; }, getBasePixel: function() { return this.bottom; } }); Chart.scaleService.registerScaleType('category', DatasetScale, defaultConfig); }; },{}],53:[function(require,module,exports){ 'use strict'; var defaults = require(25); var helpers = require(45); var Ticks = require(34); module.exports = function(Chart) { var defaultConfig = { position: 'left', ticks: { callback: Ticks.formatters.linear } }; var LinearScale = Chart.LinearScaleBase.extend({ determineDataLimits: function() { var me = this; var opts = me.options; var chart = me.chart; var data = chart.data; var datasets = data.datasets; var isHorizontal = me.isHorizontal(); var DEFAULT_MIN = 0; var DEFAULT_MAX = 1; function IDMatches(meta) { return isHorizontal ? meta.xAxisID === me.id : meta.yAxisID === me.id; } // First Calculate the range me.min = null; me.max = null; var hasStacks = opts.stacked; if (hasStacks === undefined) { helpers.each(datasets, function(dataset, datasetIndex) { if (hasStacks) { return; } var meta = chart.getDatasetMeta(datasetIndex); if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta) && meta.stack !== undefined) { hasStacks = true; } }); } if (opts.stacked || hasStacks) { var valuesPerStack = {}; helpers.each(datasets, function(dataset, datasetIndex) { var meta = chart.getDatasetMeta(datasetIndex); var key = [ meta.type, // we have a separate stack for stack=undefined datasets when the opts.stacked is undefined ((opts.stacked === undefined && meta.stack === undefined) ? datasetIndex : ''), meta.stack ].join('.'); if (valuesPerStack[key] === undefined) { valuesPerStack[key] = { positiveValues: [], negativeValues: [] }; } // Store these per type var positiveValues = valuesPerStack[key].positiveValues; var negativeValues = valuesPerStack[key].negativeValues; if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) { helpers.each(dataset.data, function(rawValue, index) { var value = +me.getRightValue(rawValue); if (isNaN(value) || meta.data[index].hidden) { return; } positiveValues[index] = positiveValues[index] || 0; negativeValues[index] = negativeValues[index] || 0; if (opts.relativePoints) { positiveValues[index] = 100; } else if (value < 0) { negativeValues[index] += value; } else { positiveValues[index] += value; } }); } }); helpers.each(valuesPerStack, function(valuesForType) { var values = valuesForType.positiveValues.concat(valuesForType.negativeValues); var minVal = helpers.min(values); var maxVal = helpers.max(values); me.min = me.min === null ? minVal : Math.min(me.min, minVal); me.max = me.max === null ? maxVal : Math.max(me.max, maxVal); }); } else { helpers.each(datasets, function(dataset, datasetIndex) { var meta = chart.getDatasetMeta(datasetIndex); if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) { helpers.each(dataset.data, function(rawValue, index) { var value = +me.getRightValue(rawValue); if (isNaN(value) || meta.data[index].hidden) { return; } if (me.min === null) { me.min = value; } else if (value < me.min) { me.min = value; } if (me.max === null) { me.max = value; } else if (value > me.max) { me.max = value; } }); } }); } me.min = isFinite(me.min) && !isNaN(me.min) ? me.min : DEFAULT_MIN; me.max = isFinite(me.max) && !isNaN(me.max) ? me.max : DEFAULT_MAX; // Common base implementation to handle ticks.min, ticks.max, ticks.beginAtZero this.handleTickRangeOptions(); }, getTickLimit: function() { var maxTicks; var me = this; var tickOpts = me.options.ticks; if (me.isHorizontal()) { maxTicks = Math.min(tickOpts.maxTicksLimit ? tickOpts.maxTicksLimit : 11, Math.ceil(me.width / 50)); } else { // The factor of 2 used to scale the font size has been experimentally determined. var tickFontSize = helpers.valueOrDefault(tickOpts.fontSize, defaults.global.defaultFontSize); maxTicks = Math.min(tickOpts.maxTicksLimit ? tickOpts.maxTicksLimit : 11, Math.ceil(me.height / (2 * tickFontSize))); } return maxTicks; }, // Called after the ticks are built. We need handleDirectionalChanges: function() { if (!this.isHorizontal()) { // We are in a vertical orientation. The top value is the highest. So reverse the array this.ticks.reverse(); } }, getLabelForIndex: function(index, datasetIndex) { return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]); }, // Utils getPixelForValue: function(value) { // This must be called after fit has been run so that // this.left, this.top, this.right, and this.bottom have been defined var me = this; var start = me.start; var rightValue = +me.getRightValue(value); var pixel; var range = me.end - start; if (me.isHorizontal()) { pixel = me.left + (me.width / range * (rightValue - start)); return Math.round(pixel); } pixel = me.bottom - (me.height / range * (rightValue - start)); return Math.round(pixel); }, getValueForPixel: function(pixel) { var me = this; var isHorizontal = me.isHorizontal(); var innerDimension = isHorizontal ? me.width : me.height; var offset = (isHorizontal ? pixel - me.left : me.bottom - pixel) / innerDimension; return me.start + ((me.end - me.start) * offset); }, getPixelForTick: function(index) { return this.getPixelForValue(this.ticksAsNumbers[index]); } }); Chart.scaleService.registerScaleType('linear', LinearScale, defaultConfig); }; },{"25":25,"34":34,"45":45}],54:[function(require,module,exports){ 'use strict'; var helpers = require(45); var Ticks = require(34); module.exports = function(Chart) { var noop = helpers.noop; Chart.LinearScaleBase = Chart.Scale.extend({ getRightValue: function(value) { if (typeof value === 'string') { return +value; } return Chart.Scale.prototype.getRightValue.call(this, value); }, handleTickRangeOptions: function() { var me = this; var opts = me.options; var tickOpts = opts.ticks; // If we are forcing it to begin at 0, but 0 will already be rendered on the chart, // do nothing since that would make the chart weird. If the user really wants a weird chart // axis, they can manually override it if (tickOpts.beginAtZero) { var minSign = helpers.sign(me.min); var maxSign = helpers.sign(me.max); if (minSign < 0 && maxSign < 0) { // move the top up to 0 me.max = 0; } else if (minSign > 0 && maxSign > 0) { // move the bottom down to 0 me.min = 0; } } var setMin = tickOpts.min !== undefined || tickOpts.suggestedMin !== undefined; var setMax = tickOpts.max !== undefined || tickOpts.suggestedMax !== undefined; if (tickOpts.min !== undefined) { me.min = tickOpts.min; } else if (tickOpts.suggestedMin !== undefined) { if (me.min === null) { me.min = tickOpts.suggestedMin; } else { me.min = Math.min(me.min, tickOpts.suggestedMin); } } if (tickOpts.max !== undefined) { me.max = tickOpts.max; } else if (tickOpts.suggestedMax !== undefined) { if (me.max === null) { me.max = tickOpts.suggestedMax; } else { me.max = Math.max(me.max, tickOpts.suggestedMax); } } if (setMin !== setMax) { // We set the min or the max but not both. // So ensure that our range is good // Inverted or 0 length range can happen when // ticks.min is set, and no datasets are visible if (me.min >= me.max) { if (setMin) { me.max = me.min + 1; } else { me.min = me.max - 1; } } } if (me.min === me.max) { me.max++; if (!tickOpts.beginAtZero) { me.min--; } } }, getTickLimit: noop, handleDirectionalChanges: noop, buildTicks: function() { var me = this; var opts = me.options; var tickOpts = opts.ticks; // Figure out what the max number of ticks we can support it is based on the size of // the axis area. For now, we say that the minimum tick spacing in pixels must be 50 // We also limit the maximum number of ticks to 11 which gives a nice 10 squares on // the graph. Make sure we always have at least 2 ticks var maxTicks = me.getTickLimit(); maxTicks = Math.max(2, maxTicks); var numericGeneratorOptions = { maxTicks: maxTicks, min: tickOpts.min, max: tickOpts.max, stepSize: helpers.valueOrDefault(tickOpts.fixedStepSize, tickOpts.stepSize) }; var ticks = me.ticks = Ticks.generators.linear(numericGeneratorOptions, me); me.handleDirectionalChanges(); // At this point, we need to update our max and min given the tick values since we have expanded the // range of the scale me.max = helpers.max(ticks); me.min = helpers.min(ticks); if (tickOpts.reverse) { ticks.reverse(); me.start = me.max; me.end = me.min; } else { me.start = me.min; me.end = me.max; } }, convertTicksToLabels: function() { var me = this; me.ticksAsNumbers = me.ticks.slice(); me.zeroLineIndex = me.ticks.indexOf(0); Chart.Scale.prototype.convertTicksToLabels.call(me); } }); }; },{"34":34,"45":45}],55:[function(require,module,exports){ 'use strict'; var helpers = require(45); var Ticks = require(34); module.exports = function(Chart) { var defaultConfig = { position: 'left', // label settings ticks: { callback: Ticks.formatters.logarithmic } }; var LogarithmicScale = Chart.Scale.extend({ determineDataLimits: function() { var me = this; var opts = me.options; var tickOpts = opts.ticks; var chart = me.chart; var data = chart.data; var datasets = data.datasets; var valueOrDefault = helpers.valueOrDefault; var isHorizontal = me.isHorizontal(); function IDMatches(meta) { return isHorizontal ? meta.xAxisID === me.id : meta.yAxisID === me.id; } // Calculate Range me.min = null; me.max = null; me.minNotZero = null; var hasStacks = opts.stacked; if (hasStacks === undefined) { helpers.each(datasets, function(dataset, datasetIndex) { if (hasStacks) { return; } var meta = chart.getDatasetMeta(datasetIndex); if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta) && meta.stack !== undefined) { hasStacks = true; } }); } if (opts.stacked || hasStacks) { var valuesPerStack = {}; helpers.each(datasets, function(dataset, datasetIndex) { var meta = chart.getDatasetMeta(datasetIndex); var key = [ meta.type, // we have a separate stack for stack=undefined datasets when the opts.stacked is undefined ((opts.stacked === undefined && meta.stack === undefined) ? datasetIndex : ''), meta.stack ].join('.'); if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) { if (valuesPerStack[key] === undefined) { valuesPerStack[key] = []; } helpers.each(dataset.data, function(rawValue, index) { var values = valuesPerStack[key]; var value = +me.getRightValue(rawValue); if (isNaN(value) || meta.data[index].hidden) { return; } values[index] = values[index] || 0; if (opts.relativePoints) { values[index] = 100; } else { // Don't need to split positive and negative since the log scale can't handle a 0 crossing values[index] += value; } }); } }); helpers.each(valuesPerStack, function(valuesForType) { var minVal = helpers.min(valuesForType); var maxVal = helpers.max(valuesForType); me.min = me.min === null ? minVal : Math.min(me.min, minVal); me.max = me.max === null ? maxVal : Math.max(me.max, maxVal); }); } else { helpers.each(datasets, function(dataset, datasetIndex) { var meta = chart.getDatasetMeta(datasetIndex); if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) { helpers.each(dataset.data, function(rawValue, index) { var value = +me.getRightValue(rawValue); if (isNaN(value) || meta.data[index].hidden) { return; } if (me.min === null) { me.min = value; } else if (value < me.min) { me.min = value; } if (me.max === null) { me.max = value; } else if (value > me.max) { me.max = value; } if (value !== 0 && (me.minNotZero === null || value < me.minNotZero)) { me.minNotZero = value; } }); } }); } me.min = valueOrDefault(tickOpts.min, me.min); me.max = valueOrDefault(tickOpts.max, me.max); if (me.min === me.max) { if (me.min !== 0 && me.min !== null) { me.min = Math.pow(10, Math.floor(helpers.log10(me.min)) - 1); me.max = Math.pow(10, Math.floor(helpers.log10(me.max)) + 1); } else { me.min = 1; me.max = 10; } } }, buildTicks: function() { var me = this; var opts = me.options; var tickOpts = opts.ticks; var generationOptions = { min: tickOpts.min, max: tickOpts.max }; var ticks = me.ticks = Ticks.generators.logarithmic(generationOptions, me); if (!me.isHorizontal()) { // We are in a vertical orientation. The top value is the highest. So reverse the array ticks.reverse(); } // At this point, we need to update our max and min given the tick values since we have expanded the // range of the scale me.max = helpers.max(ticks); me.min = helpers.min(ticks); if (tickOpts.reverse) { ticks.reverse(); me.start = me.max; me.end = me.min; } else { me.start = me.min; me.end = me.max; } }, convertTicksToLabels: function() { this.tickValues = this.ticks.slice(); Chart.Scale.prototype.convertTicksToLabels.call(this); }, // Get the correct tooltip label getLabelForIndex: function(index, datasetIndex) { return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]); }, getPixelForTick: function(index) { return this.getPixelForValue(this.tickValues[index]); }, getPixelForValue: function(value) { var me = this; var start = me.start; var newVal = +me.getRightValue(value); var opts = me.options; var tickOpts = opts.ticks; var innerDimension, pixel, range; if (me.isHorizontal()) { range = helpers.log10(me.end) - helpers.log10(start); // todo: if start === 0 if (newVal === 0) { pixel = me.left; } else { innerDimension = me.width; pixel = me.left + (innerDimension / range * (helpers.log10(newVal) - helpers.log10(start))); } } else { // Bottom - top since pixels increase downward on a screen innerDimension = me.height; if (start === 0 && !tickOpts.reverse) { range = helpers.log10(me.end) - helpers.log10(me.minNotZero); if (newVal === start) { pixel = me.bottom; } else if (newVal === me.minNotZero) { pixel = me.bottom - innerDimension * 0.02; } else { pixel = me.bottom - innerDimension * 0.02 - (innerDimension * 0.98 / range * (helpers.log10(newVal) - helpers.log10(me.minNotZero))); } } else if (me.end === 0 && tickOpts.reverse) { range = helpers.log10(me.start) - helpers.log10(me.minNotZero); if (newVal === me.end) { pixel = me.top; } else if (newVal === me.minNotZero) { pixel = me.top + innerDimension * 0.02; } else { pixel = me.top + innerDimension * 0.02 + (innerDimension * 0.98 / range * (helpers.log10(newVal) - helpers.log10(me.minNotZero))); } } else if (newVal === 0) { pixel = tickOpts.reverse ? me.top : me.bottom; } else { range = helpers.log10(me.end) - helpers.log10(start); innerDimension = me.height; pixel = me.bottom - (innerDimension / range * (helpers.log10(newVal) - helpers.log10(start))); } } return pixel; }, getValueForPixel: function(pixel) { var me = this; var range = helpers.log10(me.end) - helpers.log10(me.start); var value, innerDimension; if (me.isHorizontal()) { innerDimension = me.width; value = me.start * Math.pow(10, (pixel - me.left) * range / innerDimension); } else { // todo: if start === 0 innerDimension = me.height; value = Math.pow(10, (me.bottom - pixel) * range / innerDimension) / me.start; } return value; } }); Chart.scaleService.registerScaleType('logarithmic', LogarithmicScale, defaultConfig); }; },{"34":34,"45":45}],56:[function(require,module,exports){ 'use strict'; var defaults = require(25); var helpers = require(45); var Ticks = require(34); module.exports = function(Chart) { var globalDefaults = defaults.global; var defaultConfig = { display: true, // Boolean - Whether to animate scaling the chart from the centre animate: true, position: 'chartArea', angleLines: { display: true, color: 'rgba(0, 0, 0, 0.1)', lineWidth: 1 }, gridLines: { circular: false }, // label settings ticks: { // Boolean - Show a backdrop to the scale label showLabelBackdrop: true, // String - The colour of the label backdrop backdropColor: 'rgba(255,255,255,0.75)', // Number - The backdrop padding above & below the label in pixels backdropPaddingY: 2, // Number - The backdrop padding to the side of the label in pixels backdropPaddingX: 2, callback: Ticks.formatters.linear }, pointLabels: { // Boolean - if true, show point labels display: true, // Number - Point label font size in pixels fontSize: 10, // Function - Used to convert point labels callback: function(label) { return label; } } }; function getValueCount(scale) { var opts = scale.options; return opts.angleLines.display || opts.pointLabels.display ? scale.chart.data.labels.length : 0; } function getPointLabelFontOptions(scale) { var pointLabelOptions = scale.options.pointLabels; var fontSize = helpers.valueOrDefault(pointLabelOptions.fontSize, globalDefaults.defaultFontSize); var fontStyle = helpers.valueOrDefault(pointLabelOptions.fontStyle, globalDefaults.defaultFontStyle); var fontFamily = helpers.valueOrDefault(pointLabelOptions.fontFamily, globalDefaults.defaultFontFamily); var font = helpers.fontString(fontSize, fontStyle, fontFamily); return { size: fontSize, style: fontStyle, family: fontFamily, font: font }; } function measureLabelSize(ctx, fontSize, label) { if (helpers.isArray(label)) { return { w: helpers.longestText(ctx, ctx.font, label), h: (label.length * fontSize) + ((label.length - 1) * 1.5 * fontSize) }; } return { w: ctx.measureText(label).width, h: fontSize }; } function determineLimits(angle, pos, size, min, max) { if (angle === min || angle === max) { return { start: pos - (size / 2), end: pos + (size / 2) }; } else if (angle < min || angle > max) { return { start: pos - size - 5, end: pos }; } return { start: pos, end: pos + size + 5 }; } /** * Helper function to fit a radial linear scale with point labels */ function fitWithPointLabels(scale) { /* * Right, this is really confusing and there is a lot of maths going on here * The gist of the problem is here: https://gist.github.com/nnnick/696cc9c55f4b0beb8fe9 * * Reaction: https://dl.dropboxusercontent.com/u/34601363/toomuchscience.gif * * Solution: * * We assume the radius of the polygon is half the size of the canvas at first * at each index we check if the text overlaps. * * Where it does, we store that angle and that index. * * After finding the largest index and angle we calculate how much we need to remove * from the shape radius to move the point inwards by that x. * * We average the left and right distances to get the maximum shape radius that can fit in the box * along with labels. * * Once we have that, we can find the centre point for the chart, by taking the x text protrusion * on each side, removing that from the size, halving it and adding the left x protrusion width. * * This will mean we have a shape fitted to the canvas, as large as it can be with the labels * and position it in the most space efficient manner * * https://dl.dropboxusercontent.com/u/34601363/yeahscience.gif */ var plFont = getPointLabelFontOptions(scale); // Get maximum radius of the polygon. Either half the height (minus the text width) or half the width. // Use this to calculate the offset + change. - Make sure L/R protrusion is at least 0 to stop issues with centre points var largestPossibleRadius = Math.min(scale.height / 2, scale.width / 2); var furthestLimits = { r: scale.width, l: 0, t: scale.height, b: 0 }; var furthestAngles = {}; var i, textSize, pointPosition; scale.ctx.font = plFont.font; scale._pointLabelSizes = []; var valueCount = getValueCount(scale); for (i = 0; i < valueCount; i++) { pointPosition = scale.getPointPosition(i, largestPossibleRadius); textSize = measureLabelSize(scale.ctx, plFont.size, scale.pointLabels[i] || ''); scale._pointLabelSizes[i] = textSize; // Add quarter circle to make degree 0 mean top of circle var angleRadians = scale.getIndexAngle(i); var angle = helpers.toDegrees(angleRadians) % 360; var hLimits = determineLimits(angle, pointPosition.x, textSize.w, 0, 180); var vLimits = determineLimits(angle, pointPosition.y, textSize.h, 90, 270); if (hLimits.start < furthestLimits.l) { furthestLimits.l = hLimits.start; furthestAngles.l = angleRadians; } if (hLimits.end > furthestLimits.r) { furthestLimits.r = hLimits.end; furthestAngles.r = angleRadians; } if (vLimits.start < furthestLimits.t) { furthestLimits.t = vLimits.start; furthestAngles.t = angleRadians; } if (vLimits.end > furthestLimits.b) { furthestLimits.b = vLimits.end; furthestAngles.b = angleRadians; } } scale.setReductions(largestPossibleRadius, furthestLimits, furthestAngles); } /** * Helper function to fit a radial linear scale with no point labels */ function fit(scale) { var largestPossibleRadius = Math.min(scale.height / 2, scale.width / 2); scale.drawingArea = Math.round(largestPossibleRadius); scale.setCenterPoint(0, 0, 0, 0); } function getTextAlignForAngle(angle) { if (angle === 0 || angle === 180) { return 'center'; } else if (angle < 180) { return 'left'; } return 'right'; } function fillText(ctx, text, position, fontSize) { if (helpers.isArray(text)) { var y = position.y; var spacing = 1.5 * fontSize; for (var i = 0; i < text.length; ++i) { ctx.fillText(text[i], position.x, y); y += spacing; } } else { ctx.fillText(text, position.x, position.y); } } function adjustPointPositionForLabelHeight(angle, textSize, position) { if (angle === 90 || angle === 270) { position.y -= (textSize.h / 2); } else if (angle > 270 || angle < 90) { position.y -= textSize.h; } } function drawPointLabels(scale) { var ctx = scale.ctx; var valueOrDefault = helpers.valueOrDefault; var opts = scale.options; var angleLineOpts = opts.angleLines; var pointLabelOpts = opts.pointLabels; ctx.lineWidth = angleLineOpts.lineWidth; ctx.strokeStyle = angleLineOpts.color; var outerDistance = scale.getDistanceFromCenterForValue(opts.ticks.reverse ? scale.min : scale.max); // Point Label Font var plFont = getPointLabelFontOptions(scale); ctx.textBaseline = 'top'; for (var i = getValueCount(scale) - 1; i >= 0; i--) { if (angleLineOpts.display) { var outerPosition = scale.getPointPosition(i, outerDistance); ctx.beginPath(); ctx.moveTo(scale.xCenter, scale.yCenter); ctx.lineTo(outerPosition.x, outerPosition.y); ctx.stroke(); ctx.closePath(); } if (pointLabelOpts.display) { // Extra 3px out for some label spacing var pointLabelPosition = scale.getPointPosition(i, outerDistance + 5); // Keep this in loop since we may support array properties here var pointLabelFontColor = valueOrDefault(pointLabelOpts.fontColor, globalDefaults.defaultFontColor); ctx.font = plFont.font; ctx.fillStyle = pointLabelFontColor; var angleRadians = scale.getIndexAngle(i); var angle = helpers.toDegrees(angleRadians); ctx.textAlign = getTextAlignForAngle(angle); adjustPointPositionForLabelHeight(angle, scale._pointLabelSizes[i], pointLabelPosition); fillText(ctx, scale.pointLabels[i] || '', pointLabelPosition, plFont.size); } } } function drawRadiusLine(scale, gridLineOpts, radius, index) { var ctx = scale.ctx; ctx.strokeStyle = helpers.valueAtIndexOrDefault(gridLineOpts.color, index - 1); ctx.lineWidth = helpers.valueAtIndexOrDefault(gridLineOpts.lineWidth, index - 1); if (scale.options.gridLines.circular) { // Draw circular arcs between the points ctx.beginPath(); ctx.arc(scale.xCenter, scale.yCenter, radius, 0, Math.PI * 2); ctx.closePath(); ctx.stroke(); } else { // Draw straight lines connecting each index var valueCount = getValueCount(scale); if (valueCount === 0) { return; } ctx.beginPath(); var pointPosition = scale.getPointPosition(0, radius); ctx.moveTo(pointPosition.x, pointPosition.y); for (var i = 1; i < valueCount; i++) { pointPosition = scale.getPointPosition(i, radius); ctx.lineTo(pointPosition.x, pointPosition.y); } ctx.closePath(); ctx.stroke(); } } function numberOrZero(param) { return helpers.isNumber(param) ? param : 0; } var LinearRadialScale = Chart.LinearScaleBase.extend({ setDimensions: function() { var me = this; var opts = me.options; var tickOpts = opts.ticks; // Set the unconstrained dimension before label rotation me.width = me.maxWidth; me.height = me.maxHeight; me.xCenter = Math.round(me.width / 2); me.yCenter = Math.round(me.height / 2); var minSize = helpers.min([me.height, me.width]); var tickFontSize = helpers.valueOrDefault(tickOpts.fontSize, globalDefaults.defaultFontSize); me.drawingArea = opts.display ? (minSize / 2) - (tickFontSize / 2 + tickOpts.backdropPaddingY) : (minSize / 2); }, determineDataLimits: function() { var me = this; var chart = me.chart; var min = Number.POSITIVE_INFINITY; var max = Number.NEGATIVE_INFINITY; helpers.each(chart.data.datasets, function(dataset, datasetIndex) { if (chart.isDatasetVisible(datasetIndex)) { var meta = chart.getDatasetMeta(datasetIndex); helpers.each(dataset.data, function(rawValue, index) { var value = +me.getRightValue(rawValue); if (isNaN(value) || meta.data[index].hidden) { return; } min = Math.min(value, min); max = Math.max(value, max); }); } }); me.min = (min === Number.POSITIVE_INFINITY ? 0 : min); me.max = (max === Number.NEGATIVE_INFINITY ? 0 : max); // Common base implementation to handle ticks.min, ticks.max, ticks.beginAtZero me.handleTickRangeOptions(); }, getTickLimit: function() { var tickOpts = this.options.ticks; var tickFontSize = helpers.valueOrDefault(tickOpts.fontSize, globalDefaults.defaultFontSize); return Math.min(tickOpts.maxTicksLimit ? tickOpts.maxTicksLimit : 11, Math.ceil(this.drawingArea / (1.5 * tickFontSize))); }, convertTicksToLabels: function() { var me = this; Chart.LinearScaleBase.prototype.convertTicksToLabels.call(me); // Point labels me.pointLabels = me.chart.data.labels.map(me.options.pointLabels.callback, me); }, getLabelForIndex: function(index, datasetIndex) { return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]); }, fit: function() { if (this.options.pointLabels.display) { fitWithPointLabels(this); } else { fit(this); } }, /** * Set radius reductions and determine new radius and center point * @private */ setReductions: function(largestPossibleRadius, furthestLimits, furthestAngles) { var me = this; var radiusReductionLeft = furthestLimits.l / Math.sin(furthestAngles.l); var radiusReductionRight = Math.max(furthestLimits.r - me.width, 0) / Math.sin(furthestAngles.r); var radiusReductionTop = -furthestLimits.t / Math.cos(furthestAngles.t); var radiusReductionBottom = -Math.max(furthestLimits.b - me.height, 0) / Math.cos(furthestAngles.b); radiusReductionLeft = numberOrZero(radiusReductionLeft); radiusReductionRight = numberOrZero(radiusReductionRight); radiusReductionTop = numberOrZero(radiusReductionTop); radiusReductionBottom = numberOrZero(radiusReductionBottom); me.drawingArea = Math.min( Math.round(largestPossibleRadius - (radiusReductionLeft + radiusReductionRight) / 2), Math.round(largestPossibleRadius - (radiusReductionTop + radiusReductionBottom) / 2)); me.setCenterPoint(radiusReductionLeft, radiusReductionRight, radiusReductionTop, radiusReductionBottom); }, setCenterPoint: function(leftMovement, rightMovement, topMovement, bottomMovement) { var me = this; var maxRight = me.width - rightMovement - me.drawingArea; var maxLeft = leftMovement + me.drawingArea; var maxTop = topMovement + me.drawingArea; var maxBottom = me.height - bottomMovement - me.drawingArea; me.xCenter = Math.round(((maxLeft + maxRight) / 2) + me.left); me.yCenter = Math.round(((maxTop + maxBottom) / 2) + me.top); }, getIndexAngle: function(index) { var angleMultiplier = (Math.PI * 2) / getValueCount(this); var startAngle = this.chart.options && this.chart.options.startAngle ? this.chart.options.startAngle : 0; var startAngleRadians = startAngle * Math.PI * 2 / 360; // Start from the top instead of right, so remove a quarter of the circle return index * angleMultiplier + startAngleRadians; }, getDistanceFromCenterForValue: function(value) { var me = this; if (value === null) { return 0; // null always in center } // Take into account half font size + the yPadding of the top value var scalingFactor = me.drawingArea / (me.max - me.min); if (me.options.ticks.reverse) { return (me.max - value) * scalingFactor; } return (value - me.min) * scalingFactor; }, getPointPosition: function(index, distanceFromCenter) { var me = this; var thisAngle = me.getIndexAngle(index) - (Math.PI / 2); return { x: Math.round(Math.cos(thisAngle) * distanceFromCenter) + me.xCenter, y: Math.round(Math.sin(thisAngle) * distanceFromCenter) + me.yCenter }; }, getPointPositionForValue: function(index, value) { return this.getPointPosition(index, this.getDistanceFromCenterForValue(value)); }, getBasePosition: function() { var me = this; var min = me.min; var max = me.max; return me.getPointPositionForValue(0, me.beginAtZero ? 0 : min < 0 && max < 0 ? max : min > 0 && max > 0 ? min : 0); }, draw: function() { var me = this; var opts = me.options; var gridLineOpts = opts.gridLines; var tickOpts = opts.ticks; var valueOrDefault = helpers.valueOrDefault; if (opts.display) { var ctx = me.ctx; var startAngle = this.getIndexAngle(0); // Tick Font var tickFontSize = valueOrDefault(tickOpts.fontSize, globalDefaults.defaultFontSize); var tickFontStyle = valueOrDefault(tickOpts.fontStyle, globalDefaults.defaultFontStyle); var tickFontFamily = valueOrDefault(tickOpts.fontFamily, globalDefaults.defaultFontFamily); var tickLabelFont = helpers.fontString(tickFontSize, tickFontStyle, tickFontFamily); helpers.each(me.ticks, function(label, index) { // Don't draw a centre value (if it is minimum) if (index > 0 || tickOpts.reverse) { var yCenterOffset = me.getDistanceFromCenterForValue(me.ticksAsNumbers[index]); // Draw circular lines around the scale if (gridLineOpts.display && index !== 0) { drawRadiusLine(me, gridLineOpts, yCenterOffset, index); } if (tickOpts.display) { var tickFontColor = valueOrDefault(tickOpts.fontColor, globalDefaults.defaultFontColor); ctx.font = tickLabelFont; ctx.save(); ctx.translate(me.xCenter, me.yCenter); ctx.rotate(startAngle); if (tickOpts.showLabelBackdrop) { var labelWidth = ctx.measureText(label).width; ctx.fillStyle = tickOpts.backdropColor; ctx.fillRect( -labelWidth / 2 - tickOpts.backdropPaddingX, -yCenterOffset - tickFontSize / 2 - tickOpts.backdropPaddingY, labelWidth + tickOpts.backdropPaddingX * 2, tickFontSize + tickOpts.backdropPaddingY * 2 ); } ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; ctx.fillStyle = tickFontColor; ctx.fillText(label, 0, -yCenterOffset); ctx.restore(); } } }); if (opts.angleLines.display || opts.pointLabels.display) { drawPointLabels(me); } } } }); Chart.scaleService.registerScaleType('radialLinear', LinearRadialScale, defaultConfig); }; },{"25":25,"34":34,"45":45}],57:[function(require,module,exports){ /* global window: false */ 'use strict'; var moment = require(6); moment = typeof moment === 'function' ? moment : window.moment; var defaults = require(25); var helpers = require(45); // Integer constants are from the ES6 spec. var MIN_INTEGER = Number.MIN_SAFE_INTEGER || -9007199254740991; var MAX_INTEGER = Number.MAX_SAFE_INTEGER || 9007199254740991; var INTERVALS = { millisecond: { common: true, size: 1, steps: [1, 2, 5, 10, 20, 50, 100, 250, 500] }, second: { common: true, size: 1000, steps: [1, 2, 5, 10, 30] }, minute: { common: true, size: 60000, steps: [1, 2, 5, 10, 30] }, hour: { common: true, size: 3600000, steps: [1, 2, 3, 6, 12] }, day: { common: true, size: 86400000, steps: [1, 2, 5] }, week: { common: false, size: 604800000, steps: [1, 2, 3, 4] }, month: { common: true, size: 2.628e9, steps: [1, 2, 3] }, quarter: { common: false, size: 7.884e9, steps: [1, 2, 3, 4] }, year: { common: true, size: 3.154e10 } }; var UNITS = Object.keys(INTERVALS); function sorter(a, b) { return a - b; } function arrayUnique(items) { var hash = {}; var out = []; var i, ilen, item; for (i = 0, ilen = items.length; i < ilen; ++i) { item = items[i]; if (!hash[item]) { hash[item] = true; out.push(item); } } return out; } /** * Returns an array of {time, pos} objects used to interpolate a specific `time` or position * (`pos`) on the scale, by searching entries before and after the requested value. `pos` is * a decimal between 0 and 1: 0 being the start of the scale (left or top) and 1 the other * extremity (left + width or top + height). Note that it would be more optimized to directly * store pre-computed pixels, but the scale dimensions are not guaranteed at the time we need * to create the lookup table. The table ALWAYS contains at least two items: min and max. * * @param {Number[]} timestamps - timestamps sorted from lowest to highest. * @param {String} distribution - If 'linear', timestamps will be spread linearly along the min * and max range, so basically, the table will contains only two items: {min, 0} and {max, 1}. * If 'series', timestamps will be positioned at the same distance from each other. In this * case, only timestamps that break the time linearity are registered, meaning that in the * best case, all timestamps are linear, the table contains only min and max. */ function buildLookupTable(timestamps, min, max, distribution) { if (distribution === 'linear' || !timestamps.length) { return [ {time: min, pos: 0}, {time: max, pos: 1} ]; } var table = []; var items = [min]; var i, ilen, prev, curr, next; for (i = 0, ilen = timestamps.length; i < ilen; ++i) { curr = timestamps[i]; if (curr > min && curr < max) { items.push(curr); } } items.push(max); for (i = 0, ilen = items.length; i < ilen; ++i) { next = items[i + 1]; prev = items[i - 1]; curr = items[i]; // only add points that breaks the scale linearity if (prev === undefined || next === undefined || Math.round((next + prev) / 2) !== curr) { table.push({time: curr, pos: i / (ilen - 1)}); } } return table; } // @see adapted from http://www.anujgakhar.com/2014/03/01/binary-search-in-javascript/ function lookup(table, key, value) { var lo = 0; var hi = table.length - 1; var mid, i0, i1; while (lo >= 0 && lo <= hi) { mid = (lo + hi) >> 1; i0 = table[mid - 1] || null; i1 = table[mid]; if (!i0) { // given value is outside table (before first item) return {lo: null, hi: i1}; } else if (i1[key] < value) { lo = mid + 1; } else if (i0[key] > value) { hi = mid - 1; } else { return {lo: i0, hi: i1}; } } // given value is outside table (after last item) return {lo: i1, hi: null}; } /** * Linearly interpolates the given source `value` using the table items `skey` values and * returns the associated `tkey` value. For example, interpolate(table, 'time', 42, 'pos') * returns the position for a timestamp equal to 42. If value is out of bounds, values at * index [0, 1] or [n - 1, n] are used for the interpolation. */ function interpolate(table, skey, sval, tkey) { var range = lookup(table, skey, sval); // Note: the lookup table ALWAYS contains at least 2 items (min and max) var prev = !range.lo ? table[0] : !range.hi ? table[table.length - 2] : range.lo; var next = !range.lo ? table[1] : !range.hi ? table[table.length - 1] : range.hi; var span = next[skey] - prev[skey]; var ratio = span ? (sval - prev[skey]) / span : 0; var offset = (next[tkey] - prev[tkey]) * ratio; return prev[tkey] + offset; } /** * Convert the given value to a moment object using the given time options. * @see http://momentjs.com/docs/#/parsing/ */ function momentify(value, options) { var parser = options.parser; var format = options.parser || options.format; if (typeof parser === 'function') { return parser(value); } if (typeof value === 'string' && typeof format === 'string') { return moment(value, format); } if (!(value instanceof moment)) { value = moment(value); } if (value.isValid()) { return value; } // Labels are in an incompatible moment format and no `parser` has been provided. // The user might still use the deprecated `format` option to convert his inputs. if (typeof format === 'function') { return format(value); } return value; } function parse(input, scale) { if (helpers.isNullOrUndef(input)) { return null; } var options = scale.options.time; var value = momentify(scale.getRightValue(input), options); if (!value.isValid()) { return null; } if (options.round) { value.startOf(options.round); } return value.valueOf(); } /** * Returns the number of unit to skip to be able to display up to `capacity` number of ticks * in `unit` for the given `min` / `max` range and respecting the interval steps constraints. */ function determineStepSize(min, max, unit, capacity) { var range = max - min; var interval = INTERVALS[unit]; var milliseconds = interval.size; var steps = interval.steps; var i, ilen, factor; if (!steps) { return Math.ceil(range / ((capacity || 1) * milliseconds)); } for (i = 0, ilen = steps.length; i < ilen; ++i) { factor = steps[i]; if (Math.ceil(range / (milliseconds * factor)) <= capacity) { break; } } return factor; } /** * Figures out what unit results in an appropriate number of auto-generated ticks */ function determineUnitForAutoTicks(minUnit, min, max, capacity) { var ilen = UNITS.length; var i, interval, factor; for (i = UNITS.indexOf(minUnit); i < ilen - 1; ++i) { interval = INTERVALS[UNITS[i]]; factor = interval.steps ? interval.steps[interval.steps.length - 1] : MAX_INTEGER; if (interval.common && Math.ceil((max - min) / (factor * interval.size)) <= capacity) { return UNITS[i]; } } return UNITS[ilen - 1]; } /** * Figures out what unit to format a set of ticks with */ function determineUnitForFormatting(ticks, minUnit, min, max) { var duration = moment.duration(moment(max).diff(moment(min))); var ilen = UNITS.length; var i, unit; for (i = ilen - 1; i >= UNITS.indexOf(minUnit); i--) { unit = UNITS[i]; if (INTERVALS[unit].common && duration.as(unit) >= ticks.length) { return unit; } } return UNITS[minUnit ? UNITS.indexOf(minUnit) : 0]; } function determineMajorUnit(unit) { for (var i = UNITS.indexOf(unit) + 1, ilen = UNITS.length; i < ilen; ++i) { if (INTERVALS[UNITS[i]].common) { return UNITS[i]; } } } /** * Generates a maximum of `capacity` timestamps between min and max, rounded to the * `minor` unit, aligned on the `major` unit and using the given scale time `options`. * Important: this method can return ticks outside the min and max range, it's the * responsibility of the calling code to clamp values if needed. */ function generate(min, max, capacity, options) { var timeOpts = options.time; var minor = timeOpts.unit || determineUnitForAutoTicks(timeOpts.minUnit, min, max, capacity); var major = determineMajorUnit(minor); var stepSize = helpers.valueOrDefault(timeOpts.stepSize, timeOpts.unitStepSize); var weekday = minor === 'week' ? timeOpts.isoWeekday : false; var majorTicksEnabled = options.ticks.major.enabled; var interval = INTERVALS[minor]; var first = moment(min); var last = moment(max); var ticks = []; var time; if (!stepSize) { stepSize = determineStepSize(min, max, minor, capacity); } // For 'week' unit, handle the first day of week option if (weekday) { first = first.isoWeekday(weekday); last = last.isoWeekday(weekday); } // Align first/last ticks on unit first = first.startOf(weekday ? 'day' : minor); last = last.startOf(weekday ? 'day' : minor); // Make sure that the last tick include max if (last < max) { last.add(1, minor); } time = moment(first); if (majorTicksEnabled && major && !weekday && !timeOpts.round) { // Align the first tick on the previous `minor` unit aligned on the `major` unit: // we first aligned time on the previous `major` unit then add the number of full // stepSize there is between first and the previous major time. time.startOf(major); time.add(~~((first - time) / (interval.size * stepSize)) * stepSize, minor); } for (; time < last; time.add(stepSize, minor)) { ticks.push(+time); } ticks.push(+time); return ticks; } /** * Returns the right and left offsets from edges in the form of {left, right}. * Offsets are added when the `offset` option is true. */ function computeOffsets(table, ticks, min, max, options) { var left = 0; var right = 0; var upper, lower; if (options.offset && ticks.length) { if (!options.time.min) { upper = ticks.length > 1 ? ticks[1] : max; lower = ticks[0]; left = ( interpolate(table, 'time', upper, 'pos') - interpolate(table, 'time', lower, 'pos') ) / 2; } if (!options.time.max) { upper = ticks[ticks.length - 1]; lower = ticks.length > 1 ? ticks[ticks.length - 2] : min; right = ( interpolate(table, 'time', upper, 'pos') - interpolate(table, 'time', lower, 'pos') ) / 2; } } return {left: left, right: right}; } function ticksFromTimestamps(values, majorUnit) { var ticks = []; var i, ilen, value, major; for (i = 0, ilen = values.length; i < ilen; ++i) { value = values[i]; major = majorUnit ? value === +moment(value).startOf(majorUnit) : false; ticks.push({ value: value, major: major }); } return ticks; } module.exports = function(Chart) { var defaultConfig = { position: 'bottom', /** * Data distribution along the scale: * - 'linear': data are spread according to their time (distances can vary), * - 'series': data are spread at the same distance from each other. * @see https://github.com/chartjs/Chart.js/pull/4507 * @since 2.7.0 */ distribution: 'linear', /** * Scale boundary strategy (bypassed by min/max time options) * - `data`: make sure data are fully visible, ticks outside are removed * - `ticks`: make sure ticks are fully visible, data outside are truncated * @see https://github.com/chartjs/Chart.js/pull/4556 * @since 2.7.0 */ bounds: 'data', time: { parser: false, // false == a pattern string from http://momentjs.com/docs/#/parsing/string-format/ or a custom callback that converts its argument to a moment format: false, // DEPRECATED false == date objects, moment object, callback or a pattern string from http://momentjs.com/docs/#/parsing/string-format/ unit: false, // false == automatic or override with week, month, year, etc. round: false, // none, or override with week, month, year, etc. displayFormat: false, // DEPRECATED isoWeekday: false, // override week start day - see http://momentjs.com/docs/#/get-set/iso-weekday/ minUnit: 'millisecond', // defaults to unit's corresponding unitFormat below or override using pattern string from http://momentjs.com/docs/#/displaying/format/ displayFormats: { millisecond: 'h:mm:ss.SSS a', // 11:20:01.123 AM, second: 'h:mm:ss a', // 11:20:01 AM minute: 'h:mm a', // 11:20 AM hour: 'hA', // 5PM day: 'MMM D', // Sep 4 week: 'll', // Week 46, or maybe "[W]WW - YYYY" ? month: 'MMM YYYY', // Sept 2015 quarter: '[Q]Q - YYYY', // Q3 year: 'YYYY' // 2015 }, }, ticks: { autoSkip: false, /** * Ticks generation input values: * - 'auto': generates "optimal" ticks based on scale size and time options. * - 'data': generates ticks from data (including labels from data {t|x|y} objects). * - 'labels': generates ticks from user given `data.labels` values ONLY. * @see https://github.com/chartjs/Chart.js/pull/4507 * @since 2.7.0 */ source: 'auto', major: { enabled: false } } }; var TimeScale = Chart.Scale.extend({ initialize: function() { if (!moment) { throw new Error('Chart.js - Moment.js could not be found! You must include it before Chart.js to use the time scale. Download at https://momentjs.com'); } this.mergeTicksOptions(); Chart.Scale.prototype.initialize.call(this); }, update: function() { var me = this; var options = me.options; // DEPRECATIONS: output a message only one time per update if (options.time && options.time.format) { console.warn('options.time.format is deprecated and replaced by options.time.parser.'); } return Chart.Scale.prototype.update.apply(me, arguments); }, /** * Allows data to be referenced via 't' attribute */ getRightValue: function(rawValue) { if (rawValue && rawValue.t !== undefined) { rawValue = rawValue.t; } return Chart.Scale.prototype.getRightValue.call(this, rawValue); }, determineDataLimits: function() { var me = this; var chart = me.chart; var timeOpts = me.options.time; var min = MAX_INTEGER; var max = MIN_INTEGER; var timestamps = []; var datasets = []; var labels = []; var i, j, ilen, jlen, data, timestamp; // Convert labels to timestamps for (i = 0, ilen = chart.data.labels.length; i < ilen; ++i) { labels.push(parse(chart.data.labels[i], me)); } // Convert data to timestamps for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) { if (chart.isDatasetVisible(i)) { data = chart.data.datasets[i].data; // Let's consider that all data have the same format. if (helpers.isObject(data[0])) { datasets[i] = []; for (j = 0, jlen = data.length; j < jlen; ++j) { timestamp = parse(data[j], me); timestamps.push(timestamp); datasets[i][j] = timestamp; } } else { timestamps.push.apply(timestamps, labels); datasets[i] = labels.slice(0); } } else { datasets[i] = []; } } if (labels.length) { // Sort labels **after** data have been converted labels = arrayUnique(labels).sort(sorter); min = Math.min(min, labels[0]); max = Math.max(max, labels[labels.length - 1]); } if (timestamps.length) { timestamps = arrayUnique(timestamps).sort(sorter); min = Math.min(min, timestamps[0]); max = Math.max(max, timestamps[timestamps.length - 1]); } min = parse(timeOpts.min, me) || min; max = parse(timeOpts.max, me) || max; // In case there is no valid min/max, let's use today limits min = min === MAX_INTEGER ? +moment().startOf('day') : min; max = max === MIN_INTEGER ? +moment().endOf('day') + 1 : max; // Make sure that max is strictly higher than min (required by the lookup table) me.min = Math.min(min, max); me.max = Math.max(min + 1, max); // PRIVATE me._horizontal = me.isHorizontal(); me._table = []; me._timestamps = { data: timestamps, datasets: datasets, labels: labels }; }, buildTicks: function() { var me = this; var min = me.min; var max = me.max; var options = me.options; var timeOpts = options.time; var timestamps = []; var ticks = []; var i, ilen, timestamp; switch (options.ticks.source) { case 'data': timestamps = me._timestamps.data; break; case 'labels': timestamps = me._timestamps.labels; break; case 'auto': default: timestamps = generate(min, max, me.getLabelCapacity(min), options); } if (options.bounds === 'ticks' && timestamps.length) { min = timestamps[0]; max = timestamps[timestamps.length - 1]; } // Enforce limits with user min/max options min = parse(timeOpts.min, me) || min; max = parse(timeOpts.max, me) || max; // Remove ticks outside the min/max range for (i = 0, ilen = timestamps.length; i < ilen; ++i) { timestamp = timestamps[i]; if (timestamp >= min && timestamp <= max) { ticks.push(timestamp); } } me.min = min; me.max = max; // PRIVATE me._unit = timeOpts.unit || determineUnitForFormatting(ticks, timeOpts.minUnit, me.min, me.max); me._majorUnit = determineMajorUnit(me._unit); me._table = buildLookupTable(me._timestamps.data, min, max, options.distribution); me._offsets = computeOffsets(me._table, ticks, min, max, options); return ticksFromTimestamps(ticks, me._majorUnit); }, getLabelForIndex: function(index, datasetIndex) { var me = this; var data = me.chart.data; var timeOpts = me.options.time; var label = data.labels && index < data.labels.length ? data.labels[index] : ''; var value = data.datasets[datasetIndex].data[index]; if (helpers.isObject(value)) { label = me.getRightValue(value); } if (timeOpts.tooltipFormat) { label = momentify(label, timeOpts).format(timeOpts.tooltipFormat); } return label; }, /** * Function to format an individual tick mark * @private */ tickFormatFunction: function(tick, index, ticks, formatOverride) { var me = this; var options = me.options; var time = tick.valueOf(); var formats = options.time.displayFormats; var minorFormat = formats[me._unit]; var majorUnit = me._majorUnit; var majorFormat = formats[majorUnit]; var majorTime = tick.clone().startOf(majorUnit).valueOf(); var majorTickOpts = options.ticks.major; var major = majorTickOpts.enabled && majorUnit && majorFormat && time === majorTime; var label = tick.format(formatOverride ? formatOverride : major ? majorFormat : minorFormat); var tickOpts = major ? majorTickOpts : options.ticks.minor; var formatter = helpers.valueOrDefault(tickOpts.callback, tickOpts.userCallback); return formatter ? formatter(label, index, ticks) : label; }, convertTicksToLabels: function(ticks) { var labels = []; var i, ilen; for (i = 0, ilen = ticks.length; i < ilen; ++i) { labels.push(this.tickFormatFunction(moment(ticks[i].value), i, ticks)); } return labels; }, /** * @private */ getPixelForOffset: function(time) { var me = this; var size = me._horizontal ? me.width : me.height; var start = me._horizontal ? me.left : me.top; var pos = interpolate(me._table, 'time', time, 'pos'); return start + size * (me._offsets.left + pos) / (me._offsets.left + 1 + me._offsets.right); }, getPixelForValue: function(value, index, datasetIndex) { var me = this; var time = null; if (index !== undefined && datasetIndex !== undefined) { time = me._timestamps.datasets[datasetIndex][index]; } if (time === null) { time = parse(value, me); } if (time !== null) { return me.getPixelForOffset(time); } }, getPixelForTick: function(index) { var ticks = this.getTicks(); return index >= 0 && index < ticks.length ? this.getPixelForOffset(ticks[index].value) : null; }, getValueForPixel: function(pixel) { var me = this; var size = me._horizontal ? me.width : me.height; var start = me._horizontal ? me.left : me.top; var pos = (size ? (pixel - start) / size : 0) * (me._offsets.left + 1 + me._offsets.left) - me._offsets.right; var time = interpolate(me._table, 'pos', pos, 'time'); return moment(time); }, /** * Crude approximation of what the label width might be * @private */ getLabelWidth: function(label) { var me = this; var ticksOpts = me.options.ticks; var tickLabelWidth = me.ctx.measureText(label).width; var angle = helpers.toRadians(ticksOpts.maxRotation); var cosRotation = Math.cos(angle); var sinRotation = Math.sin(angle); var tickFontSize = helpers.valueOrDefault(ticksOpts.fontSize, defaults.global.defaultFontSize); return (tickLabelWidth * cosRotation) + (tickFontSize * sinRotation); }, /** * @private */ getLabelCapacity: function(exampleTime) { var me = this; var formatOverride = me.options.time.displayFormats.millisecond; // Pick the longest format for guestimation var exampleLabel = me.tickFormatFunction(moment(exampleTime), 0, [], formatOverride); var tickLabelWidth = me.getLabelWidth(exampleLabel); var innerWidth = me.isHorizontal() ? me.width : me.height; return Math.floor(innerWidth / tickLabelWidth); } }); Chart.scaleService.registerScaleType('time', TimeScale, defaultConfig); }; },{"25":25,"45":45,"6":6}]},{},[7])(7) }); ================================================ FILE: src/main/resources/static/admin/dist/js/plugins/chartjs2/Chart.js ================================================ /*! * Chart.js * http://chartjs.org/ * Version: 2.7.1 * * Copyright 2017 Nick Downie * Released under the MIT license * https://github.com/chartjs/Chart.js/blob/master/LICENSE.md */ (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Chart = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o lum2) { return (lum1 + 0.05) / (lum2 + 0.05); } return (lum2 + 0.05) / (lum1 + 0.05); }, level: function (color2) { var contrastRatio = this.contrast(color2); if (contrastRatio >= 7.1) { return 'AAA'; } return (contrastRatio >= 4.5) ? 'AA' : ''; }, dark: function () { // YIQ equation from http://24ways.org/2010/calculating-color-contrast var rgb = this.values.rgb; var yiq = (rgb[0] * 299 + rgb[1] * 587 + rgb[2] * 114) / 1000; return yiq < 128; }, light: function () { return !this.dark(); }, negate: function () { var rgb = []; for (var i = 0; i < 3; i++) { rgb[i] = 255 - this.values.rgb[i]; } this.setValues('rgb', rgb); return this; }, lighten: function (ratio) { var hsl = this.values.hsl; hsl[2] += hsl[2] * ratio; this.setValues('hsl', hsl); return this; }, darken: function (ratio) { var hsl = this.values.hsl; hsl[2] -= hsl[2] * ratio; this.setValues('hsl', hsl); return this; }, saturate: function (ratio) { var hsl = this.values.hsl; hsl[1] += hsl[1] * ratio; this.setValues('hsl', hsl); return this; }, desaturate: function (ratio) { var hsl = this.values.hsl; hsl[1] -= hsl[1] * ratio; this.setValues('hsl', hsl); return this; }, whiten: function (ratio) { var hwb = this.values.hwb; hwb[1] += hwb[1] * ratio; this.setValues('hwb', hwb); return this; }, blacken: function (ratio) { var hwb = this.values.hwb; hwb[2] += hwb[2] * ratio; this.setValues('hwb', hwb); return this; }, greyscale: function () { var rgb = this.values.rgb; // http://en.wikipedia.org/wiki/Grayscale#Converting_color_to_grayscale var val = rgb[0] * 0.3 + rgb[1] * 0.59 + rgb[2] * 0.11; this.setValues('rgb', [val, val, val]); return this; }, clearer: function (ratio) { var alpha = this.values.alpha; this.setValues('alpha', alpha - (alpha * ratio)); return this; }, opaquer: function (ratio) { var alpha = this.values.alpha; this.setValues('alpha', alpha + (alpha * ratio)); return this; }, rotate: function (degrees) { var hsl = this.values.hsl; var hue = (hsl[0] + degrees) % 360; hsl[0] = hue < 0 ? 360 + hue : hue; this.setValues('hsl', hsl); return this; }, /** * Ported from sass implementation in C * https://github.com/sass/libsass/blob/0e6b4a2850092356aa3ece07c6b249f0221caced/functions.cpp#L209 */ mix: function (mixinColor, weight) { var color1 = this; var color2 = mixinColor; var p = weight === undefined ? 0.5 : weight; var w = 2 * p - 1; var a = color1.alpha() - color2.alpha(); var w1 = (((w * a === -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0; var w2 = 1 - w1; return this .rgb( w1 * color1.red() + w2 * color2.red(), w1 * color1.green() + w2 * color2.green(), w1 * color1.blue() + w2 * color2.blue() ) .alpha(color1.alpha() * p + color2.alpha() * (1 - p)); }, toJSON: function () { return this.rgb(); }, clone: function () { // NOTE(SB): using node-clone creates a dependency to Buffer when using browserify, // making the final build way to big to embed in Chart.js. So let's do it manually, // assuming that values to clone are 1 dimension arrays containing only numbers, // except 'alpha' which is a number. var result = new Color(); var source = this.values; var target = result.values; var value, type; for (var prop in source) { if (source.hasOwnProperty(prop)) { value = source[prop]; type = ({}).toString.call(value); if (type === '[object Array]') { target[prop] = value.slice(0); } else if (type === '[object Number]') { target[prop] = value; } else { console.error('unexpected color value:', value); } } } return result; } }; Color.prototype.spaces = { rgb: ['red', 'green', 'blue'], hsl: ['hue', 'saturation', 'lightness'], hsv: ['hue', 'saturation', 'value'], hwb: ['hue', 'whiteness', 'blackness'], cmyk: ['cyan', 'magenta', 'yellow', 'black'] }; Color.prototype.maxes = { rgb: [255, 255, 255], hsl: [360, 100, 100], hsv: [360, 100, 100], hwb: [360, 100, 100], cmyk: [100, 100, 100, 100] }; Color.prototype.getValues = function (space) { var values = this.values; var vals = {}; for (var i = 0; i < space.length; i++) { vals[space.charAt(i)] = values[space][i]; } if (values.alpha !== 1) { vals.a = values.alpha; } // {r: 255, g: 255, b: 255, a: 0.4} return vals; }; Color.prototype.setValues = function (space, vals) { var values = this.values; var spaces = this.spaces; var maxes = this.maxes; var alpha = 1; var i; this.valid = true; if (space === 'alpha') { alpha = vals; } else if (vals.length) { // [10, 10, 10] values[space] = vals.slice(0, space.length); alpha = vals[space.length]; } else if (vals[space.charAt(0)] !== undefined) { // {r: 10, g: 10, b: 10} for (i = 0; i < space.length; i++) { values[space][i] = vals[space.charAt(i)]; } alpha = vals.a; } else if (vals[spaces[space][0]] !== undefined) { // {red: 10, green: 10, blue: 10} var chans = spaces[space]; for (i = 0; i < space.length; i++) { values[space][i] = vals[chans[i]]; } alpha = vals.alpha; } values.alpha = Math.max(0, Math.min(1, (alpha === undefined ? values.alpha : alpha))); if (space === 'alpha') { return false; } var capped; // cap values of the space prior converting all values for (i = 0; i < space.length; i++) { capped = Math.max(0, Math.min(maxes[space][i], values[space][i])); values[space][i] = Math.round(capped); } // convert to all the other color spaces for (var sname in spaces) { if (sname !== space) { values[sname] = convert[space][sname](values[space]); } } return true; }; Color.prototype.setSpace = function (space, args) { var vals = args[0]; if (vals === undefined) { // color.rgb() return this.getValues(space); } // color.rgb(10, 10, 10) if (typeof vals === 'number') { vals = Array.prototype.slice.call(args); } this.setValues(space, vals); return this; }; Color.prototype.setChannel = function (space, index, val) { var svalues = this.values[space]; if (val === undefined) { // color.red() return svalues[index]; } else if (val === svalues[index]) { // color.red(color.red()) return this; } // color.red(100) svalues[index] = val; this.setValues(space, svalues); return this; }; if (typeof window !== 'undefined') { window.Color = Color; } module.exports = Color; },{"2":2,"5":5}],4:[function(require,module,exports){ /* MIT license */ module.exports = { rgb2hsl: rgb2hsl, rgb2hsv: rgb2hsv, rgb2hwb: rgb2hwb, rgb2cmyk: rgb2cmyk, rgb2keyword: rgb2keyword, rgb2xyz: rgb2xyz, rgb2lab: rgb2lab, rgb2lch: rgb2lch, hsl2rgb: hsl2rgb, hsl2hsv: hsl2hsv, hsl2hwb: hsl2hwb, hsl2cmyk: hsl2cmyk, hsl2keyword: hsl2keyword, hsv2rgb: hsv2rgb, hsv2hsl: hsv2hsl, hsv2hwb: hsv2hwb, hsv2cmyk: hsv2cmyk, hsv2keyword: hsv2keyword, hwb2rgb: hwb2rgb, hwb2hsl: hwb2hsl, hwb2hsv: hwb2hsv, hwb2cmyk: hwb2cmyk, hwb2keyword: hwb2keyword, cmyk2rgb: cmyk2rgb, cmyk2hsl: cmyk2hsl, cmyk2hsv: cmyk2hsv, cmyk2hwb: cmyk2hwb, cmyk2keyword: cmyk2keyword, keyword2rgb: keyword2rgb, keyword2hsl: keyword2hsl, keyword2hsv: keyword2hsv, keyword2hwb: keyword2hwb, keyword2cmyk: keyword2cmyk, keyword2lab: keyword2lab, keyword2xyz: keyword2xyz, xyz2rgb: xyz2rgb, xyz2lab: xyz2lab, xyz2lch: xyz2lch, lab2xyz: lab2xyz, lab2rgb: lab2rgb, lab2lch: lab2lch, lch2lab: lch2lab, lch2xyz: lch2xyz, lch2rgb: lch2rgb } function rgb2hsl(rgb) { var r = rgb[0]/255, g = rgb[1]/255, b = rgb[2]/255, min = Math.min(r, g, b), max = Math.max(r, g, b), delta = max - min, h, s, l; if (max == min) h = 0; else if (r == max) h = (g - b) / delta; else if (g == max) h = 2 + (b - r) / delta; else if (b == max) h = 4 + (r - g)/ delta; h = Math.min(h * 60, 360); if (h < 0) h += 360; l = (min + max) / 2; if (max == min) s = 0; else if (l <= 0.5) s = delta / (max + min); else s = delta / (2 - max - min); return [h, s * 100, l * 100]; } function rgb2hsv(rgb) { var r = rgb[0], g = rgb[1], b = rgb[2], min = Math.min(r, g, b), max = Math.max(r, g, b), delta = max - min, h, s, v; if (max == 0) s = 0; else s = (delta/max * 1000)/10; if (max == min) h = 0; else if (r == max) h = (g - b) / delta; else if (g == max) h = 2 + (b - r) / delta; else if (b == max) h = 4 + (r - g) / delta; h = Math.min(h * 60, 360); if (h < 0) h += 360; v = ((max / 255) * 1000) / 10; return [h, s, v]; } function rgb2hwb(rgb) { var r = rgb[0], g = rgb[1], b = rgb[2], h = rgb2hsl(rgb)[0], w = 1/255 * Math.min(r, Math.min(g, b)), b = 1 - 1/255 * Math.max(r, Math.max(g, b)); return [h, w * 100, b * 100]; } function rgb2cmyk(rgb) { var r = rgb[0] / 255, g = rgb[1] / 255, b = rgb[2] / 255, c, m, y, k; k = Math.min(1 - r, 1 - g, 1 - b); c = (1 - r - k) / (1 - k) || 0; m = (1 - g - k) / (1 - k) || 0; y = (1 - b - k) / (1 - k) || 0; return [c * 100, m * 100, y * 100, k * 100]; } function rgb2keyword(rgb) { return reverseKeywords[JSON.stringify(rgb)]; } function rgb2xyz(rgb) { var r = rgb[0] / 255, g = rgb[1] / 255, b = rgb[2] / 255; // assume sRGB r = r > 0.04045 ? Math.pow(((r + 0.055) / 1.055), 2.4) : (r / 12.92); g = g > 0.04045 ? Math.pow(((g + 0.055) / 1.055), 2.4) : (g / 12.92); b = b > 0.04045 ? Math.pow(((b + 0.055) / 1.055), 2.4) : (b / 12.92); var x = (r * 0.4124) + (g * 0.3576) + (b * 0.1805); var y = (r * 0.2126) + (g * 0.7152) + (b * 0.0722); var z = (r * 0.0193) + (g * 0.1192) + (b * 0.9505); return [x * 100, y *100, z * 100]; } function rgb2lab(rgb) { var xyz = rgb2xyz(rgb), x = xyz[0], y = xyz[1], z = xyz[2], l, a, b; x /= 95.047; y /= 100; z /= 108.883; x = x > 0.008856 ? Math.pow(x, 1/3) : (7.787 * x) + (16 / 116); y = y > 0.008856 ? Math.pow(y, 1/3) : (7.787 * y) + (16 / 116); z = z > 0.008856 ? Math.pow(z, 1/3) : (7.787 * z) + (16 / 116); l = (116 * y) - 16; a = 500 * (x - y); b = 200 * (y - z); return [l, a, b]; } function rgb2lch(args) { return lab2lch(rgb2lab(args)); } function hsl2rgb(hsl) { var h = hsl[0] / 360, s = hsl[1] / 100, l = hsl[2] / 100, t1, t2, t3, rgb, val; if (s == 0) { val = l * 255; return [val, val, val]; } if (l < 0.5) t2 = l * (1 + s); else t2 = l + s - l * s; t1 = 2 * l - t2; rgb = [0, 0, 0]; for (var i = 0; i < 3; i++) { t3 = h + 1 / 3 * - (i - 1); t3 < 0 && t3++; t3 > 1 && t3--; if (6 * t3 < 1) val = t1 + (t2 - t1) * 6 * t3; else if (2 * t3 < 1) val = t2; else if (3 * t3 < 2) val = t1 + (t2 - t1) * (2 / 3 - t3) * 6; else val = t1; rgb[i] = val * 255; } return rgb; } function hsl2hsv(hsl) { var h = hsl[0], s = hsl[1] / 100, l = hsl[2] / 100, sv, v; if(l === 0) { // no need to do calc on black // also avoids divide by 0 error return [0, 0, 0]; } l *= 2; s *= (l <= 1) ? l : 2 - l; v = (l + s) / 2; sv = (2 * s) / (l + s); return [h, sv * 100, v * 100]; } function hsl2hwb(args) { return rgb2hwb(hsl2rgb(args)); } function hsl2cmyk(args) { return rgb2cmyk(hsl2rgb(args)); } function hsl2keyword(args) { return rgb2keyword(hsl2rgb(args)); } function hsv2rgb(hsv) { var h = hsv[0] / 60, s = hsv[1] / 100, v = hsv[2] / 100, hi = Math.floor(h) % 6; var f = h - Math.floor(h), p = 255 * v * (1 - s), q = 255 * v * (1 - (s * f)), t = 255 * v * (1 - (s * (1 - f))), v = 255 * v; switch(hi) { case 0: return [v, t, p]; case 1: return [q, v, p]; case 2: return [p, v, t]; case 3: return [p, q, v]; case 4: return [t, p, v]; case 5: return [v, p, q]; } } function hsv2hsl(hsv) { var h = hsv[0], s = hsv[1] / 100, v = hsv[2] / 100, sl, l; l = (2 - s) * v; sl = s * v; sl /= (l <= 1) ? l : 2 - l; sl = sl || 0; l /= 2; return [h, sl * 100, l * 100]; } function hsv2hwb(args) { return rgb2hwb(hsv2rgb(args)) } function hsv2cmyk(args) { return rgb2cmyk(hsv2rgb(args)); } function hsv2keyword(args) { return rgb2keyword(hsv2rgb(args)); } // http://dev.w3.org/csswg/css-color/#hwb-to-rgb function hwb2rgb(hwb) { var h = hwb[0] / 360, wh = hwb[1] / 100, bl = hwb[2] / 100, ratio = wh + bl, i, v, f, n; // wh + bl cant be > 1 if (ratio > 1) { wh /= ratio; bl /= ratio; } i = Math.floor(6 * h); v = 1 - bl; f = 6 * h - i; if ((i & 0x01) != 0) { f = 1 - f; } n = wh + f * (v - wh); // linear interpolation switch (i) { default: case 6: case 0: r = v; g = n; b = wh; break; case 1: r = n; g = v; b = wh; break; case 2: r = wh; g = v; b = n; break; case 3: r = wh; g = n; b = v; break; case 4: r = n; g = wh; b = v; break; case 5: r = v; g = wh; b = n; break; } return [r * 255, g * 255, b * 255]; } function hwb2hsl(args) { return rgb2hsl(hwb2rgb(args)); } function hwb2hsv(args) { return rgb2hsv(hwb2rgb(args)); } function hwb2cmyk(args) { return rgb2cmyk(hwb2rgb(args)); } function hwb2keyword(args) { return rgb2keyword(hwb2rgb(args)); } function cmyk2rgb(cmyk) { var c = cmyk[0] / 100, m = cmyk[1] / 100, y = cmyk[2] / 100, k = cmyk[3] / 100, r, g, b; r = 1 - Math.min(1, c * (1 - k) + k); g = 1 - Math.min(1, m * (1 - k) + k); b = 1 - Math.min(1, y * (1 - k) + k); return [r * 255, g * 255, b * 255]; } function cmyk2hsl(args) { return rgb2hsl(cmyk2rgb(args)); } function cmyk2hsv(args) { return rgb2hsv(cmyk2rgb(args)); } function cmyk2hwb(args) { return rgb2hwb(cmyk2rgb(args)); } function cmyk2keyword(args) { return rgb2keyword(cmyk2rgb(args)); } function xyz2rgb(xyz) { var x = xyz[0] / 100, y = xyz[1] / 100, z = xyz[2] / 100, r, g, b; r = (x * 3.2406) + (y * -1.5372) + (z * -0.4986); g = (x * -0.9689) + (y * 1.8758) + (z * 0.0415); b = (x * 0.0557) + (y * -0.2040) + (z * 1.0570); // assume sRGB r = r > 0.0031308 ? ((1.055 * Math.pow(r, 1.0 / 2.4)) - 0.055) : r = (r * 12.92); g = g > 0.0031308 ? ((1.055 * Math.pow(g, 1.0 / 2.4)) - 0.055) : g = (g * 12.92); b = b > 0.0031308 ? ((1.055 * Math.pow(b, 1.0 / 2.4)) - 0.055) : b = (b * 12.92); r = Math.min(Math.max(0, r), 1); g = Math.min(Math.max(0, g), 1); b = Math.min(Math.max(0, b), 1); return [r * 255, g * 255, b * 255]; } function xyz2lab(xyz) { var x = xyz[0], y = xyz[1], z = xyz[2], l, a, b; x /= 95.047; y /= 100; z /= 108.883; x = x > 0.008856 ? Math.pow(x, 1/3) : (7.787 * x) + (16 / 116); y = y > 0.008856 ? Math.pow(y, 1/3) : (7.787 * y) + (16 / 116); z = z > 0.008856 ? Math.pow(z, 1/3) : (7.787 * z) + (16 / 116); l = (116 * y) - 16; a = 500 * (x - y); b = 200 * (y - z); return [l, a, b]; } function xyz2lch(args) { return lab2lch(xyz2lab(args)); } function lab2xyz(lab) { var l = lab[0], a = lab[1], b = lab[2], x, y, z, y2; if (l <= 8) { y = (l * 100) / 903.3; y2 = (7.787 * (y / 100)) + (16 / 116); } else { y = 100 * Math.pow((l + 16) / 116, 3); y2 = Math.pow(y / 100, 1/3); } x = x / 95.047 <= 0.008856 ? x = (95.047 * ((a / 500) + y2 - (16 / 116))) / 7.787 : 95.047 * Math.pow((a / 500) + y2, 3); z = z / 108.883 <= 0.008859 ? z = (108.883 * (y2 - (b / 200) - (16 / 116))) / 7.787 : 108.883 * Math.pow(y2 - (b / 200), 3); return [x, y, z]; } function lab2lch(lab) { var l = lab[0], a = lab[1], b = lab[2], hr, h, c; hr = Math.atan2(b, a); h = hr * 360 / 2 / Math.PI; if (h < 0) { h += 360; } c = Math.sqrt(a * a + b * b); return [l, c, h]; } function lab2rgb(args) { return xyz2rgb(lab2xyz(args)); } function lch2lab(lch) { var l = lch[0], c = lch[1], h = lch[2], a, b, hr; hr = h / 360 * 2 * Math.PI; a = c * Math.cos(hr); b = c * Math.sin(hr); return [l, a, b]; } function lch2xyz(args) { return lab2xyz(lch2lab(args)); } function lch2rgb(args) { return lab2rgb(lch2lab(args)); } function keyword2rgb(keyword) { return cssKeywords[keyword]; } function keyword2hsl(args) { return rgb2hsl(keyword2rgb(args)); } function keyword2hsv(args) { return rgb2hsv(keyword2rgb(args)); } function keyword2hwb(args) { return rgb2hwb(keyword2rgb(args)); } function keyword2cmyk(args) { return rgb2cmyk(keyword2rgb(args)); } function keyword2lab(args) { return rgb2lab(keyword2rgb(args)); } function keyword2xyz(args) { return rgb2xyz(keyword2rgb(args)); } var cssKeywords = { aliceblue: [240,248,255], antiquewhite: [250,235,215], aqua: [0,255,255], aquamarine: [127,255,212], azure: [240,255,255], beige: [245,245,220], bisque: [255,228,196], black: [0,0,0], blanchedalmond: [255,235,205], blue: [0,0,255], blueviolet: [138,43,226], brown: [165,42,42], burlywood: [222,184,135], cadetblue: [95,158,160], chartreuse: [127,255,0], chocolate: [210,105,30], coral: [255,127,80], cornflowerblue: [100,149,237], cornsilk: [255,248,220], crimson: [220,20,60], cyan: [0,255,255], darkblue: [0,0,139], darkcyan: [0,139,139], darkgoldenrod: [184,134,11], darkgray: [169,169,169], darkgreen: [0,100,0], darkgrey: [169,169,169], darkkhaki: [189,183,107], darkmagenta: [139,0,139], darkolivegreen: [85,107,47], darkorange: [255,140,0], darkorchid: [153,50,204], darkred: [139,0,0], darksalmon: [233,150,122], darkseagreen: [143,188,143], darkslateblue: [72,61,139], darkslategray: [47,79,79], darkslategrey: [47,79,79], darkturquoise: [0,206,209], darkviolet: [148,0,211], deeppink: [255,20,147], deepskyblue: [0,191,255], dimgray: [105,105,105], dimgrey: [105,105,105], dodgerblue: [30,144,255], firebrick: [178,34,34], floralwhite: [255,250,240], forestgreen: [34,139,34], fuchsia: [255,0,255], gainsboro: [220,220,220], ghostwhite: [248,248,255], gold: [255,215,0], goldenrod: [218,165,32], gray: [128,128,128], green: [0,128,0], greenyellow: [173,255,47], grey: [128,128,128], honeydew: [240,255,240], hotpink: [255,105,180], indianred: [205,92,92], indigo: [75,0,130], ivory: [255,255,240], khaki: [240,230,140], lavender: [230,230,250], lavenderblush: [255,240,245], lawngreen: [124,252,0], lemonchiffon: [255,250,205], lightblue: [173,216,230], lightcoral: [240,128,128], lightcyan: [224,255,255], lightgoldenrodyellow: [250,250,210], lightgray: [211,211,211], lightgreen: [144,238,144], lightgrey: [211,211,211], lightpink: [255,182,193], lightsalmon: [255,160,122], lightseagreen: [32,178,170], lightskyblue: [135,206,250], lightslategray: [119,136,153], lightslategrey: [119,136,153], lightsteelblue: [176,196,222], lightyellow: [255,255,224], lime: [0,255,0], limegreen: [50,205,50], linen: [250,240,230], magenta: [255,0,255], maroon: [128,0,0], mediumaquamarine: [102,205,170], mediumblue: [0,0,205], mediumorchid: [186,85,211], mediumpurple: [147,112,219], mediumseagreen: [60,179,113], mediumslateblue: [123,104,238], mediumspringgreen: [0,250,154], mediumturquoise: [72,209,204], mediumvioletred: [199,21,133], midnightblue: [25,25,112], mintcream: [245,255,250], mistyrose: [255,228,225], moccasin: [255,228,181], navajowhite: [255,222,173], navy: [0,0,128], oldlace: [253,245,230], olive: [128,128,0], olivedrab: [107,142,35], orange: [255,165,0], orangered: [255,69,0], orchid: [218,112,214], palegoldenrod: [238,232,170], palegreen: [152,251,152], paleturquoise: [175,238,238], palevioletred: [219,112,147], papayawhip: [255,239,213], peachpuff: [255,218,185], peru: [205,133,63], pink: [255,192,203], plum: [221,160,221], powderblue: [176,224,230], purple: [128,0,128], rebeccapurple: [102, 51, 153], red: [255,0,0], rosybrown: [188,143,143], royalblue: [65,105,225], saddlebrown: [139,69,19], salmon: [250,128,114], sandybrown: [244,164,96], seagreen: [46,139,87], seashell: [255,245,238], sienna: [160,82,45], silver: [192,192,192], skyblue: [135,206,235], slateblue: [106,90,205], slategray: [112,128,144], slategrey: [112,128,144], snow: [255,250,250], springgreen: [0,255,127], steelblue: [70,130,180], tan: [210,180,140], teal: [0,128,128], thistle: [216,191,216], tomato: [255,99,71], turquoise: [64,224,208], violet: [238,130,238], wheat: [245,222,179], white: [255,255,255], whitesmoke: [245,245,245], yellow: [255,255,0], yellowgreen: [154,205,50] }; var reverseKeywords = {}; for (var key in cssKeywords) { reverseKeywords[JSON.stringify(cssKeywords[key])] = key; } },{}],5:[function(require,module,exports){ var conversions = require(4); var convert = function() { return new Converter(); } for (var func in conversions) { // export Raw versions convert[func + "Raw"] = (function(func) { // accept array or plain args return function(arg) { if (typeof arg == "number") arg = Array.prototype.slice.call(arguments); return conversions[func](arg); } })(func); var pair = /(\w+)2(\w+)/.exec(func), from = pair[1], to = pair[2]; // export rgb2hsl and ["rgb"]["hsl"] convert[from] = convert[from] || {}; convert[from][to] = convert[func] = (function(func) { return function(arg) { if (typeof arg == "number") arg = Array.prototype.slice.call(arguments); var val = conversions[func](arg); if (typeof val == "string" || val === undefined) return val; // keyword for (var i = 0; i < val.length; i++) val[i] = Math.round(val[i]); return val; } })(func); } /* Converter does lazy conversion and caching */ var Converter = function() { this.convs = {}; }; /* Either get the values for a space or set the values for a space, depending on args */ Converter.prototype.routeSpace = function(space, args) { var values = args[0]; if (values === undefined) { // color.rgb() return this.getValues(space); } // color.rgb(10, 10, 10) if (typeof values == "number") { values = Array.prototype.slice.call(args); } return this.setValues(space, values); }; /* Set the values for a space, invalidating cache */ Converter.prototype.setValues = function(space, values) { this.space = space; this.convs = {}; this.convs[space] = values; return this; }; /* Get the values for a space. If there's already a conversion for the space, fetch it, otherwise compute it */ Converter.prototype.getValues = function(space) { var vals = this.convs[space]; if (!vals) { var fspace = this.space, from = this.convs[fspace]; vals = convert[fspace][space](from); this.convs[space] = vals; } return vals; }; ["rgb", "hsl", "hsv", "cmyk", "keyword"].forEach(function(space) { Converter.prototype[space] = function(vals) { return this.routeSpace(space, arguments); } }); module.exports = convert; },{"4":4}],6:[function(require,module,exports){ 'use strict' module.exports = { "aliceblue": [240, 248, 255], "antiquewhite": [250, 235, 215], "aqua": [0, 255, 255], "aquamarine": [127, 255, 212], "azure": [240, 255, 255], "beige": [245, 245, 220], "bisque": [255, 228, 196], "black": [0, 0, 0], "blanchedalmond": [255, 235, 205], "blue": [0, 0, 255], "blueviolet": [138, 43, 226], "brown": [165, 42, 42], "burlywood": [222, 184, 135], "cadetblue": [95, 158, 160], "chartreuse": [127, 255, 0], "chocolate": [210, 105, 30], "coral": [255, 127, 80], "cornflowerblue": [100, 149, 237], "cornsilk": [255, 248, 220], "crimson": [220, 20, 60], "cyan": [0, 255, 255], "darkblue": [0, 0, 139], "darkcyan": [0, 139, 139], "darkgoldenrod": [184, 134, 11], "darkgray": [169, 169, 169], "darkgreen": [0, 100, 0], "darkgrey": [169, 169, 169], "darkkhaki": [189, 183, 107], "darkmagenta": [139, 0, 139], "darkolivegreen": [85, 107, 47], "darkorange": [255, 140, 0], "darkorchid": [153, 50, 204], "darkred": [139, 0, 0], "darksalmon": [233, 150, 122], "darkseagreen": [143, 188, 143], "darkslateblue": [72, 61, 139], "darkslategray": [47, 79, 79], "darkslategrey": [47, 79, 79], "darkturquoise": [0, 206, 209], "darkviolet": [148, 0, 211], "deeppink": [255, 20, 147], "deepskyblue": [0, 191, 255], "dimgray": [105, 105, 105], "dimgrey": [105, 105, 105], "dodgerblue": [30, 144, 255], "firebrick": [178, 34, 34], "floralwhite": [255, 250, 240], "forestgreen": [34, 139, 34], "fuchsia": [255, 0, 255], "gainsboro": [220, 220, 220], "ghostwhite": [248, 248, 255], "gold": [255, 215, 0], "goldenrod": [218, 165, 32], "gray": [128, 128, 128], "green": [0, 128, 0], "greenyellow": [173, 255, 47], "grey": [128, 128, 128], "honeydew": [240, 255, 240], "hotpink": [255, 105, 180], "indianred": [205, 92, 92], "indigo": [75, 0, 130], "ivory": [255, 255, 240], "khaki": [240, 230, 140], "lavender": [230, 230, 250], "lavenderblush": [255, 240, 245], "lawngreen": [124, 252, 0], "lemonchiffon": [255, 250, 205], "lightblue": [173, 216, 230], "lightcoral": [240, 128, 128], "lightcyan": [224, 255, 255], "lightgoldenrodyellow": [250, 250, 210], "lightgray": [211, 211, 211], "lightgreen": [144, 238, 144], "lightgrey": [211, 211, 211], "lightpink": [255, 182, 193], "lightsalmon": [255, 160, 122], "lightseagreen": [32, 178, 170], "lightskyblue": [135, 206, 250], "lightslategray": [119, 136, 153], "lightslategrey": [119, 136, 153], "lightsteelblue": [176, 196, 222], "lightyellow": [255, 255, 224], "lime": [0, 255, 0], "limegreen": [50, 205, 50], "linen": [250, 240, 230], "magenta": [255, 0, 255], "maroon": [128, 0, 0], "mediumaquamarine": [102, 205, 170], "mediumblue": [0, 0, 205], "mediumorchid": [186, 85, 211], "mediumpurple": [147, 112, 219], "mediumseagreen": [60, 179, 113], "mediumslateblue": [123, 104, 238], "mediumspringgreen": [0, 250, 154], "mediumturquoise": [72, 209, 204], "mediumvioletred": [199, 21, 133], "midnightblue": [25, 25, 112], "mintcream": [245, 255, 250], "mistyrose": [255, 228, 225], "moccasin": [255, 228, 181], "navajowhite": [255, 222, 173], "navy": [0, 0, 128], "oldlace": [253, 245, 230], "olive": [128, 128, 0], "olivedrab": [107, 142, 35], "orange": [255, 165, 0], "orangered": [255, 69, 0], "orchid": [218, 112, 214], "palegoldenrod": [238, 232, 170], "palegreen": [152, 251, 152], "paleturquoise": [175, 238, 238], "palevioletred": [219, 112, 147], "papayawhip": [255, 239, 213], "peachpuff": [255, 218, 185], "peru": [205, 133, 63], "pink": [255, 192, 203], "plum": [221, 160, 221], "powderblue": [176, 224, 230], "purple": [128, 0, 128], "rebeccapurple": [102, 51, 153], "red": [255, 0, 0], "rosybrown": [188, 143, 143], "royalblue": [65, 105, 225], "saddlebrown": [139, 69, 19], "salmon": [250, 128, 114], "sandybrown": [244, 164, 96], "seagreen": [46, 139, 87], "seashell": [255, 245, 238], "sienna": [160, 82, 45], "silver": [192, 192, 192], "skyblue": [135, 206, 235], "slateblue": [106, 90, 205], "slategray": [112, 128, 144], "slategrey": [112, 128, 144], "snow": [255, 250, 250], "springgreen": [0, 255, 127], "steelblue": [70, 130, 180], "tan": [210, 180, 140], "teal": [0, 128, 128], "thistle": [216, 191, 216], "tomato": [255, 99, 71], "turquoise": [64, 224, 208], "violet": [238, 130, 238], "wheat": [245, 222, 179], "white": [255, 255, 255], "whitesmoke": [245, 245, 245], "yellow": [255, 255, 0], "yellowgreen": [154, 205, 50] }; },{}],7:[function(require,module,exports){ /** * @namespace Chart */ var Chart = require(29)(); Chart.helpers = require(45); // @todo dispatch these helpers into appropriated helpers/helpers.* file and write unit tests! require(27)(Chart); Chart.defaults = require(25); Chart.Element = require(26); Chart.elements = require(40); Chart.Interaction = require(28); Chart.platform = require(48); require(31)(Chart); require(22)(Chart); require(23)(Chart); require(24)(Chart); require(30)(Chart); require(33)(Chart); require(32)(Chart); require(35)(Chart); require(54)(Chart); require(52)(Chart); require(53)(Chart); require(55)(Chart); require(56)(Chart); require(57)(Chart); // Controllers must be loaded after elements // See Chart.core.datasetController.dataElementType require(15)(Chart); require(16)(Chart); require(17)(Chart); require(18)(Chart); require(19)(Chart); require(20)(Chart); require(21)(Chart); require(8)(Chart); require(9)(Chart); require(10)(Chart); require(11)(Chart); require(12)(Chart); require(13)(Chart); require(14)(Chart); // Loading built-it plugins var plugins = []; plugins.push( require(49)(Chart), require(50)(Chart), require(51)(Chart) ); Chart.plugins.register(plugins); Chart.platform.initialize(); module.exports = Chart; if (typeof window !== 'undefined') { window.Chart = Chart; } // DEPRECATIONS /** * Provided for backward compatibility, use Chart.helpers.canvas instead. * @namespace Chart.canvasHelpers * @deprecated since version 2.6.0 * @todo remove at version 3 * @private */ Chart.canvasHelpers = Chart.helpers.canvas; },{"10":10,"11":11,"12":12,"13":13,"14":14,"15":15,"16":16,"17":17,"18":18,"19":19,"20":20,"21":21,"22":22,"23":23,"24":24,"25":25,"26":26,"27":27,"28":28,"29":29,"30":30,"31":31,"32":32,"33":33,"35":35,"40":40,"45":45,"48":48,"49":49,"50":50,"51":51,"52":52,"53":53,"54":54,"55":55,"56":56,"57":57,"8":8,"9":9}],8:[function(require,module,exports){ 'use strict'; module.exports = function(Chart) { Chart.Bar = function(context, config) { config.type = 'bar'; return new Chart(context, config); }; }; },{}],9:[function(require,module,exports){ 'use strict'; module.exports = function(Chart) { Chart.Bubble = function(context, config) { config.type = 'bubble'; return new Chart(context, config); }; }; },{}],10:[function(require,module,exports){ 'use strict'; module.exports = function(Chart) { Chart.Doughnut = function(context, config) { config.type = 'doughnut'; return new Chart(context, config); }; }; },{}],11:[function(require,module,exports){ 'use strict'; module.exports = function(Chart) { Chart.Line = function(context, config) { config.type = 'line'; return new Chart(context, config); }; }; },{}],12:[function(require,module,exports){ 'use strict'; module.exports = function(Chart) { Chart.PolarArea = function(context, config) { config.type = 'polarArea'; return new Chart(context, config); }; }; },{}],13:[function(require,module,exports){ 'use strict'; module.exports = function(Chart) { Chart.Radar = function(context, config) { config.type = 'radar'; return new Chart(context, config); }; }; },{}],14:[function(require,module,exports){ 'use strict'; module.exports = function(Chart) { Chart.Scatter = function(context, config) { config.type = 'scatter'; return new Chart(context, config); }; }; },{}],15:[function(require,module,exports){ 'use strict'; var defaults = require(25); var elements = require(40); var helpers = require(45); defaults._set('bar', { hover: { mode: 'label' }, scales: { xAxes: [{ type: 'category', // Specific to Bar Controller categoryPercentage: 0.8, barPercentage: 0.9, // offset settings offset: true, // grid line settings gridLines: { offsetGridLines: true } }], yAxes: [{ type: 'linear' }] } }); defaults._set('horizontalBar', { hover: { mode: 'index', axis: 'y' }, scales: { xAxes: [{ type: 'linear', position: 'bottom' }], yAxes: [{ position: 'left', type: 'category', // Specific to Horizontal Bar Controller categoryPercentage: 0.8, barPercentage: 0.9, // offset settings offset: true, // grid line settings gridLines: { offsetGridLines: true } }] }, elements: { rectangle: { borderSkipped: 'left' } }, tooltips: { callbacks: { title: function(item, data) { // Pick first xLabel for now var title = ''; if (item.length > 0) { if (item[0].yLabel) { title = item[0].yLabel; } else if (data.labels.length > 0 && item[0].index < data.labels.length) { title = data.labels[item[0].index]; } } return title; }, label: function(item, data) { var datasetLabel = data.datasets[item.datasetIndex].label || ''; return datasetLabel + ': ' + item.xLabel; } }, mode: 'index', axis: 'y' } }); module.exports = function(Chart) { Chart.controllers.bar = Chart.DatasetController.extend({ dataElementType: elements.Rectangle, initialize: function() { var me = this; var meta; Chart.DatasetController.prototype.initialize.apply(me, arguments); meta = me.getMeta(); meta.stack = me.getDataset().stack; meta.bar = true; }, update: function(reset) { var me = this; var rects = me.getMeta().data; var i, ilen; me._ruler = me.getRuler(); for (i = 0, ilen = rects.length; i < ilen; ++i) { me.updateElement(rects[i], i, reset); } }, updateElement: function(rectangle, index, reset) { var me = this; var chart = me.chart; var meta = me.getMeta(); var dataset = me.getDataset(); var custom = rectangle.custom || {}; var rectangleOptions = chart.options.elements.rectangle; rectangle._xScale = me.getScaleForId(meta.xAxisID); rectangle._yScale = me.getScaleForId(meta.yAxisID); rectangle._datasetIndex = me.index; rectangle._index = index; rectangle._model = { datasetLabel: dataset.label, label: chart.data.labels[index], borderSkipped: custom.borderSkipped ? custom.borderSkipped : rectangleOptions.borderSkipped, backgroundColor: custom.backgroundColor ? custom.backgroundColor : helpers.valueAtIndexOrDefault(dataset.backgroundColor, index, rectangleOptions.backgroundColor), borderColor: custom.borderColor ? custom.borderColor : helpers.valueAtIndexOrDefault(dataset.borderColor, index, rectangleOptions.borderColor), borderWidth: custom.borderWidth ? custom.borderWidth : helpers.valueAtIndexOrDefault(dataset.borderWidth, index, rectangleOptions.borderWidth) }; me.updateElementGeometry(rectangle, index, reset); rectangle.pivot(); }, /** * @private */ updateElementGeometry: function(rectangle, index, reset) { var me = this; var model = rectangle._model; var vscale = me.getValueScale(); var base = vscale.getBasePixel(); var horizontal = vscale.isHorizontal(); var ruler = me._ruler || me.getRuler(); var vpixels = me.calculateBarValuePixels(me.index, index); var ipixels = me.calculateBarIndexPixels(me.index, index, ruler); model.horizontal = horizontal; model.base = reset ? base : vpixels.base; model.x = horizontal ? reset ? base : vpixels.head : ipixels.center; model.y = horizontal ? ipixels.center : reset ? base : vpixels.head; model.height = horizontal ? ipixels.size : undefined; model.width = horizontal ? undefined : ipixels.size; }, /** * @private */ getValueScaleId: function() { return this.getMeta().yAxisID; }, /** * @private */ getIndexScaleId: function() { return this.getMeta().xAxisID; }, /** * @private */ getValueScale: function() { return this.getScaleForId(this.getValueScaleId()); }, /** * @private */ getIndexScale: function() { return this.getScaleForId(this.getIndexScaleId()); }, /** * Returns the effective number of stacks based on groups and bar visibility. * @private */ getStackCount: function(last) { var me = this; var chart = me.chart; var scale = me.getIndexScale(); var stacked = scale.options.stacked; var ilen = last === undefined ? chart.data.datasets.length : last + 1; var stacks = []; var i, meta; for (i = 0; i < ilen; ++i) { meta = chart.getDatasetMeta(i); if (meta.bar && chart.isDatasetVisible(i) && (stacked === false || (stacked === true && stacks.indexOf(meta.stack) === -1) || (stacked === undefined && (meta.stack === undefined || stacks.indexOf(meta.stack) === -1)))) { stacks.push(meta.stack); } } return stacks.length; }, /** * Returns the stack index for the given dataset based on groups and bar visibility. * @private */ getStackIndex: function(datasetIndex) { return this.getStackCount(datasetIndex) - 1; }, /** * @private */ getRuler: function() { var me = this; var scale = me.getIndexScale(); var stackCount = me.getStackCount(); var datasetIndex = me.index; var pixels = []; var isHorizontal = scale.isHorizontal(); var start = isHorizontal ? scale.left : scale.top; var end = start + (isHorizontal ? scale.width : scale.height); var i, ilen; for (i = 0, ilen = me.getMeta().data.length; i < ilen; ++i) { pixels.push(scale.getPixelForValue(null, i, datasetIndex)); } return { pixels: pixels, start: start, end: end, stackCount: stackCount, scale: scale }; }, /** * Note: pixel values are not clamped to the scale area. * @private */ calculateBarValuePixels: function(datasetIndex, index) { var me = this; var chart = me.chart; var meta = me.getMeta(); var scale = me.getValueScale(); var datasets = chart.data.datasets; var value = scale.getRightValue(datasets[datasetIndex].data[index]); var stacked = scale.options.stacked; var stack = meta.stack; var start = 0; var i, imeta, ivalue, base, head, size; if (stacked || (stacked === undefined && stack !== undefined)) { for (i = 0; i < datasetIndex; ++i) { imeta = chart.getDatasetMeta(i); if (imeta.bar && imeta.stack === stack && imeta.controller.getValueScaleId() === scale.id && chart.isDatasetVisible(i)) { ivalue = scale.getRightValue(datasets[i].data[index]); if ((value < 0 && ivalue < 0) || (value >= 0 && ivalue > 0)) { start += ivalue; } } } } base = scale.getPixelForValue(start); head = scale.getPixelForValue(start + value); size = (head - base) / 2; return { size: size, base: base, head: head, center: head + size / 2 }; }, /** * @private */ calculateBarIndexPixels: function(datasetIndex, index, ruler) { var me = this; var options = ruler.scale.options; var stackIndex = me.getStackIndex(datasetIndex); var pixels = ruler.pixels; var base = pixels[index]; var length = pixels.length; var start = ruler.start; var end = ruler.end; var leftSampleSize, rightSampleSize, leftCategorySize, rightCategorySize, fullBarSize, size; if (length === 1) { leftSampleSize = base > start ? base - start : end - base; rightSampleSize = base < end ? end - base : base - start; } else { if (index > 0) { leftSampleSize = (base - pixels[index - 1]) / 2; if (index === length - 1) { rightSampleSize = leftSampleSize; } } if (index < length - 1) { rightSampleSize = (pixels[index + 1] - base) / 2; if (index === 0) { leftSampleSize = rightSampleSize; } } } leftCategorySize = leftSampleSize * options.categoryPercentage; rightCategorySize = rightSampleSize * options.categoryPercentage; fullBarSize = (leftCategorySize + rightCategorySize) / ruler.stackCount; size = fullBarSize * options.barPercentage; size = Math.min( helpers.valueOrDefault(options.barThickness, size), helpers.valueOrDefault(options.maxBarThickness, Infinity)); base -= leftCategorySize; base += fullBarSize * stackIndex; base += (fullBarSize - size) / 2; return { size: size, base: base, head: base + size, center: base + size / 2 }; }, draw: function() { var me = this; var chart = me.chart; var scale = me.getValueScale(); var rects = me.getMeta().data; var dataset = me.getDataset(); var ilen = rects.length; var i = 0; helpers.canvas.clipArea(chart.ctx, chart.chartArea); for (; i < ilen; ++i) { if (!isNaN(scale.getRightValue(dataset.data[i]))) { rects[i].draw(); } } helpers.canvas.unclipArea(chart.ctx); }, setHoverStyle: function(rectangle) { var dataset = this.chart.data.datasets[rectangle._datasetIndex]; var index = rectangle._index; var custom = rectangle.custom || {}; var model = rectangle._model; model.backgroundColor = custom.hoverBackgroundColor ? custom.hoverBackgroundColor : helpers.valueAtIndexOrDefault(dataset.hoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor)); model.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : helpers.valueAtIndexOrDefault(dataset.hoverBorderColor, index, helpers.getHoverColor(model.borderColor)); model.borderWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : helpers.valueAtIndexOrDefault(dataset.hoverBorderWidth, index, model.borderWidth); }, removeHoverStyle: function(rectangle) { var dataset = this.chart.data.datasets[rectangle._datasetIndex]; var index = rectangle._index; var custom = rectangle.custom || {}; var model = rectangle._model; var rectangleElementOptions = this.chart.options.elements.rectangle; model.backgroundColor = custom.backgroundColor ? custom.backgroundColor : helpers.valueAtIndexOrDefault(dataset.backgroundColor, index, rectangleElementOptions.backgroundColor); model.borderColor = custom.borderColor ? custom.borderColor : helpers.valueAtIndexOrDefault(dataset.borderColor, index, rectangleElementOptions.borderColor); model.borderWidth = custom.borderWidth ? custom.borderWidth : helpers.valueAtIndexOrDefault(dataset.borderWidth, index, rectangleElementOptions.borderWidth); } }); Chart.controllers.horizontalBar = Chart.controllers.bar.extend({ /** * @private */ getValueScaleId: function() { return this.getMeta().xAxisID; }, /** * @private */ getIndexScaleId: function() { return this.getMeta().yAxisID; } }); }; },{"25":25,"40":40,"45":45}],16:[function(require,module,exports){ 'use strict'; var defaults = require(25); var elements = require(40); var helpers = require(45); defaults._set('bubble', { hover: { mode: 'single' }, scales: { xAxes: [{ type: 'linear', // bubble should probably use a linear scale by default position: 'bottom', id: 'x-axis-0' // need an ID so datasets can reference the scale }], yAxes: [{ type: 'linear', position: 'left', id: 'y-axis-0' }] }, tooltips: { callbacks: { title: function() { // Title doesn't make sense for scatter since we format the data as a point return ''; }, label: function(item, data) { var datasetLabel = data.datasets[item.datasetIndex].label || ''; var dataPoint = data.datasets[item.datasetIndex].data[item.index]; return datasetLabel + ': (' + item.xLabel + ', ' + item.yLabel + ', ' + dataPoint.r + ')'; } } } }); module.exports = function(Chart) { Chart.controllers.bubble = Chart.DatasetController.extend({ /** * @protected */ dataElementType: elements.Point, /** * @protected */ update: function(reset) { var me = this; var meta = me.getMeta(); var points = meta.data; // Update Points helpers.each(points, function(point, index) { me.updateElement(point, index, reset); }); }, /** * @protected */ updateElement: function(point, index, reset) { var me = this; var meta = me.getMeta(); var custom = point.custom || {}; var xScale = me.getScaleForId(meta.xAxisID); var yScale = me.getScaleForId(meta.yAxisID); var options = me._resolveElementOptions(point, index); var data = me.getDataset().data[index]; var dsIndex = me.index; var x = reset ? xScale.getPixelForDecimal(0.5) : xScale.getPixelForValue(typeof data === 'object' ? data : NaN, index, dsIndex); var y = reset ? yScale.getBasePixel() : yScale.getPixelForValue(data, index, dsIndex); point._xScale = xScale; point._yScale = yScale; point._options = options; point._datasetIndex = dsIndex; point._index = index; point._model = { backgroundColor: options.backgroundColor, borderColor: options.borderColor, borderWidth: options.borderWidth, hitRadius: options.hitRadius, pointStyle: options.pointStyle, radius: reset ? 0 : options.radius, skip: custom.skip || isNaN(x) || isNaN(y), x: x, y: y, }; point.pivot(); }, /** * @protected */ setHoverStyle: function(point) { var model = point._model; var options = point._options; model.backgroundColor = helpers.valueOrDefault(options.hoverBackgroundColor, helpers.getHoverColor(options.backgroundColor)); model.borderColor = helpers.valueOrDefault(options.hoverBorderColor, helpers.getHoverColor(options.borderColor)); model.borderWidth = helpers.valueOrDefault(options.hoverBorderWidth, options.borderWidth); model.radius = options.radius + options.hoverRadius; }, /** * @protected */ removeHoverStyle: function(point) { var model = point._model; var options = point._options; model.backgroundColor = options.backgroundColor; model.borderColor = options.borderColor; model.borderWidth = options.borderWidth; model.radius = options.radius; }, /** * @private */ _resolveElementOptions: function(point, index) { var me = this; var chart = me.chart; var datasets = chart.data.datasets; var dataset = datasets[me.index]; var custom = point.custom || {}; var options = chart.options.elements.point; var resolve = helpers.options.resolve; var data = dataset.data[index]; var values = {}; var i, ilen, key; // Scriptable options var context = { chart: chart, dataIndex: index, dataset: dataset, datasetIndex: me.index }; var keys = [ 'backgroundColor', 'borderColor', 'borderWidth', 'hoverBackgroundColor', 'hoverBorderColor', 'hoverBorderWidth', 'hoverRadius', 'hitRadius', 'pointStyle' ]; for (i = 0, ilen = keys.length; i < ilen; ++i) { key = keys[i]; values[key] = resolve([ custom[key], dataset[key], options[key] ], context, index); } // Custom radius resolution values.radius = resolve([ custom.radius, data ? data.r : undefined, dataset.radius, options.radius ], context, index); return values; } }); }; },{"25":25,"40":40,"45":45}],17:[function(require,module,exports){ 'use strict'; var defaults = require(25); var elements = require(40); var helpers = require(45); defaults._set('doughnut', { animation: { // Boolean - Whether we animate the rotation of the Doughnut animateRotate: true, // Boolean - Whether we animate scaling the Doughnut from the centre animateScale: false }, hover: { mode: 'single' }, legendCallback: function(chart) { var text = []; text.push('
    '); var data = chart.data; var datasets = data.datasets; var labels = data.labels; if (datasets.length) { for (var i = 0; i < datasets[0].data.length; ++i) { text.push('
  • '); if (labels[i]) { text.push(labels[i]); } text.push('
  • '); } } text.push('
'); return text.join(''); }, legend: { labels: { generateLabels: function(chart) { var data = chart.data; if (data.labels.length && data.datasets.length) { return data.labels.map(function(label, i) { var meta = chart.getDatasetMeta(0); var ds = data.datasets[0]; var arc = meta.data[i]; var custom = arc && arc.custom || {}; var valueAtIndexOrDefault = helpers.valueAtIndexOrDefault; var arcOpts = chart.options.elements.arc; var fill = custom.backgroundColor ? custom.backgroundColor : valueAtIndexOrDefault(ds.backgroundColor, i, arcOpts.backgroundColor); var stroke = custom.borderColor ? custom.borderColor : valueAtIndexOrDefault(ds.borderColor, i, arcOpts.borderColor); var bw = custom.borderWidth ? custom.borderWidth : valueAtIndexOrDefault(ds.borderWidth, i, arcOpts.borderWidth); return { text: label, fillStyle: fill, strokeStyle: stroke, lineWidth: bw, hidden: isNaN(ds.data[i]) || meta.data[i].hidden, // Extra data used for toggling the correct item index: i }; }); } return []; } }, onClick: function(e, legendItem) { var index = legendItem.index; var chart = this.chart; var i, ilen, meta; for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) { meta = chart.getDatasetMeta(i); // toggle visibility of index if exists if (meta.data[index]) { meta.data[index].hidden = !meta.data[index].hidden; } } chart.update(); } }, // The percentage of the chart that we cut out of the middle. cutoutPercentage: 50, // The rotation of the chart, where the first data arc begins. rotation: Math.PI * -0.5, // The total circumference of the chart. circumference: Math.PI * 2.0, // Need to override these to give a nice default tooltips: { callbacks: { title: function() { return ''; }, label: function(tooltipItem, data) { var dataLabel = data.labels[tooltipItem.index]; var value = ': ' + data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index]; if (helpers.isArray(dataLabel)) { // show value on first line of multiline label // need to clone because we are changing the value dataLabel = dataLabel.slice(); dataLabel[0] += value; } else { dataLabel += value; } return dataLabel; } } } }); defaults._set('pie', helpers.clone(defaults.doughnut)); defaults._set('pie', { cutoutPercentage: 0 }); module.exports = function(Chart) { Chart.controllers.doughnut = Chart.controllers.pie = Chart.DatasetController.extend({ dataElementType: elements.Arc, linkScales: helpers.noop, // Get index of the dataset in relation to the visible datasets. This allows determining the inner and outer radius correctly getRingIndex: function(datasetIndex) { var ringIndex = 0; for (var j = 0; j < datasetIndex; ++j) { if (this.chart.isDatasetVisible(j)) { ++ringIndex; } } return ringIndex; }, update: function(reset) { var me = this; var chart = me.chart; var chartArea = chart.chartArea; var opts = chart.options; var arcOpts = opts.elements.arc; var availableWidth = chartArea.right - chartArea.left - arcOpts.borderWidth; var availableHeight = chartArea.bottom - chartArea.top - arcOpts.borderWidth; var minSize = Math.min(availableWidth, availableHeight); var offset = {x: 0, y: 0}; var meta = me.getMeta(); var cutoutPercentage = opts.cutoutPercentage; var circumference = opts.circumference; // If the chart's circumference isn't a full circle, calculate minSize as a ratio of the width/height of the arc if (circumference < Math.PI * 2.0) { var startAngle = opts.rotation % (Math.PI * 2.0); startAngle += Math.PI * 2.0 * (startAngle >= Math.PI ? -1 : startAngle < -Math.PI ? 1 : 0); var endAngle = startAngle + circumference; var start = {x: Math.cos(startAngle), y: Math.sin(startAngle)}; var end = {x: Math.cos(endAngle), y: Math.sin(endAngle)}; var contains0 = (startAngle <= 0 && endAngle >= 0) || (startAngle <= Math.PI * 2.0 && Math.PI * 2.0 <= endAngle); var contains90 = (startAngle <= Math.PI * 0.5 && Math.PI * 0.5 <= endAngle) || (startAngle <= Math.PI * 2.5 && Math.PI * 2.5 <= endAngle); var contains180 = (startAngle <= -Math.PI && -Math.PI <= endAngle) || (startAngle <= Math.PI && Math.PI <= endAngle); var contains270 = (startAngle <= -Math.PI * 0.5 && -Math.PI * 0.5 <= endAngle) || (startAngle <= Math.PI * 1.5 && Math.PI * 1.5 <= endAngle); var cutout = cutoutPercentage / 100.0; var min = {x: contains180 ? -1 : Math.min(start.x * (start.x < 0 ? 1 : cutout), end.x * (end.x < 0 ? 1 : cutout)), y: contains270 ? -1 : Math.min(start.y * (start.y < 0 ? 1 : cutout), end.y * (end.y < 0 ? 1 : cutout))}; var max = {x: contains0 ? 1 : Math.max(start.x * (start.x > 0 ? 1 : cutout), end.x * (end.x > 0 ? 1 : cutout)), y: contains90 ? 1 : Math.max(start.y * (start.y > 0 ? 1 : cutout), end.y * (end.y > 0 ? 1 : cutout))}; var size = {width: (max.x - min.x) * 0.5, height: (max.y - min.y) * 0.5}; minSize = Math.min(availableWidth / size.width, availableHeight / size.height); offset = {x: (max.x + min.x) * -0.5, y: (max.y + min.y) * -0.5}; } chart.borderWidth = me.getMaxBorderWidth(meta.data); chart.outerRadius = Math.max((minSize - chart.borderWidth) / 2, 0); chart.innerRadius = Math.max(cutoutPercentage ? (chart.outerRadius / 100) * (cutoutPercentage) : 0, 0); chart.radiusLength = (chart.outerRadius - chart.innerRadius) / chart.getVisibleDatasetCount(); chart.offsetX = offset.x * chart.outerRadius; chart.offsetY = offset.y * chart.outerRadius; meta.total = me.calculateTotal(); me.outerRadius = chart.outerRadius - (chart.radiusLength * me.getRingIndex(me.index)); me.innerRadius = Math.max(me.outerRadius - chart.radiusLength, 0); helpers.each(meta.data, function(arc, index) { me.updateElement(arc, index, reset); }); }, updateElement: function(arc, index, reset) { var me = this; var chart = me.chart; var chartArea = chart.chartArea; var opts = chart.options; var animationOpts = opts.animation; var centerX = (chartArea.left + chartArea.right) / 2; var centerY = (chartArea.top + chartArea.bottom) / 2; var startAngle = opts.rotation; // non reset case handled later var endAngle = opts.rotation; // non reset case handled later var dataset = me.getDataset(); var circumference = reset && animationOpts.animateRotate ? 0 : arc.hidden ? 0 : me.calculateCircumference(dataset.data[index]) * (opts.circumference / (2.0 * Math.PI)); var innerRadius = reset && animationOpts.animateScale ? 0 : me.innerRadius; var outerRadius = reset && animationOpts.animateScale ? 0 : me.outerRadius; var valueAtIndexOrDefault = helpers.valueAtIndexOrDefault; helpers.extend(arc, { // Utility _datasetIndex: me.index, _index: index, // Desired view properties _model: { x: centerX + chart.offsetX, y: centerY + chart.offsetY, startAngle: startAngle, endAngle: endAngle, circumference: circumference, outerRadius: outerRadius, innerRadius: innerRadius, label: valueAtIndexOrDefault(dataset.label, index, chart.data.labels[index]) } }); var model = arc._model; // Resets the visual styles this.removeHoverStyle(arc); // Set correct angles if not resetting if (!reset || !animationOpts.animateRotate) { if (index === 0) { model.startAngle = opts.rotation; } else { model.startAngle = me.getMeta().data[index - 1]._model.endAngle; } model.endAngle = model.startAngle + model.circumference; } arc.pivot(); }, removeHoverStyle: function(arc) { Chart.DatasetController.prototype.removeHoverStyle.call(this, arc, this.chart.options.elements.arc); }, calculateTotal: function() { var dataset = this.getDataset(); var meta = this.getMeta(); var total = 0; var value; helpers.each(meta.data, function(element, index) { value = dataset.data[index]; if (!isNaN(value) && !element.hidden) { total += Math.abs(value); } }); /* if (total === 0) { total = NaN; }*/ return total; }, calculateCircumference: function(value) { var total = this.getMeta().total; if (total > 0 && !isNaN(value)) { return (Math.PI * 2.0) * (value / total); } return 0; }, // gets the max border or hover width to properly scale pie charts getMaxBorderWidth: function(arcs) { var max = 0; var index = this.index; var length = arcs.length; var borderWidth; var hoverWidth; for (var i = 0; i < length; i++) { borderWidth = arcs[i]._model ? arcs[i]._model.borderWidth : 0; hoverWidth = arcs[i]._chart ? arcs[i]._chart.config.data.datasets[index].hoverBorderWidth : 0; max = borderWidth > max ? borderWidth : max; max = hoverWidth > max ? hoverWidth : max; } return max; } }); }; },{"25":25,"40":40,"45":45}],18:[function(require,module,exports){ 'use strict'; var defaults = require(25); var elements = require(40); var helpers = require(45); defaults._set('line', { showLines: true, spanGaps: false, hover: { mode: 'label' }, scales: { xAxes: [{ type: 'category', id: 'x-axis-0' }], yAxes: [{ type: 'linear', id: 'y-axis-0' }] } }); module.exports = function(Chart) { function lineEnabled(dataset, options) { return helpers.valueOrDefault(dataset.showLine, options.showLines); } Chart.controllers.line = Chart.DatasetController.extend({ datasetElementType: elements.Line, dataElementType: elements.Point, update: function(reset) { var me = this; var meta = me.getMeta(); var line = meta.dataset; var points = meta.data || []; var options = me.chart.options; var lineElementOptions = options.elements.line; var scale = me.getScaleForId(meta.yAxisID); var i, ilen, custom; var dataset = me.getDataset(); var showLine = lineEnabled(dataset, options); // Update Line if (showLine) { custom = line.custom || {}; // Compatibility: If the properties are defined with only the old name, use those values if ((dataset.tension !== undefined) && (dataset.lineTension === undefined)) { dataset.lineTension = dataset.tension; } // Utility line._scale = scale; line._datasetIndex = me.index; // Data line._children = points; // Model line._model = { // Appearance // The default behavior of lines is to break at null values, according // to https://github.com/chartjs/Chart.js/issues/2435#issuecomment-216718158 // This option gives lines the ability to span gaps spanGaps: dataset.spanGaps ? dataset.spanGaps : options.spanGaps, tension: custom.tension ? custom.tension : helpers.valueOrDefault(dataset.lineTension, lineElementOptions.tension), backgroundColor: custom.backgroundColor ? custom.backgroundColor : (dataset.backgroundColor || lineElementOptions.backgroundColor), borderWidth: custom.borderWidth ? custom.borderWidth : (dataset.borderWidth || lineElementOptions.borderWidth), borderColor: custom.borderColor ? custom.borderColor : (dataset.borderColor || lineElementOptions.borderColor), borderCapStyle: custom.borderCapStyle ? custom.borderCapStyle : (dataset.borderCapStyle || lineElementOptions.borderCapStyle), borderDash: custom.borderDash ? custom.borderDash : (dataset.borderDash || lineElementOptions.borderDash), borderDashOffset: custom.borderDashOffset ? custom.borderDashOffset : (dataset.borderDashOffset || lineElementOptions.borderDashOffset), borderJoinStyle: custom.borderJoinStyle ? custom.borderJoinStyle : (dataset.borderJoinStyle || lineElementOptions.borderJoinStyle), fill: custom.fill ? custom.fill : (dataset.fill !== undefined ? dataset.fill : lineElementOptions.fill), steppedLine: custom.steppedLine ? custom.steppedLine : helpers.valueOrDefault(dataset.steppedLine, lineElementOptions.stepped), cubicInterpolationMode: custom.cubicInterpolationMode ? custom.cubicInterpolationMode : helpers.valueOrDefault(dataset.cubicInterpolationMode, lineElementOptions.cubicInterpolationMode), }; line.pivot(); } // Update Points for (i = 0, ilen = points.length; i < ilen; ++i) { me.updateElement(points[i], i, reset); } if (showLine && line._model.tension !== 0) { me.updateBezierControlPoints(); } // Now pivot the point for animation for (i = 0, ilen = points.length; i < ilen; ++i) { points[i].pivot(); } }, getPointBackgroundColor: function(point, index) { var backgroundColor = this.chart.options.elements.point.backgroundColor; var dataset = this.getDataset(); var custom = point.custom || {}; if (custom.backgroundColor) { backgroundColor = custom.backgroundColor; } else if (dataset.pointBackgroundColor) { backgroundColor = helpers.valueAtIndexOrDefault(dataset.pointBackgroundColor, index, backgroundColor); } else if (dataset.backgroundColor) { backgroundColor = dataset.backgroundColor; } return backgroundColor; }, getPointBorderColor: function(point, index) { var borderColor = this.chart.options.elements.point.borderColor; var dataset = this.getDataset(); var custom = point.custom || {}; if (custom.borderColor) { borderColor = custom.borderColor; } else if (dataset.pointBorderColor) { borderColor = helpers.valueAtIndexOrDefault(dataset.pointBorderColor, index, borderColor); } else if (dataset.borderColor) { borderColor = dataset.borderColor; } return borderColor; }, getPointBorderWidth: function(point, index) { var borderWidth = this.chart.options.elements.point.borderWidth; var dataset = this.getDataset(); var custom = point.custom || {}; if (!isNaN(custom.borderWidth)) { borderWidth = custom.borderWidth; } else if (!isNaN(dataset.pointBorderWidth) || helpers.isArray(dataset.pointBorderWidth)) { borderWidth = helpers.valueAtIndexOrDefault(dataset.pointBorderWidth, index, borderWidth); } else if (!isNaN(dataset.borderWidth)) { borderWidth = dataset.borderWidth; } return borderWidth; }, updateElement: function(point, index, reset) { var me = this; var meta = me.getMeta(); var custom = point.custom || {}; var dataset = me.getDataset(); var datasetIndex = me.index; var value = dataset.data[index]; var yScale = me.getScaleForId(meta.yAxisID); var xScale = me.getScaleForId(meta.xAxisID); var pointOptions = me.chart.options.elements.point; var x, y; // Compatibility: If the properties are defined with only the old name, use those values if ((dataset.radius !== undefined) && (dataset.pointRadius === undefined)) { dataset.pointRadius = dataset.radius; } if ((dataset.hitRadius !== undefined) && (dataset.pointHitRadius === undefined)) { dataset.pointHitRadius = dataset.hitRadius; } x = xScale.getPixelForValue(typeof value === 'object' ? value : NaN, index, datasetIndex); y = reset ? yScale.getBasePixel() : me.calculatePointY(value, index, datasetIndex); // Utility point._xScale = xScale; point._yScale = yScale; point._datasetIndex = datasetIndex; point._index = index; // Desired view properties point._model = { x: x, y: y, skip: custom.skip || isNaN(x) || isNaN(y), // Appearance radius: custom.radius || helpers.valueAtIndexOrDefault(dataset.pointRadius, index, pointOptions.radius), pointStyle: custom.pointStyle || helpers.valueAtIndexOrDefault(dataset.pointStyle, index, pointOptions.pointStyle), backgroundColor: me.getPointBackgroundColor(point, index), borderColor: me.getPointBorderColor(point, index), borderWidth: me.getPointBorderWidth(point, index), tension: meta.dataset._model ? meta.dataset._model.tension : 0, steppedLine: meta.dataset._model ? meta.dataset._model.steppedLine : false, // Tooltip hitRadius: custom.hitRadius || helpers.valueAtIndexOrDefault(dataset.pointHitRadius, index, pointOptions.hitRadius) }; }, calculatePointY: function(value, index, datasetIndex) { var me = this; var chart = me.chart; var meta = me.getMeta(); var yScale = me.getScaleForId(meta.yAxisID); var sumPos = 0; var sumNeg = 0; var i, ds, dsMeta; if (yScale.options.stacked) { for (i = 0; i < datasetIndex; i++) { ds = chart.data.datasets[i]; dsMeta = chart.getDatasetMeta(i); if (dsMeta.type === 'line' && dsMeta.yAxisID === yScale.id && chart.isDatasetVisible(i)) { var stackedRightValue = Number(yScale.getRightValue(ds.data[index])); if (stackedRightValue < 0) { sumNeg += stackedRightValue || 0; } else { sumPos += stackedRightValue || 0; } } } var rightValue = Number(yScale.getRightValue(value)); if (rightValue < 0) { return yScale.getPixelForValue(sumNeg + rightValue); } return yScale.getPixelForValue(sumPos + rightValue); } return yScale.getPixelForValue(value); }, updateBezierControlPoints: function() { var me = this; var meta = me.getMeta(); var area = me.chart.chartArea; var points = (meta.data || []); var i, ilen, point, model, controlPoints; // Only consider points that are drawn in case the spanGaps option is used if (meta.dataset._model.spanGaps) { points = points.filter(function(pt) { return !pt._model.skip; }); } function capControlPoint(pt, min, max) { return Math.max(Math.min(pt, max), min); } if (meta.dataset._model.cubicInterpolationMode === 'monotone') { helpers.splineCurveMonotone(points); } else { for (i = 0, ilen = points.length; i < ilen; ++i) { point = points[i]; model = point._model; controlPoints = helpers.splineCurve( helpers.previousItem(points, i)._model, model, helpers.nextItem(points, i)._model, meta.dataset._model.tension ); model.controlPointPreviousX = controlPoints.previous.x; model.controlPointPreviousY = controlPoints.previous.y; model.controlPointNextX = controlPoints.next.x; model.controlPointNextY = controlPoints.next.y; } } if (me.chart.options.elements.line.capBezierPoints) { for (i = 0, ilen = points.length; i < ilen; ++i) { model = points[i]._model; model.controlPointPreviousX = capControlPoint(model.controlPointPreviousX, area.left, area.right); model.controlPointPreviousY = capControlPoint(model.controlPointPreviousY, area.top, area.bottom); model.controlPointNextX = capControlPoint(model.controlPointNextX, area.left, area.right); model.controlPointNextY = capControlPoint(model.controlPointNextY, area.top, area.bottom); } } }, draw: function() { var me = this; var chart = me.chart; var meta = me.getMeta(); var points = meta.data || []; var area = chart.chartArea; var ilen = points.length; var i = 0; helpers.canvas.clipArea(chart.ctx, area); if (lineEnabled(me.getDataset(), chart.options)) { meta.dataset.draw(); } helpers.canvas.unclipArea(chart.ctx); // Draw the points for (; i < ilen; ++i) { points[i].draw(area); } }, setHoverStyle: function(point) { // Point var dataset = this.chart.data.datasets[point._datasetIndex]; var index = point._index; var custom = point.custom || {}; var model = point._model; model.radius = custom.hoverRadius || helpers.valueAtIndexOrDefault(dataset.pointHoverRadius, index, this.chart.options.elements.point.hoverRadius); model.backgroundColor = custom.hoverBackgroundColor || helpers.valueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor)); model.borderColor = custom.hoverBorderColor || helpers.valueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.getHoverColor(model.borderColor)); model.borderWidth = custom.hoverBorderWidth || helpers.valueAtIndexOrDefault(dataset.pointHoverBorderWidth, index, model.borderWidth); }, removeHoverStyle: function(point) { var me = this; var dataset = me.chart.data.datasets[point._datasetIndex]; var index = point._index; var custom = point.custom || {}; var model = point._model; // Compatibility: If the properties are defined with only the old name, use those values if ((dataset.radius !== undefined) && (dataset.pointRadius === undefined)) { dataset.pointRadius = dataset.radius; } model.radius = custom.radius || helpers.valueAtIndexOrDefault(dataset.pointRadius, index, me.chart.options.elements.point.radius); model.backgroundColor = me.getPointBackgroundColor(point, index); model.borderColor = me.getPointBorderColor(point, index); model.borderWidth = me.getPointBorderWidth(point, index); } }); }; },{"25":25,"40":40,"45":45}],19:[function(require,module,exports){ 'use strict'; var defaults = require(25); var elements = require(40); var helpers = require(45); defaults._set('polarArea', { scale: { type: 'radialLinear', angleLines: { display: false }, gridLines: { circular: true }, pointLabels: { display: false }, ticks: { beginAtZero: true } }, // Boolean - Whether to animate the rotation of the chart animation: { animateRotate: true, animateScale: true }, startAngle: -0.5 * Math.PI, legendCallback: function(chart) { var text = []; text.push('
    '); var data = chart.data; var datasets = data.datasets; var labels = data.labels; if (datasets.length) { for (var i = 0; i < datasets[0].data.length; ++i) { text.push('
  • '); if (labels[i]) { text.push(labels[i]); } text.push('
  • '); } } text.push('
'); return text.join(''); }, legend: { labels: { generateLabels: function(chart) { var data = chart.data; if (data.labels.length && data.datasets.length) { return data.labels.map(function(label, i) { var meta = chart.getDatasetMeta(0); var ds = data.datasets[0]; var arc = meta.data[i]; var custom = arc.custom || {}; var valueAtIndexOrDefault = helpers.valueAtIndexOrDefault; var arcOpts = chart.options.elements.arc; var fill = custom.backgroundColor ? custom.backgroundColor : valueAtIndexOrDefault(ds.backgroundColor, i, arcOpts.backgroundColor); var stroke = custom.borderColor ? custom.borderColor : valueAtIndexOrDefault(ds.borderColor, i, arcOpts.borderColor); var bw = custom.borderWidth ? custom.borderWidth : valueAtIndexOrDefault(ds.borderWidth, i, arcOpts.borderWidth); return { text: label, fillStyle: fill, strokeStyle: stroke, lineWidth: bw, hidden: isNaN(ds.data[i]) || meta.data[i].hidden, // Extra data used for toggling the correct item index: i }; }); } return []; } }, onClick: function(e, legendItem) { var index = legendItem.index; var chart = this.chart; var i, ilen, meta; for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) { meta = chart.getDatasetMeta(i); meta.data[index].hidden = !meta.data[index].hidden; } chart.update(); } }, // Need to override these to give a nice default tooltips: { callbacks: { title: function() { return ''; }, label: function(item, data) { return data.labels[item.index] + ': ' + item.yLabel; } } } }); module.exports = function(Chart) { Chart.controllers.polarArea = Chart.DatasetController.extend({ dataElementType: elements.Arc, linkScales: helpers.noop, update: function(reset) { var me = this; var chart = me.chart; var chartArea = chart.chartArea; var meta = me.getMeta(); var opts = chart.options; var arcOpts = opts.elements.arc; var minSize = Math.min(chartArea.right - chartArea.left, chartArea.bottom - chartArea.top); chart.outerRadius = Math.max((minSize - arcOpts.borderWidth / 2) / 2, 0); chart.innerRadius = Math.max(opts.cutoutPercentage ? (chart.outerRadius / 100) * (opts.cutoutPercentage) : 1, 0); chart.radiusLength = (chart.outerRadius - chart.innerRadius) / chart.getVisibleDatasetCount(); me.outerRadius = chart.outerRadius - (chart.radiusLength * me.index); me.innerRadius = me.outerRadius - chart.radiusLength; meta.count = me.countVisibleElements(); helpers.each(meta.data, function(arc, index) { me.updateElement(arc, index, reset); }); }, updateElement: function(arc, index, reset) { var me = this; var chart = me.chart; var dataset = me.getDataset(); var opts = chart.options; var animationOpts = opts.animation; var scale = chart.scale; var labels = chart.data.labels; var circumference = me.calculateCircumference(dataset.data[index]); var centerX = scale.xCenter; var centerY = scale.yCenter; // If there is NaN data before us, we need to calculate the starting angle correctly. // We could be way more efficient here, but its unlikely that the polar area chart will have a lot of data var visibleCount = 0; var meta = me.getMeta(); for (var i = 0; i < index; ++i) { if (!isNaN(dataset.data[i]) && !meta.data[i].hidden) { ++visibleCount; } } // var negHalfPI = -0.5 * Math.PI; var datasetStartAngle = opts.startAngle; var distance = arc.hidden ? 0 : scale.getDistanceFromCenterForValue(dataset.data[index]); var startAngle = datasetStartAngle + (circumference * visibleCount); var endAngle = startAngle + (arc.hidden ? 0 : circumference); var resetRadius = animationOpts.animateScale ? 0 : scale.getDistanceFromCenterForValue(dataset.data[index]); helpers.extend(arc, { // Utility _datasetIndex: me.index, _index: index, _scale: scale, // Desired view properties _model: { x: centerX, y: centerY, innerRadius: 0, outerRadius: reset ? resetRadius : distance, startAngle: reset && animationOpts.animateRotate ? datasetStartAngle : startAngle, endAngle: reset && animationOpts.animateRotate ? datasetStartAngle : endAngle, label: helpers.valueAtIndexOrDefault(labels, index, labels[index]) } }); // Apply border and fill style me.removeHoverStyle(arc); arc.pivot(); }, removeHoverStyle: function(arc) { Chart.DatasetController.prototype.removeHoverStyle.call(this, arc, this.chart.options.elements.arc); }, countVisibleElements: function() { var dataset = this.getDataset(); var meta = this.getMeta(); var count = 0; helpers.each(meta.data, function(element, index) { if (!isNaN(dataset.data[index]) && !element.hidden) { count++; } }); return count; }, calculateCircumference: function(value) { var count = this.getMeta().count; if (count > 0 && !isNaN(value)) { return (2 * Math.PI) / count; } return 0; } }); }; },{"25":25,"40":40,"45":45}],20:[function(require,module,exports){ 'use strict'; var defaults = require(25); var elements = require(40); var helpers = require(45); defaults._set('radar', { scale: { type: 'radialLinear' }, elements: { line: { tension: 0 // no bezier in radar } } }); module.exports = function(Chart) { Chart.controllers.radar = Chart.DatasetController.extend({ datasetElementType: elements.Line, dataElementType: elements.Point, linkScales: helpers.noop, update: function(reset) { var me = this; var meta = me.getMeta(); var line = meta.dataset; var points = meta.data; var custom = line.custom || {}; var dataset = me.getDataset(); var lineElementOptions = me.chart.options.elements.line; var scale = me.chart.scale; // Compatibility: If the properties are defined with only the old name, use those values if ((dataset.tension !== undefined) && (dataset.lineTension === undefined)) { dataset.lineTension = dataset.tension; } helpers.extend(meta.dataset, { // Utility _datasetIndex: me.index, _scale: scale, // Data _children: points, _loop: true, // Model _model: { // Appearance tension: custom.tension ? custom.tension : helpers.valueOrDefault(dataset.lineTension, lineElementOptions.tension), backgroundColor: custom.backgroundColor ? custom.backgroundColor : (dataset.backgroundColor || lineElementOptions.backgroundColor), borderWidth: custom.borderWidth ? custom.borderWidth : (dataset.borderWidth || lineElementOptions.borderWidth), borderColor: custom.borderColor ? custom.borderColor : (dataset.borderColor || lineElementOptions.borderColor), fill: custom.fill ? custom.fill : (dataset.fill !== undefined ? dataset.fill : lineElementOptions.fill), borderCapStyle: custom.borderCapStyle ? custom.borderCapStyle : (dataset.borderCapStyle || lineElementOptions.borderCapStyle), borderDash: custom.borderDash ? custom.borderDash : (dataset.borderDash || lineElementOptions.borderDash), borderDashOffset: custom.borderDashOffset ? custom.borderDashOffset : (dataset.borderDashOffset || lineElementOptions.borderDashOffset), borderJoinStyle: custom.borderJoinStyle ? custom.borderJoinStyle : (dataset.borderJoinStyle || lineElementOptions.borderJoinStyle), } }); meta.dataset.pivot(); // Update Points helpers.each(points, function(point, index) { me.updateElement(point, index, reset); }, me); // Update bezier control points me.updateBezierControlPoints(); }, updateElement: function(point, index, reset) { var me = this; var custom = point.custom || {}; var dataset = me.getDataset(); var scale = me.chart.scale; var pointElementOptions = me.chart.options.elements.point; var pointPosition = scale.getPointPositionForValue(index, dataset.data[index]); // Compatibility: If the properties are defined with only the old name, use those values if ((dataset.radius !== undefined) && (dataset.pointRadius === undefined)) { dataset.pointRadius = dataset.radius; } if ((dataset.hitRadius !== undefined) && (dataset.pointHitRadius === undefined)) { dataset.pointHitRadius = dataset.hitRadius; } helpers.extend(point, { // Utility _datasetIndex: me.index, _index: index, _scale: scale, // Desired view properties _model: { x: reset ? scale.xCenter : pointPosition.x, // value not used in dataset scale, but we want a consistent API between scales y: reset ? scale.yCenter : pointPosition.y, // Appearance tension: custom.tension ? custom.tension : helpers.valueOrDefault(dataset.lineTension, me.chart.options.elements.line.tension), radius: custom.radius ? custom.radius : helpers.valueAtIndexOrDefault(dataset.pointRadius, index, pointElementOptions.radius), backgroundColor: custom.backgroundColor ? custom.backgroundColor : helpers.valueAtIndexOrDefault(dataset.pointBackgroundColor, index, pointElementOptions.backgroundColor), borderColor: custom.borderColor ? custom.borderColor : helpers.valueAtIndexOrDefault(dataset.pointBorderColor, index, pointElementOptions.borderColor), borderWidth: custom.borderWidth ? custom.borderWidth : helpers.valueAtIndexOrDefault(dataset.pointBorderWidth, index, pointElementOptions.borderWidth), pointStyle: custom.pointStyle ? custom.pointStyle : helpers.valueAtIndexOrDefault(dataset.pointStyle, index, pointElementOptions.pointStyle), // Tooltip hitRadius: custom.hitRadius ? custom.hitRadius : helpers.valueAtIndexOrDefault(dataset.pointHitRadius, index, pointElementOptions.hitRadius) } }); point._model.skip = custom.skip ? custom.skip : (isNaN(point._model.x) || isNaN(point._model.y)); }, updateBezierControlPoints: function() { var chartArea = this.chart.chartArea; var meta = this.getMeta(); helpers.each(meta.data, function(point, index) { var model = point._model; var controlPoints = helpers.splineCurve( helpers.previousItem(meta.data, index, true)._model, model, helpers.nextItem(meta.data, index, true)._model, model.tension ); // Prevent the bezier going outside of the bounds of the graph model.controlPointPreviousX = Math.max(Math.min(controlPoints.previous.x, chartArea.right), chartArea.left); model.controlPointPreviousY = Math.max(Math.min(controlPoints.previous.y, chartArea.bottom), chartArea.top); model.controlPointNextX = Math.max(Math.min(controlPoints.next.x, chartArea.right), chartArea.left); model.controlPointNextY = Math.max(Math.min(controlPoints.next.y, chartArea.bottom), chartArea.top); // Now pivot the point for animation point.pivot(); }); }, setHoverStyle: function(point) { // Point var dataset = this.chart.data.datasets[point._datasetIndex]; var custom = point.custom || {}; var index = point._index; var model = point._model; model.radius = custom.hoverRadius ? custom.hoverRadius : helpers.valueAtIndexOrDefault(dataset.pointHoverRadius, index, this.chart.options.elements.point.hoverRadius); model.backgroundColor = custom.hoverBackgroundColor ? custom.hoverBackgroundColor : helpers.valueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor)); model.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : helpers.valueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.getHoverColor(model.borderColor)); model.borderWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : helpers.valueAtIndexOrDefault(dataset.pointHoverBorderWidth, index, model.borderWidth); }, removeHoverStyle: function(point) { var dataset = this.chart.data.datasets[point._datasetIndex]; var custom = point.custom || {}; var index = point._index; var model = point._model; var pointElementOptions = this.chart.options.elements.point; model.radius = custom.radius ? custom.radius : helpers.valueAtIndexOrDefault(dataset.pointRadius, index, pointElementOptions.radius); model.backgroundColor = custom.backgroundColor ? custom.backgroundColor : helpers.valueAtIndexOrDefault(dataset.pointBackgroundColor, index, pointElementOptions.backgroundColor); model.borderColor = custom.borderColor ? custom.borderColor : helpers.valueAtIndexOrDefault(dataset.pointBorderColor, index, pointElementOptions.borderColor); model.borderWidth = custom.borderWidth ? custom.borderWidth : helpers.valueAtIndexOrDefault(dataset.pointBorderWidth, index, pointElementOptions.borderWidth); } }); }; },{"25":25,"40":40,"45":45}],21:[function(require,module,exports){ 'use strict'; var defaults = require(25); defaults._set('scatter', { hover: { mode: 'single' }, scales: { xAxes: [{ id: 'x-axis-1', // need an ID so datasets can reference the scale type: 'linear', // scatter should not use a category axis position: 'bottom' }], yAxes: [{ id: 'y-axis-1', type: 'linear', position: 'left' }] }, showLines: false, tooltips: { callbacks: { title: function() { return ''; // doesn't make sense for scatter since data are formatted as a point }, label: function(item) { return '(' + item.xLabel + ', ' + item.yLabel + ')'; } } } }); module.exports = function(Chart) { // Scatter charts use line controllers Chart.controllers.scatter = Chart.controllers.line; }; },{"25":25}],22:[function(require,module,exports){ /* global window: false */ 'use strict'; var defaults = require(25); var Element = require(26); var helpers = require(45); defaults._set('global', { animation: { duration: 1000, easing: 'easeOutQuart', onProgress: helpers.noop, onComplete: helpers.noop } }); module.exports = function(Chart) { Chart.Animation = Element.extend({ chart: null, // the animation associated chart instance currentStep: 0, // the current animation step numSteps: 60, // default number of steps easing: '', // the easing to use for this animation render: null, // render function used by the animation service onAnimationProgress: null, // user specified callback to fire on each step of the animation onAnimationComplete: null, // user specified callback to fire when the animation finishes }); Chart.animationService = { frameDuration: 17, animations: [], dropFrames: 0, request: null, /** * @param {Chart} chart - The chart to animate. * @param {Chart.Animation} animation - The animation that we will animate. * @param {Number} duration - The animation duration in ms. * @param {Boolean} lazy - if true, the chart is not marked as animating to enable more responsive interactions */ addAnimation: function(chart, animation, duration, lazy) { var animations = this.animations; var i, ilen; animation.chart = chart; if (!lazy) { chart.animating = true; } for (i = 0, ilen = animations.length; i < ilen; ++i) { if (animations[i].chart === chart) { animations[i] = animation; return; } } animations.push(animation); // If there are no animations queued, manually kickstart a digest, for lack of a better word if (animations.length === 1) { this.requestAnimationFrame(); } }, cancelAnimation: function(chart) { var index = helpers.findIndex(this.animations, function(animation) { return animation.chart === chart; }); if (index !== -1) { this.animations.splice(index, 1); chart.animating = false; } }, requestAnimationFrame: function() { var me = this; if (me.request === null) { // Skip animation frame requests until the active one is executed. // This can happen when processing mouse events, e.g. 'mousemove' // and 'mouseout' events will trigger multiple renders. me.request = helpers.requestAnimFrame.call(window, function() { me.request = null; me.startDigest(); }); } }, /** * @private */ startDigest: function() { var me = this; var startTime = Date.now(); var framesToDrop = 0; if (me.dropFrames > 1) { framesToDrop = Math.floor(me.dropFrames); me.dropFrames = me.dropFrames % 1; } me.advance(1 + framesToDrop); var endTime = Date.now(); me.dropFrames += (endTime - startTime) / me.frameDuration; // Do we have more stuff to animate? if (me.animations.length > 0) { me.requestAnimationFrame(); } }, /** * @private */ advance: function(count) { var animations = this.animations; var animation, chart; var i = 0; while (i < animations.length) { animation = animations[i]; chart = animation.chart; animation.currentStep = (animation.currentStep || 0) + count; animation.currentStep = Math.min(animation.currentStep, animation.numSteps); helpers.callback(animation.render, [chart, animation], chart); helpers.callback(animation.onAnimationProgress, [animation], chart); if (animation.currentStep >= animation.numSteps) { helpers.callback(animation.onAnimationComplete, [animation], chart); chart.animating = false; animations.splice(i, 1); } else { ++i; } } } }; /** * Provided for backward compatibility, use Chart.Animation instead * @prop Chart.Animation#animationObject * @deprecated since version 2.6.0 * @todo remove at version 3 */ Object.defineProperty(Chart.Animation.prototype, 'animationObject', { get: function() { return this; } }); /** * Provided for backward compatibility, use Chart.Animation#chart instead * @prop Chart.Animation#chartInstance * @deprecated since version 2.6.0 * @todo remove at version 3 */ Object.defineProperty(Chart.Animation.prototype, 'chartInstance', { get: function() { return this.chart; }, set: function(value) { this.chart = value; } }); }; },{"25":25,"26":26,"45":45}],23:[function(require,module,exports){ 'use strict'; var defaults = require(25); var helpers = require(45); var Interaction = require(28); var platform = require(48); module.exports = function(Chart) { var plugins = Chart.plugins; // Create a dictionary of chart types, to allow for extension of existing types Chart.types = {}; // Store a reference to each instance - allowing us to globally resize chart instances on window resize. // Destroy method on the chart will remove the instance of the chart from this reference. Chart.instances = {}; // Controllers available for dataset visualization eg. bar, line, slice, etc. Chart.controllers = {}; /** * Initializes the given config with global and chart default values. */ function initConfig(config) { config = config || {}; // Do NOT use configMerge() for the data object because this method merges arrays // and so would change references to labels and datasets, preventing data updates. var data = config.data = config.data || {}; data.datasets = data.datasets || []; data.labels = data.labels || []; config.options = helpers.configMerge( defaults.global, defaults[config.type], config.options || {}); return config; } /** * Updates the config of the chart * @param chart {Chart} chart to update the options for */ function updateConfig(chart) { var newOptions = chart.options; // Update Scale(s) with options if (newOptions.scale) { chart.scale.options = newOptions.scale; } else if (newOptions.scales) { newOptions.scales.xAxes.concat(newOptions.scales.yAxes).forEach(function(scaleOptions) { chart.scales[scaleOptions.id].options = scaleOptions; }); } // Tooltip chart.tooltip._options = newOptions.tooltips; } function positionIsHorizontal(position) { return position === 'top' || position === 'bottom'; } helpers.extend(Chart.prototype, /** @lends Chart */ { /** * @private */ construct: function(item, config) { var me = this; config = initConfig(config); var context = platform.acquireContext(item, config); var canvas = context && context.canvas; var height = canvas && canvas.height; var width = canvas && canvas.width; me.id = helpers.uid(); me.ctx = context; me.canvas = canvas; me.config = config; me.width = width; me.height = height; me.aspectRatio = height ? width / height : null; me.options = config.options; me._bufferedRender = false; /** * Provided for backward compatibility, Chart and Chart.Controller have been merged, * the "instance" still need to be defined since it might be called from plugins. * @prop Chart#chart * @deprecated since version 2.6.0 * @todo remove at version 3 * @private */ me.chart = me; me.controller = me; // chart.chart.controller #inception // Add the chart instance to the global namespace Chart.instances[me.id] = me; // Define alias to the config data: `chart.data === chart.config.data` Object.defineProperty(me, 'data', { get: function() { return me.config.data; }, set: function(value) { me.config.data = value; } }); if (!context || !canvas) { // The given item is not a compatible context2d element, let's return before finalizing // the chart initialization but after setting basic chart / controller properties that // can help to figure out that the chart is not valid (e.g chart.canvas !== null); // https://github.com/chartjs/Chart.js/issues/2807 console.error("Failed to create chart: can't acquire context from the given item"); return; } me.initialize(); me.update(); }, /** * @private */ initialize: function() { var me = this; // Before init plugin notification plugins.notify(me, 'beforeInit'); helpers.retinaScale(me, me.options.devicePixelRatio); me.bindEvents(); if (me.options.responsive) { // Initial resize before chart draws (must be silent to preserve initial animations). me.resize(true); } // Make sure scales have IDs and are built before we build any controllers. me.ensureScalesHaveIDs(); me.buildScales(); me.initToolTip(); // After init plugin notification plugins.notify(me, 'afterInit'); return me; }, clear: function() { helpers.canvas.clear(this); return this; }, stop: function() { // Stops any current animation loop occurring Chart.animationService.cancelAnimation(this); return this; }, resize: function(silent) { var me = this; var options = me.options; var canvas = me.canvas; var aspectRatio = (options.maintainAspectRatio && me.aspectRatio) || null; // the canvas render width and height will be casted to integers so make sure that // the canvas display style uses the same integer values to avoid blurring effect. // Set to 0 instead of canvas.size because the size defaults to 300x150 if the element is collased var newWidth = Math.max(0, Math.floor(helpers.getMaximumWidth(canvas))); var newHeight = Math.max(0, Math.floor(aspectRatio ? newWidth / aspectRatio : helpers.getMaximumHeight(canvas))); if (me.width === newWidth && me.height === newHeight) { return; } canvas.width = me.width = newWidth; canvas.height = me.height = newHeight; canvas.style.width = newWidth + 'px'; canvas.style.height = newHeight + 'px'; helpers.retinaScale(me, options.devicePixelRatio); if (!silent) { // Notify any plugins about the resize var newSize = {width: newWidth, height: newHeight}; plugins.notify(me, 'resize', [newSize]); // Notify of resize if (me.options.onResize) { me.options.onResize(me, newSize); } me.stop(); me.update(me.options.responsiveAnimationDuration); } }, ensureScalesHaveIDs: function() { var options = this.options; var scalesOptions = options.scales || {}; var scaleOptions = options.scale; helpers.each(scalesOptions.xAxes, function(xAxisOptions, index) { xAxisOptions.id = xAxisOptions.id || ('x-axis-' + index); }); helpers.each(scalesOptions.yAxes, function(yAxisOptions, index) { yAxisOptions.id = yAxisOptions.id || ('y-axis-' + index); }); if (scaleOptions) { scaleOptions.id = scaleOptions.id || 'scale'; } }, /** * Builds a map of scale ID to scale object for future lookup. */ buildScales: function() { var me = this; var options = me.options; var scales = me.scales = {}; var items = []; if (options.scales) { items = items.concat( (options.scales.xAxes || []).map(function(xAxisOptions) { return {options: xAxisOptions, dtype: 'category', dposition: 'bottom'}; }), (options.scales.yAxes || []).map(function(yAxisOptions) { return {options: yAxisOptions, dtype: 'linear', dposition: 'left'}; }) ); } if (options.scale) { items.push({ options: options.scale, dtype: 'radialLinear', isDefault: true, dposition: 'chartArea' }); } helpers.each(items, function(item) { var scaleOptions = item.options; var scaleType = helpers.valueOrDefault(scaleOptions.type, item.dtype); var scaleClass = Chart.scaleService.getScaleConstructor(scaleType); if (!scaleClass) { return; } if (positionIsHorizontal(scaleOptions.position) !== positionIsHorizontal(item.dposition)) { scaleOptions.position = item.dposition; } var scale = new scaleClass({ id: scaleOptions.id, options: scaleOptions, ctx: me.ctx, chart: me }); scales[scale.id] = scale; scale.mergeTicksOptions(); // TODO(SB): I think we should be able to remove this custom case (options.scale) // and consider it as a regular scale part of the "scales"" map only! This would // make the logic easier and remove some useless? custom code. if (item.isDefault) { me.scale = scale; } }); Chart.scaleService.addScalesToLayout(this); }, buildOrUpdateControllers: function() { var me = this; var types = []; var newControllers = []; helpers.each(me.data.datasets, function(dataset, datasetIndex) { var meta = me.getDatasetMeta(datasetIndex); var type = dataset.type || me.config.type; if (meta.type && meta.type !== type) { me.destroyDatasetMeta(datasetIndex); meta = me.getDatasetMeta(datasetIndex); } meta.type = type; types.push(meta.type); if (meta.controller) { meta.controller.updateIndex(datasetIndex); } else { var ControllerClass = Chart.controllers[meta.type]; if (ControllerClass === undefined) { throw new Error('"' + meta.type + '" is not a chart type.'); } meta.controller = new ControllerClass(me, datasetIndex); newControllers.push(meta.controller); } }, me); return newControllers; }, /** * Reset the elements of all datasets * @private */ resetElements: function() { var me = this; helpers.each(me.data.datasets, function(dataset, datasetIndex) { me.getDatasetMeta(datasetIndex).controller.reset(); }, me); }, /** * Resets the chart back to it's state before the initial animation */ reset: function() { this.resetElements(); this.tooltip.initialize(); }, update: function(config) { var me = this; if (!config || typeof config !== 'object') { // backwards compatibility config = { duration: config, lazy: arguments[1] }; } updateConfig(me); if (plugins.notify(me, 'beforeUpdate') === false) { return; } // In case the entire data object changed me.tooltip._data = me.data; // Make sure dataset controllers are updated and new controllers are reset var newControllers = me.buildOrUpdateControllers(); // Make sure all dataset controllers have correct meta data counts helpers.each(me.data.datasets, function(dataset, datasetIndex) { me.getDatasetMeta(datasetIndex).controller.buildOrUpdateElements(); }, me); me.updateLayout(); // Can only reset the new controllers after the scales have been updated helpers.each(newControllers, function(controller) { controller.reset(); }); me.updateDatasets(); // Need to reset tooltip in case it is displayed with elements that are removed // after update. me.tooltip.initialize(); // Last active contains items that were previously in the tooltip. // When we reset the tooltip, we need to clear it me.lastActive = []; // Do this before render so that any plugins that need final scale updates can use it plugins.notify(me, 'afterUpdate'); if (me._bufferedRender) { me._bufferedRequest = { duration: config.duration, easing: config.easing, lazy: config.lazy }; } else { me.render(config); } }, /** * Updates the chart layout unless a plugin returns `false` to the `beforeLayout` * hook, in which case, plugins will not be called on `afterLayout`. * @private */ updateLayout: function() { var me = this; if (plugins.notify(me, 'beforeLayout') === false) { return; } Chart.layoutService.update(this, this.width, this.height); /** * Provided for backward compatibility, use `afterLayout` instead. * @method IPlugin#afterScaleUpdate * @deprecated since version 2.5.0 * @todo remove at version 3 * @private */ plugins.notify(me, 'afterScaleUpdate'); plugins.notify(me, 'afterLayout'); }, /** * Updates all datasets unless a plugin returns `false` to the `beforeDatasetsUpdate` * hook, in which case, plugins will not be called on `afterDatasetsUpdate`. * @private */ updateDatasets: function() { var me = this; if (plugins.notify(me, 'beforeDatasetsUpdate') === false) { return; } for (var i = 0, ilen = me.data.datasets.length; i < ilen; ++i) { me.updateDataset(i); } plugins.notify(me, 'afterDatasetsUpdate'); }, /** * Updates dataset at index unless a plugin returns `false` to the `beforeDatasetUpdate` * hook, in which case, plugins will not be called on `afterDatasetUpdate`. * @private */ updateDataset: function(index) { var me = this; var meta = me.getDatasetMeta(index); var args = { meta: meta, index: index }; if (plugins.notify(me, 'beforeDatasetUpdate', [args]) === false) { return; } meta.controller.update(); plugins.notify(me, 'afterDatasetUpdate', [args]); }, render: function(config) { var me = this; if (!config || typeof config !== 'object') { // backwards compatibility config = { duration: config, lazy: arguments[1] }; } var duration = config.duration; var lazy = config.lazy; if (plugins.notify(me, 'beforeRender') === false) { return; } var animationOptions = me.options.animation; var onComplete = function(animation) { plugins.notify(me, 'afterRender'); helpers.callback(animationOptions && animationOptions.onComplete, [animation], me); }; if (animationOptions && ((typeof duration !== 'undefined' && duration !== 0) || (typeof duration === 'undefined' && animationOptions.duration !== 0))) { var animation = new Chart.Animation({ numSteps: (duration || animationOptions.duration) / 16.66, // 60 fps easing: config.easing || animationOptions.easing, render: function(chart, animationObject) { var easingFunction = helpers.easing.effects[animationObject.easing]; var currentStep = animationObject.currentStep; var stepDecimal = currentStep / animationObject.numSteps; chart.draw(easingFunction(stepDecimal), stepDecimal, currentStep); }, onAnimationProgress: animationOptions.onProgress, onAnimationComplete: onComplete }); Chart.animationService.addAnimation(me, animation, duration, lazy); } else { me.draw(); // See https://github.com/chartjs/Chart.js/issues/3781 onComplete(new Chart.Animation({numSteps: 0, chart: me})); } return me; }, draw: function(easingValue) { var me = this; me.clear(); if (helpers.isNullOrUndef(easingValue)) { easingValue = 1; } me.transition(easingValue); if (plugins.notify(me, 'beforeDraw', [easingValue]) === false) { return; } // Draw all the scales helpers.each(me.boxes, function(box) { box.draw(me.chartArea); }, me); if (me.scale) { me.scale.draw(); } me.drawDatasets(easingValue); me._drawTooltip(easingValue); plugins.notify(me, 'afterDraw', [easingValue]); }, /** * @private */ transition: function(easingValue) { var me = this; for (var i = 0, ilen = (me.data.datasets || []).length; i < ilen; ++i) { if (me.isDatasetVisible(i)) { me.getDatasetMeta(i).controller.transition(easingValue); } } me.tooltip.transition(easingValue); }, /** * Draws all datasets unless a plugin returns `false` to the `beforeDatasetsDraw` * hook, in which case, plugins will not be called on `afterDatasetsDraw`. * @private */ drawDatasets: function(easingValue) { var me = this; if (plugins.notify(me, 'beforeDatasetsDraw', [easingValue]) === false) { return; } // Draw datasets reversed to support proper line stacking for (var i = (me.data.datasets || []).length - 1; i >= 0; --i) { if (me.isDatasetVisible(i)) { me.drawDataset(i, easingValue); } } plugins.notify(me, 'afterDatasetsDraw', [easingValue]); }, /** * Draws dataset at index unless a plugin returns `false` to the `beforeDatasetDraw` * hook, in which case, plugins will not be called on `afterDatasetDraw`. * @private */ drawDataset: function(index, easingValue) { var me = this; var meta = me.getDatasetMeta(index); var args = { meta: meta, index: index, easingValue: easingValue }; if (plugins.notify(me, 'beforeDatasetDraw', [args]) === false) { return; } meta.controller.draw(easingValue); plugins.notify(me, 'afterDatasetDraw', [args]); }, /** * Draws tooltip unless a plugin returns `false` to the `beforeTooltipDraw` * hook, in which case, plugins will not be called on `afterTooltipDraw`. * @private */ _drawTooltip: function(easingValue) { var me = this; var tooltip = me.tooltip; var args = { tooltip: tooltip, easingValue: easingValue }; if (plugins.notify(me, 'beforeTooltipDraw', [args]) === false) { return; } tooltip.draw(); plugins.notify(me, 'afterTooltipDraw', [args]); }, // Get the single element that was clicked on // @return : An object containing the dataset index and element index of the matching element. Also contains the rectangle that was draw getElementAtEvent: function(e) { return Interaction.modes.single(this, e); }, getElementsAtEvent: function(e) { return Interaction.modes.label(this, e, {intersect: true}); }, getElementsAtXAxis: function(e) { return Interaction.modes['x-axis'](this, e, {intersect: true}); }, getElementsAtEventForMode: function(e, mode, options) { var method = Interaction.modes[mode]; if (typeof method === 'function') { return method(this, e, options); } return []; }, getDatasetAtEvent: function(e) { return Interaction.modes.dataset(this, e, {intersect: true}); }, getDatasetMeta: function(datasetIndex) { var me = this; var dataset = me.data.datasets[datasetIndex]; if (!dataset._meta) { dataset._meta = {}; } var meta = dataset._meta[me.id]; if (!meta) { meta = dataset._meta[me.id] = { type: null, data: [], dataset: null, controller: null, hidden: null, // See isDatasetVisible() comment xAxisID: null, yAxisID: null }; } return meta; }, getVisibleDatasetCount: function() { var count = 0; for (var i = 0, ilen = this.data.datasets.length; i < ilen; ++i) { if (this.isDatasetVisible(i)) { count++; } } return count; }, isDatasetVisible: function(datasetIndex) { var meta = this.getDatasetMeta(datasetIndex); // meta.hidden is a per chart dataset hidden flag override with 3 states: if true or false, // the dataset.hidden value is ignored, else if null, the dataset hidden state is returned. return typeof meta.hidden === 'boolean' ? !meta.hidden : !this.data.datasets[datasetIndex].hidden; }, generateLegend: function() { return this.options.legendCallback(this); }, /** * @private */ destroyDatasetMeta: function(datasetIndex) { var id = this.id; var dataset = this.data.datasets[datasetIndex]; var meta = dataset._meta && dataset._meta[id]; if (meta) { meta.controller.destroy(); delete dataset._meta[id]; } }, destroy: function() { var me = this; var canvas = me.canvas; var i, ilen; me.stop(); // dataset controllers need to cleanup associated data for (i = 0, ilen = me.data.datasets.length; i < ilen; ++i) { me.destroyDatasetMeta(i); } if (canvas) { me.unbindEvents(); helpers.canvas.clear(me); platform.releaseContext(me.ctx); me.canvas = null; me.ctx = null; } plugins.notify(me, 'destroy'); delete Chart.instances[me.id]; }, toBase64Image: function() { return this.canvas.toDataURL.apply(this.canvas, arguments); }, initToolTip: function() { var me = this; me.tooltip = new Chart.Tooltip({ _chart: me, _chartInstance: me, // deprecated, backward compatibility _data: me.data, _options: me.options.tooltips }, me); }, /** * @private */ bindEvents: function() { var me = this; var listeners = me._listeners = {}; var listener = function() { me.eventHandler.apply(me, arguments); }; helpers.each(me.options.events, function(type) { platform.addEventListener(me, type, listener); listeners[type] = listener; }); // Elements used to detect size change should not be injected for non responsive charts. // See https://github.com/chartjs/Chart.js/issues/2210 if (me.options.responsive) { listener = function() { me.resize(); }; platform.addEventListener(me, 'resize', listener); listeners.resize = listener; } }, /** * @private */ unbindEvents: function() { var me = this; var listeners = me._listeners; if (!listeners) { return; } delete me._listeners; helpers.each(listeners, function(listener, type) { platform.removeEventListener(me, type, listener); }); }, updateHoverStyle: function(elements, mode, enabled) { var method = enabled ? 'setHoverStyle' : 'removeHoverStyle'; var element, i, ilen; for (i = 0, ilen = elements.length; i < ilen; ++i) { element = elements[i]; if (element) { this.getDatasetMeta(element._datasetIndex).controller[method](element); } } }, /** * @private */ eventHandler: function(e) { var me = this; var tooltip = me.tooltip; if (plugins.notify(me, 'beforeEvent', [e]) === false) { return; } // Buffer any update calls so that renders do not occur me._bufferedRender = true; me._bufferedRequest = null; var changed = me.handleEvent(e); changed |= tooltip && tooltip.handleEvent(e); plugins.notify(me, 'afterEvent', [e]); var bufferedRequest = me._bufferedRequest; if (bufferedRequest) { // If we have an update that was triggered, we need to do a normal render me.render(bufferedRequest); } else if (changed && !me.animating) { // If entering, leaving, or changing elements, animate the change via pivot me.stop(); // We only need to render at this point. Updating will cause scales to be // recomputed generating flicker & using more memory than necessary. me.render(me.options.hover.animationDuration, true); } me._bufferedRender = false; me._bufferedRequest = null; return me; }, /** * Handle an event * @private * @param {IEvent} event the event to handle * @return {Boolean} true if the chart needs to re-render */ handleEvent: function(e) { var me = this; var options = me.options || {}; var hoverOptions = options.hover; var changed = false; me.lastActive = me.lastActive || []; // Find Active Elements for hover and tooltips if (e.type === 'mouseout') { me.active = []; } else { me.active = me.getElementsAtEventForMode(e, hoverOptions.mode, hoverOptions); } // Invoke onHover hook // Need to call with native event here to not break backwards compatibility helpers.callback(options.onHover || options.hover.onHover, [e.native, me.active], me); if (e.type === 'mouseup' || e.type === 'click') { if (options.onClick) { // Use e.native here for backwards compatibility options.onClick.call(me, e.native, me.active); } } // Remove styling for last active (even if it may still be active) if (me.lastActive.length) { me.updateHoverStyle(me.lastActive, hoverOptions.mode, false); } // Built in hover styling if (me.active.length && hoverOptions.mode) { me.updateHoverStyle(me.active, hoverOptions.mode, true); } changed = !helpers.arrayEquals(me.active, me.lastActive); // Remember Last Actives me.lastActive = me.active; return changed; } }); /** * Provided for backward compatibility, use Chart instead. * @class Chart.Controller * @deprecated since version 2.6.0 * @todo remove at version 3 * @private */ Chart.Controller = Chart; }; },{"25":25,"28":28,"45":45,"48":48}],24:[function(require,module,exports){ 'use strict'; var helpers = require(45); module.exports = function(Chart) { var arrayEvents = ['push', 'pop', 'shift', 'splice', 'unshift']; /** * Hooks the array methods that add or remove values ('push', pop', 'shift', 'splice', * 'unshift') and notify the listener AFTER the array has been altered. Listeners are * called on the 'onData*' callbacks (e.g. onDataPush, etc.) with same arguments. */ function listenArrayEvents(array, listener) { if (array._chartjs) { array._chartjs.listeners.push(listener); return; } Object.defineProperty(array, '_chartjs', { configurable: true, enumerable: false, value: { listeners: [listener] } }); arrayEvents.forEach(function(key) { var method = 'onData' + key.charAt(0).toUpperCase() + key.slice(1); var base = array[key]; Object.defineProperty(array, key, { configurable: true, enumerable: false, value: function() { var args = Array.prototype.slice.call(arguments); var res = base.apply(this, args); helpers.each(array._chartjs.listeners, function(object) { if (typeof object[method] === 'function') { object[method].apply(object, args); } }); return res; } }); }); } /** * Removes the given array event listener and cleanup extra attached properties (such as * the _chartjs stub and overridden methods) if array doesn't have any more listeners. */ function unlistenArrayEvents(array, listener) { var stub = array._chartjs; if (!stub) { return; } var listeners = stub.listeners; var index = listeners.indexOf(listener); if (index !== -1) { listeners.splice(index, 1); } if (listeners.length > 0) { return; } arrayEvents.forEach(function(key) { delete array[key]; }); delete array._chartjs; } // Base class for all dataset controllers (line, bar, etc) Chart.DatasetController = function(chart, datasetIndex) { this.initialize(chart, datasetIndex); }; helpers.extend(Chart.DatasetController.prototype, { /** * Element type used to generate a meta dataset (e.g. Chart.element.Line). * @type {Chart.core.element} */ datasetElementType: null, /** * Element type used to generate a meta data (e.g. Chart.element.Point). * @type {Chart.core.element} */ dataElementType: null, initialize: function(chart, datasetIndex) { var me = this; me.chart = chart; me.index = datasetIndex; me.linkScales(); me.addElements(); }, updateIndex: function(datasetIndex) { this.index = datasetIndex; }, linkScales: function() { var me = this; var meta = me.getMeta(); var dataset = me.getDataset(); if (meta.xAxisID === null) { meta.xAxisID = dataset.xAxisID || me.chart.options.scales.xAxes[0].id; } if (meta.yAxisID === null) { meta.yAxisID = dataset.yAxisID || me.chart.options.scales.yAxes[0].id; } }, getDataset: function() { return this.chart.data.datasets[this.index]; }, getMeta: function() { return this.chart.getDatasetMeta(this.index); }, getScaleForId: function(scaleID) { return this.chart.scales[scaleID]; }, reset: function() { this.update(true); }, /** * @private */ destroy: function() { if (this._data) { unlistenArrayEvents(this._data, this); } }, createMetaDataset: function() { var me = this; var type = me.datasetElementType; return type && new type({ _chart: me.chart, _datasetIndex: me.index }); }, createMetaData: function(index) { var me = this; var type = me.dataElementType; return type && new type({ _chart: me.chart, _datasetIndex: me.index, _index: index }); }, addElements: function() { var me = this; var meta = me.getMeta(); var data = me.getDataset().data || []; var metaData = meta.data; var i, ilen; for (i = 0, ilen = data.length; i < ilen; ++i) { metaData[i] = metaData[i] || me.createMetaData(i); } meta.dataset = meta.dataset || me.createMetaDataset(); }, addElementAndReset: function(index) { var element = this.createMetaData(index); this.getMeta().data.splice(index, 0, element); this.updateElement(element, index, true); }, buildOrUpdateElements: function() { var me = this; var dataset = me.getDataset(); var data = dataset.data || (dataset.data = []); // In order to correctly handle data addition/deletion animation (an thus simulate // real-time charts), we need to monitor these data modifications and synchronize // the internal meta data accordingly. if (me._data !== data) { if (me._data) { // This case happens when the user replaced the data array instance. unlistenArrayEvents(me._data, me); } listenArrayEvents(data, me); me._data = data; } // Re-sync meta data in case the user replaced the data array or if we missed // any updates and so make sure that we handle number of datapoints changing. me.resyncElements(); }, update: helpers.noop, transition: function(easingValue) { var meta = this.getMeta(); var elements = meta.data || []; var ilen = elements.length; var i = 0; for (; i < ilen; ++i) { elements[i].transition(easingValue); } if (meta.dataset) { meta.dataset.transition(easingValue); } }, draw: function() { var meta = this.getMeta(); var elements = meta.data || []; var ilen = elements.length; var i = 0; if (meta.dataset) { meta.dataset.draw(); } for (; i < ilen; ++i) { elements[i].draw(); } }, removeHoverStyle: function(element, elementOpts) { var dataset = this.chart.data.datasets[element._datasetIndex]; var index = element._index; var custom = element.custom || {}; var valueOrDefault = helpers.valueAtIndexOrDefault; var model = element._model; model.backgroundColor = custom.backgroundColor ? custom.backgroundColor : valueOrDefault(dataset.backgroundColor, index, elementOpts.backgroundColor); model.borderColor = custom.borderColor ? custom.borderColor : valueOrDefault(dataset.borderColor, index, elementOpts.borderColor); model.borderWidth = custom.borderWidth ? custom.borderWidth : valueOrDefault(dataset.borderWidth, index, elementOpts.borderWidth); }, setHoverStyle: function(element) { var dataset = this.chart.data.datasets[element._datasetIndex]; var index = element._index; var custom = element.custom || {}; var valueOrDefault = helpers.valueAtIndexOrDefault; var getHoverColor = helpers.getHoverColor; var model = element._model; model.backgroundColor = custom.hoverBackgroundColor ? custom.hoverBackgroundColor : valueOrDefault(dataset.hoverBackgroundColor, index, getHoverColor(model.backgroundColor)); model.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : valueOrDefault(dataset.hoverBorderColor, index, getHoverColor(model.borderColor)); model.borderWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : valueOrDefault(dataset.hoverBorderWidth, index, model.borderWidth); }, /** * @private */ resyncElements: function() { var me = this; var meta = me.getMeta(); var data = me.getDataset().data; var numMeta = meta.data.length; var numData = data.length; if (numData < numMeta) { meta.data.splice(numData, numMeta - numData); } else if (numData > numMeta) { me.insertElements(numMeta, numData - numMeta); } }, /** * @private */ insertElements: function(start, count) { for (var i = 0; i < count; ++i) { this.addElementAndReset(start + i); } }, /** * @private */ onDataPush: function() { this.insertElements(this.getDataset().data.length - 1, arguments.length); }, /** * @private */ onDataPop: function() { this.getMeta().data.pop(); }, /** * @private */ onDataShift: function() { this.getMeta().data.shift(); }, /** * @private */ onDataSplice: function(start, count) { this.getMeta().data.splice(start, count); this.insertElements(start, arguments.length - 2); }, /** * @private */ onDataUnshift: function() { this.insertElements(0, arguments.length); } }); Chart.DatasetController.extend = helpers.inherits; }; },{"45":45}],25:[function(require,module,exports){ 'use strict'; var helpers = require(45); module.exports = { /** * @private */ _set: function(scope, values) { return helpers.merge(this[scope] || (this[scope] = {}), values); } }; },{"45":45}],26:[function(require,module,exports){ 'use strict'; var color = require(3); var helpers = require(45); function interpolate(start, view, model, ease) { var keys = Object.keys(model); var i, ilen, key, actual, origin, target, type, c0, c1; for (i = 0, ilen = keys.length; i < ilen; ++i) { key = keys[i]; target = model[key]; // if a value is added to the model after pivot() has been called, the view // doesn't contain it, so let's initialize the view to the target value. if (!view.hasOwnProperty(key)) { view[key] = target; } actual = view[key]; if (actual === target || key[0] === '_') { continue; } if (!start.hasOwnProperty(key)) { start[key] = actual; } origin = start[key]; type = typeof target; if (type === typeof origin) { if (type === 'string') { c0 = color(origin); if (c0.valid) { c1 = color(target); if (c1.valid) { view[key] = c1.mix(c0, ease).rgbString(); continue; } } } else if (type === 'number' && isFinite(origin) && isFinite(target)) { view[key] = origin + (target - origin) * ease; continue; } } view[key] = target; } } var Element = function(configuration) { helpers.extend(this, configuration); this.initialize.apply(this, arguments); }; helpers.extend(Element.prototype, { initialize: function() { this.hidden = false; }, pivot: function() { var me = this; if (!me._view) { me._view = helpers.clone(me._model); } me._start = {}; return me; }, transition: function(ease) { var me = this; var model = me._model; var start = me._start; var view = me._view; // No animation -> No Transition if (!model || ease === 1) { me._view = model; me._start = null; return me; } if (!view) { view = me._view = {}; } if (!start) { start = me._start = {}; } interpolate(start, view, model, ease); return me; }, tooltipPosition: function() { return { x: this._model.x, y: this._model.y }; }, hasValue: function() { return helpers.isNumber(this._model.x) && helpers.isNumber(this._model.y); } }); Element.extend = helpers.inherits; module.exports = Element; },{"3":3,"45":45}],27:[function(require,module,exports){ /* global window: false */ /* global document: false */ 'use strict'; var color = require(3); var defaults = require(25); var helpers = require(45); module.exports = function(Chart) { // -- Basic js utility methods helpers.configMerge = function(/* objects ... */) { return helpers.merge(helpers.clone(arguments[0]), [].slice.call(arguments, 1), { merger: function(key, target, source, options) { var tval = target[key] || {}; var sval = source[key]; if (key === 'scales') { // scale config merging is complex. Add our own function here for that target[key] = helpers.scaleMerge(tval, sval); } else if (key === 'scale') { // used in polar area & radar charts since there is only one scale target[key] = helpers.merge(tval, [Chart.scaleService.getScaleDefaults(sval.type), sval]); } else { helpers._merger(key, target, source, options); } } }); }; helpers.scaleMerge = function(/* objects ... */) { return helpers.merge(helpers.clone(arguments[0]), [].slice.call(arguments, 1), { merger: function(key, target, source, options) { if (key === 'xAxes' || key === 'yAxes') { var slen = source[key].length; var i, type, scale; if (!target[key]) { target[key] = []; } for (i = 0; i < slen; ++i) { scale = source[key][i]; type = helpers.valueOrDefault(scale.type, key === 'xAxes' ? 'category' : 'linear'); if (i >= target[key].length) { target[key].push({}); } if (!target[key][i].type || (scale.type && scale.type !== target[key][i].type)) { // new/untyped scale or type changed: let's apply the new defaults // then merge source scale to correctly overwrite the defaults. helpers.merge(target[key][i], [Chart.scaleService.getScaleDefaults(type), scale]); } else { // scales type are the same helpers.merge(target[key][i], scale); } } } else { helpers._merger(key, target, source, options); } } }); }; helpers.where = function(collection, filterCallback) { if (helpers.isArray(collection) && Array.prototype.filter) { return collection.filter(filterCallback); } var filtered = []; helpers.each(collection, function(item) { if (filterCallback(item)) { filtered.push(item); } }); return filtered; }; helpers.findIndex = Array.prototype.findIndex ? function(array, callback, scope) { return array.findIndex(callback, scope); } : function(array, callback, scope) { scope = scope === undefined ? array : scope; for (var i = 0, ilen = array.length; i < ilen; ++i) { if (callback.call(scope, array[i], i, array)) { return i; } } return -1; }; helpers.findNextWhere = function(arrayToSearch, filterCallback, startIndex) { // Default to start of the array if (helpers.isNullOrUndef(startIndex)) { startIndex = -1; } for (var i = startIndex + 1; i < arrayToSearch.length; i++) { var currentItem = arrayToSearch[i]; if (filterCallback(currentItem)) { return currentItem; } } }; helpers.findPreviousWhere = function(arrayToSearch, filterCallback, startIndex) { // Default to end of the array if (helpers.isNullOrUndef(startIndex)) { startIndex = arrayToSearch.length; } for (var i = startIndex - 1; i >= 0; i--) { var currentItem = arrayToSearch[i]; if (filterCallback(currentItem)) { return currentItem; } } }; // -- Math methods helpers.isNumber = function(n) { return !isNaN(parseFloat(n)) && isFinite(n); }; helpers.almostEquals = function(x, y, epsilon) { return Math.abs(x - y) < epsilon; }; helpers.almostWhole = function(x, epsilon) { var rounded = Math.round(x); return (((rounded - epsilon) < x) && ((rounded + epsilon) > x)); }; helpers.max = function(array) { return array.reduce(function(max, value) { if (!isNaN(value)) { return Math.max(max, value); } return max; }, Number.NEGATIVE_INFINITY); }; helpers.min = function(array) { return array.reduce(function(min, value) { if (!isNaN(value)) { return Math.min(min, value); } return min; }, Number.POSITIVE_INFINITY); }; helpers.sign = Math.sign ? function(x) { return Math.sign(x); } : function(x) { x = +x; // convert to a number if (x === 0 || isNaN(x)) { return x; } return x > 0 ? 1 : -1; }; helpers.log10 = Math.log10 ? function(x) { return Math.log10(x); } : function(x) { return Math.log(x) / Math.LN10; }; helpers.toRadians = function(degrees) { return degrees * (Math.PI / 180); }; helpers.toDegrees = function(radians) { return radians * (180 / Math.PI); }; // Gets the angle from vertical upright to the point about a centre. helpers.getAngleFromPoint = function(centrePoint, anglePoint) { var distanceFromXCenter = anglePoint.x - centrePoint.x; var distanceFromYCenter = anglePoint.y - centrePoint.y; var radialDistanceFromCenter = Math.sqrt(distanceFromXCenter * distanceFromXCenter + distanceFromYCenter * distanceFromYCenter); var angle = Math.atan2(distanceFromYCenter, distanceFromXCenter); if (angle < (-0.5 * Math.PI)) { angle += 2.0 * Math.PI; // make sure the returned angle is in the range of (-PI/2, 3PI/2] } return { angle: angle, distance: radialDistanceFromCenter }; }; helpers.distanceBetweenPoints = function(pt1, pt2) { return Math.sqrt(Math.pow(pt2.x - pt1.x, 2) + Math.pow(pt2.y - pt1.y, 2)); }; helpers.aliasPixel = function(pixelWidth) { return (pixelWidth % 2 === 0) ? 0 : 0.5; }; helpers.splineCurve = function(firstPoint, middlePoint, afterPoint, t) { // Props to Rob Spencer at scaled innovation for his post on splining between points // http://scaledinnovation.com/analytics/splines/aboutSplines.html // This function must also respect "skipped" points var previous = firstPoint.skip ? middlePoint : firstPoint; var current = middlePoint; var next = afterPoint.skip ? middlePoint : afterPoint; var d01 = Math.sqrt(Math.pow(current.x - previous.x, 2) + Math.pow(current.y - previous.y, 2)); var d12 = Math.sqrt(Math.pow(next.x - current.x, 2) + Math.pow(next.y - current.y, 2)); var s01 = d01 / (d01 + d12); var s12 = d12 / (d01 + d12); // If all points are the same, s01 & s02 will be inf s01 = isNaN(s01) ? 0 : s01; s12 = isNaN(s12) ? 0 : s12; var fa = t * s01; // scaling factor for triangle Ta var fb = t * s12; return { previous: { x: current.x - fa * (next.x - previous.x), y: current.y - fa * (next.y - previous.y) }, next: { x: current.x + fb * (next.x - previous.x), y: current.y + fb * (next.y - previous.y) } }; }; helpers.EPSILON = Number.EPSILON || 1e-14; helpers.splineCurveMonotone = function(points) { // This function calculates Bézier control points in a similar way than |splineCurve|, // but preserves monotonicity of the provided data and ensures no local extremums are added // between the dataset discrete points due to the interpolation. // See : https://en.wikipedia.org/wiki/Monotone_cubic_interpolation var pointsWithTangents = (points || []).map(function(point) { return { model: point._model, deltaK: 0, mK: 0 }; }); // Calculate slopes (deltaK) and initialize tangents (mK) var pointsLen = pointsWithTangents.length; var i, pointBefore, pointCurrent, pointAfter; for (i = 0; i < pointsLen; ++i) { pointCurrent = pointsWithTangents[i]; if (pointCurrent.model.skip) { continue; } pointBefore = i > 0 ? pointsWithTangents[i - 1] : null; pointAfter = i < pointsLen - 1 ? pointsWithTangents[i + 1] : null; if (pointAfter && !pointAfter.model.skip) { var slopeDeltaX = (pointAfter.model.x - pointCurrent.model.x); // In the case of two points that appear at the same x pixel, slopeDeltaX is 0 pointCurrent.deltaK = slopeDeltaX !== 0 ? (pointAfter.model.y - pointCurrent.model.y) / slopeDeltaX : 0; } if (!pointBefore || pointBefore.model.skip) { pointCurrent.mK = pointCurrent.deltaK; } else if (!pointAfter || pointAfter.model.skip) { pointCurrent.mK = pointBefore.deltaK; } else if (this.sign(pointBefore.deltaK) !== this.sign(pointCurrent.deltaK)) { pointCurrent.mK = 0; } else { pointCurrent.mK = (pointBefore.deltaK + pointCurrent.deltaK) / 2; } } // Adjust tangents to ensure monotonic properties var alphaK, betaK, tauK, squaredMagnitude; for (i = 0; i < pointsLen - 1; ++i) { pointCurrent = pointsWithTangents[i]; pointAfter = pointsWithTangents[i + 1]; if (pointCurrent.model.skip || pointAfter.model.skip) { continue; } if (helpers.almostEquals(pointCurrent.deltaK, 0, this.EPSILON)) { pointCurrent.mK = pointAfter.mK = 0; continue; } alphaK = pointCurrent.mK / pointCurrent.deltaK; betaK = pointAfter.mK / pointCurrent.deltaK; squaredMagnitude = Math.pow(alphaK, 2) + Math.pow(betaK, 2); if (squaredMagnitude <= 9) { continue; } tauK = 3 / Math.sqrt(squaredMagnitude); pointCurrent.mK = alphaK * tauK * pointCurrent.deltaK; pointAfter.mK = betaK * tauK * pointCurrent.deltaK; } // Compute control points var deltaX; for (i = 0; i < pointsLen; ++i) { pointCurrent = pointsWithTangents[i]; if (pointCurrent.model.skip) { continue; } pointBefore = i > 0 ? pointsWithTangents[i - 1] : null; pointAfter = i < pointsLen - 1 ? pointsWithTangents[i + 1] : null; if (pointBefore && !pointBefore.model.skip) { deltaX = (pointCurrent.model.x - pointBefore.model.x) / 3; pointCurrent.model.controlPointPreviousX = pointCurrent.model.x - deltaX; pointCurrent.model.controlPointPreviousY = pointCurrent.model.y - deltaX * pointCurrent.mK; } if (pointAfter && !pointAfter.model.skip) { deltaX = (pointAfter.model.x - pointCurrent.model.x) / 3; pointCurrent.model.controlPointNextX = pointCurrent.model.x + deltaX; pointCurrent.model.controlPointNextY = pointCurrent.model.y + deltaX * pointCurrent.mK; } } }; helpers.nextItem = function(collection, index, loop) { if (loop) { return index >= collection.length - 1 ? collection[0] : collection[index + 1]; } return index >= collection.length - 1 ? collection[collection.length - 1] : collection[index + 1]; }; helpers.previousItem = function(collection, index, loop) { if (loop) { return index <= 0 ? collection[collection.length - 1] : collection[index - 1]; } return index <= 0 ? collection[0] : collection[index - 1]; }; // Implementation of the nice number algorithm used in determining where axis labels will go helpers.niceNum = function(range, round) { var exponent = Math.floor(helpers.log10(range)); var fraction = range / Math.pow(10, exponent); var niceFraction; if (round) { if (fraction < 1.5) { niceFraction = 1; } else if (fraction < 3) { niceFraction = 2; } else if (fraction < 7) { niceFraction = 5; } else { niceFraction = 10; } } else if (fraction <= 1.0) { niceFraction = 1; } else if (fraction <= 2) { niceFraction = 2; } else if (fraction <= 5) { niceFraction = 5; } else { niceFraction = 10; } return niceFraction * Math.pow(10, exponent); }; // Request animation polyfill - http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/ helpers.requestAnimFrame = (function() { if (typeof window === 'undefined') { return function(callback) { callback(); }; } return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function(callback) { return window.setTimeout(callback, 1000 / 60); }; }()); // -- DOM methods helpers.getRelativePosition = function(evt, chart) { var mouseX, mouseY; var e = evt.originalEvent || evt; var canvas = evt.currentTarget || evt.srcElement; var boundingRect = canvas.getBoundingClientRect(); var touches = e.touches; if (touches && touches.length > 0) { mouseX = touches[0].clientX; mouseY = touches[0].clientY; } else { mouseX = e.clientX; mouseY = e.clientY; } // Scale mouse coordinates into canvas coordinates // by following the pattern laid out by 'jerryj' in the comments of // http://www.html5canvastutorials.com/advanced/html5-canvas-mouse-coordinates/ var paddingLeft = parseFloat(helpers.getStyle(canvas, 'padding-left')); var paddingTop = parseFloat(helpers.getStyle(canvas, 'padding-top')); var paddingRight = parseFloat(helpers.getStyle(canvas, 'padding-right')); var paddingBottom = parseFloat(helpers.getStyle(canvas, 'padding-bottom')); var width = boundingRect.right - boundingRect.left - paddingLeft - paddingRight; var height = boundingRect.bottom - boundingRect.top - paddingTop - paddingBottom; // We divide by the current device pixel ratio, because the canvas is scaled up by that amount in each direction. However // the backend model is in unscaled coordinates. Since we are going to deal with our model coordinates, we go back here mouseX = Math.round((mouseX - boundingRect.left - paddingLeft) / (width) * canvas.width / chart.currentDevicePixelRatio); mouseY = Math.round((mouseY - boundingRect.top - paddingTop) / (height) * canvas.height / chart.currentDevicePixelRatio); return { x: mouseX, y: mouseY }; }; // Private helper function to convert max-width/max-height values that may be percentages into a number function parseMaxStyle(styleValue, node, parentProperty) { var valueInPixels; if (typeof styleValue === 'string') { valueInPixels = parseInt(styleValue, 10); if (styleValue.indexOf('%') !== -1) { // percentage * size in dimension valueInPixels = valueInPixels / 100 * node.parentNode[parentProperty]; } } else { valueInPixels = styleValue; } return valueInPixels; } /** * Returns if the given value contains an effective constraint. * @private */ function isConstrainedValue(value) { return value !== undefined && value !== null && value !== 'none'; } // Private helper to get a constraint dimension // @param domNode : the node to check the constraint on // @param maxStyle : the style that defines the maximum for the direction we are using (maxWidth / maxHeight) // @param percentageProperty : property of parent to use when calculating width as a percentage // @see http://www.nathanaeljones.com/blog/2013/reading-max-width-cross-browser function getConstraintDimension(domNode, maxStyle, percentageProperty) { var view = document.defaultView; var parentNode = domNode.parentNode; var constrainedNode = view.getComputedStyle(domNode)[maxStyle]; var constrainedContainer = view.getComputedStyle(parentNode)[maxStyle]; var hasCNode = isConstrainedValue(constrainedNode); var hasCContainer = isConstrainedValue(constrainedContainer); var infinity = Number.POSITIVE_INFINITY; if (hasCNode || hasCContainer) { return Math.min( hasCNode ? parseMaxStyle(constrainedNode, domNode, percentageProperty) : infinity, hasCContainer ? parseMaxStyle(constrainedContainer, parentNode, percentageProperty) : infinity); } return 'none'; } // returns Number or undefined if no constraint helpers.getConstraintWidth = function(domNode) { return getConstraintDimension(domNode, 'max-width', 'clientWidth'); }; // returns Number or undefined if no constraint helpers.getConstraintHeight = function(domNode) { return getConstraintDimension(domNode, 'max-height', 'clientHeight'); }; helpers.getMaximumWidth = function(domNode) { var container = domNode.parentNode; if (!container) { return domNode.clientWidth; } var paddingLeft = parseInt(helpers.getStyle(container, 'padding-left'), 10); var paddingRight = parseInt(helpers.getStyle(container, 'padding-right'), 10); var w = container.clientWidth - paddingLeft - paddingRight; var cw = helpers.getConstraintWidth(domNode); return isNaN(cw) ? w : Math.min(w, cw); }; helpers.getMaximumHeight = function(domNode) { var container = domNode.parentNode; if (!container) { return domNode.clientHeight; } var paddingTop = parseInt(helpers.getStyle(container, 'padding-top'), 10); var paddingBottom = parseInt(helpers.getStyle(container, 'padding-bottom'), 10); var h = container.clientHeight - paddingTop - paddingBottom; var ch = helpers.getConstraintHeight(domNode); return isNaN(ch) ? h : Math.min(h, ch); }; helpers.getStyle = function(el, property) { return el.currentStyle ? el.currentStyle[property] : document.defaultView.getComputedStyle(el, null).getPropertyValue(property); }; helpers.retinaScale = function(chart, forceRatio) { var pixelRatio = chart.currentDevicePixelRatio = forceRatio || window.devicePixelRatio || 1; if (pixelRatio === 1) { return; } var canvas = chart.canvas; var height = chart.height; var width = chart.width; canvas.height = height * pixelRatio; canvas.width = width * pixelRatio; chart.ctx.scale(pixelRatio, pixelRatio); // If no style has been set on the canvas, the render size is used as display size, // making the chart visually bigger, so let's enforce it to the "correct" values. // See https://github.com/chartjs/Chart.js/issues/3575 canvas.style.height = height + 'px'; canvas.style.width = width + 'px'; }; // -- Canvas methods helpers.fontString = function(pixelSize, fontStyle, fontFamily) { return fontStyle + ' ' + pixelSize + 'px ' + fontFamily; }; helpers.longestText = function(ctx, font, arrayOfThings, cache) { cache = cache || {}; var data = cache.data = cache.data || {}; var gc = cache.garbageCollect = cache.garbageCollect || []; if (cache.font !== font) { data = cache.data = {}; gc = cache.garbageCollect = []; cache.font = font; } ctx.font = font; var longest = 0; helpers.each(arrayOfThings, function(thing) { // Undefined strings and arrays should not be measured if (thing !== undefined && thing !== null && helpers.isArray(thing) !== true) { longest = helpers.measureText(ctx, data, gc, longest, thing); } else if (helpers.isArray(thing)) { // if it is an array lets measure each element // to do maybe simplify this function a bit so we can do this more recursively? helpers.each(thing, function(nestedThing) { // Undefined strings and arrays should not be measured if (nestedThing !== undefined && nestedThing !== null && !helpers.isArray(nestedThing)) { longest = helpers.measureText(ctx, data, gc, longest, nestedThing); } }); } }); var gcLen = gc.length / 2; if (gcLen > arrayOfThings.length) { for (var i = 0; i < gcLen; i++) { delete data[gc[i]]; } gc.splice(0, gcLen); } return longest; }; helpers.measureText = function(ctx, data, gc, longest, string) { var textWidth = data[string]; if (!textWidth) { textWidth = data[string] = ctx.measureText(string).width; gc.push(string); } if (textWidth > longest) { longest = textWidth; } return longest; }; helpers.numberOfLabelLines = function(arrayOfThings) { var numberOfLines = 1; helpers.each(arrayOfThings, function(thing) { if (helpers.isArray(thing)) { if (thing.length > numberOfLines) { numberOfLines = thing.length; } } }); return numberOfLines; }; helpers.color = !color ? function(value) { console.error('Color.js not found!'); return value; } : function(value) { /* global CanvasGradient */ if (value instanceof CanvasGradient) { value = defaults.global.defaultColor; } return color(value); }; helpers.getHoverColor = function(colorValue) { /* global CanvasPattern */ return (colorValue instanceof CanvasPattern) ? colorValue : helpers.color(colorValue).saturate(0.5).darken(0.1).rgbString(); }; }; },{"25":25,"3":3,"45":45}],28:[function(require,module,exports){ 'use strict'; var helpers = require(45); /** * Helper function to get relative position for an event * @param {Event|IEvent} event - The event to get the position for * @param {Chart} chart - The chart * @returns {Point} the event position */ function getRelativePosition(e, chart) { if (e.native) { return { x: e.x, y: e.y }; } return helpers.getRelativePosition(e, chart); } /** * Helper function to traverse all of the visible elements in the chart * @param chart {chart} the chart * @param handler {Function} the callback to execute for each visible item */ function parseVisibleItems(chart, handler) { var datasets = chart.data.datasets; var meta, i, j, ilen, jlen; for (i = 0, ilen = datasets.length; i < ilen; ++i) { if (!chart.isDatasetVisible(i)) { continue; } meta = chart.getDatasetMeta(i); for (j = 0, jlen = meta.data.length; j < jlen; ++j) { var element = meta.data[j]; if (!element._view.skip) { handler(element); } } } } /** * Helper function to get the items that intersect the event position * @param items {ChartElement[]} elements to filter * @param position {Point} the point to be nearest to * @return {ChartElement[]} the nearest items */ function getIntersectItems(chart, position) { var elements = []; parseVisibleItems(chart, function(element) { if (element.inRange(position.x, position.y)) { elements.push(element); } }); return elements; } /** * Helper function to get the items nearest to the event position considering all visible items in teh chart * @param chart {Chart} the chart to look at elements from * @param position {Point} the point to be nearest to * @param intersect {Boolean} if true, only consider items that intersect the position * @param distanceMetric {Function} function to provide the distance between points * @return {ChartElement[]} the nearest items */ function getNearestItems(chart, position, intersect, distanceMetric) { var minDistance = Number.POSITIVE_INFINITY; var nearestItems = []; parseVisibleItems(chart, function(element) { if (intersect && !element.inRange(position.x, position.y)) { return; } var center = element.getCenterPoint(); var distance = distanceMetric(position, center); if (distance < minDistance) { nearestItems = [element]; minDistance = distance; } else if (distance === minDistance) { // Can have multiple items at the same distance in which case we sort by size nearestItems.push(element); } }); return nearestItems; } /** * Get a distance metric function for two points based on the * axis mode setting * @param {String} axis the axis mode. x|y|xy */ function getDistanceMetricForAxis(axis) { var useX = axis.indexOf('x') !== -1; var useY = axis.indexOf('y') !== -1; return function(pt1, pt2) { var deltaX = useX ? Math.abs(pt1.x - pt2.x) : 0; var deltaY = useY ? Math.abs(pt1.y - pt2.y) : 0; return Math.sqrt(Math.pow(deltaX, 2) + Math.pow(deltaY, 2)); }; } function indexMode(chart, e, options) { var position = getRelativePosition(e, chart); // Default axis for index mode is 'x' to match old behaviour options.axis = options.axis || 'x'; var distanceMetric = getDistanceMetricForAxis(options.axis); var items = options.intersect ? getIntersectItems(chart, position) : getNearestItems(chart, position, false, distanceMetric); var elements = []; if (!items.length) { return []; } chart.data.datasets.forEach(function(dataset, datasetIndex) { if (chart.isDatasetVisible(datasetIndex)) { var meta = chart.getDatasetMeta(datasetIndex); var element = meta.data[items[0]._index]; // don't count items that are skipped (null data) if (element && !element._view.skip) { elements.push(element); } } }); return elements; } /** * @interface IInteractionOptions */ /** * If true, only consider items that intersect the point * @name IInterfaceOptions#boolean * @type Boolean */ /** * Contains interaction related functions * @namespace Chart.Interaction */ module.exports = { // Helper function for different modes modes: { single: function(chart, e) { var position = getRelativePosition(e, chart); var elements = []; parseVisibleItems(chart, function(element) { if (element.inRange(position.x, position.y)) { elements.push(element); return elements; } }); return elements.slice(0, 1); }, /** * @function Chart.Interaction.modes.label * @deprecated since version 2.4.0 * @todo remove at version 3 * @private */ label: indexMode, /** * Returns items at the same index. If the options.intersect parameter is true, we only return items if we intersect something * If the options.intersect mode is false, we find the nearest item and return the items at the same index as that item * @function Chart.Interaction.modes.index * @since v2.4.0 * @param chart {chart} the chart we are returning items from * @param e {Event} the event we are find things at * @param options {IInteractionOptions} options to use during interaction * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned */ index: indexMode, /** * Returns items in the same dataset. If the options.intersect parameter is true, we only return items if we intersect something * If the options.intersect is false, we find the nearest item and return the items in that dataset * @function Chart.Interaction.modes.dataset * @param chart {chart} the chart we are returning items from * @param e {Event} the event we are find things at * @param options {IInteractionOptions} options to use during interaction * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned */ dataset: function(chart, e, options) { var position = getRelativePosition(e, chart); options.axis = options.axis || 'xy'; var distanceMetric = getDistanceMetricForAxis(options.axis); var items = options.intersect ? getIntersectItems(chart, position) : getNearestItems(chart, position, false, distanceMetric); if (items.length > 0) { items = chart.getDatasetMeta(items[0]._datasetIndex).data; } return items; }, /** * @function Chart.Interaction.modes.x-axis * @deprecated since version 2.4.0. Use index mode and intersect == true * @todo remove at version 3 * @private */ 'x-axis': function(chart, e) { return indexMode(chart, e, {intersect: false}); }, /** * Point mode returns all elements that hit test based on the event position * of the event * @function Chart.Interaction.modes.intersect * @param chart {chart} the chart we are returning items from * @param e {Event} the event we are find things at * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned */ point: function(chart, e) { var position = getRelativePosition(e, chart); return getIntersectItems(chart, position); }, /** * nearest mode returns the element closest to the point * @function Chart.Interaction.modes.intersect * @param chart {chart} the chart we are returning items from * @param e {Event} the event we are find things at * @param options {IInteractionOptions} options to use * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned */ nearest: function(chart, e, options) { var position = getRelativePosition(e, chart); options.axis = options.axis || 'xy'; var distanceMetric = getDistanceMetricForAxis(options.axis); var nearestItems = getNearestItems(chart, position, options.intersect, distanceMetric); // We have multiple items at the same distance from the event. Now sort by smallest if (nearestItems.length > 1) { nearestItems.sort(function(a, b) { var sizeA = a.getArea(); var sizeB = b.getArea(); var ret = sizeA - sizeB; if (ret === 0) { // if equal sort by dataset index ret = a._datasetIndex - b._datasetIndex; } return ret; }); } // Return only 1 item return nearestItems.slice(0, 1); }, /** * x mode returns the elements that hit-test at the current x coordinate * @function Chart.Interaction.modes.x * @param chart {chart} the chart we are returning items from * @param e {Event} the event we are find things at * @param options {IInteractionOptions} options to use * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned */ x: function(chart, e, options) { var position = getRelativePosition(e, chart); var items = []; var intersectsItem = false; parseVisibleItems(chart, function(element) { if (element.inXRange(position.x)) { items.push(element); } if (element.inRange(position.x, position.y)) { intersectsItem = true; } }); // If we want to trigger on an intersect and we don't have any items // that intersect the position, return nothing if (options.intersect && !intersectsItem) { items = []; } return items; }, /** * y mode returns the elements that hit-test at the current y coordinate * @function Chart.Interaction.modes.y * @param chart {chart} the chart we are returning items from * @param e {Event} the event we are find things at * @param options {IInteractionOptions} options to use * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned */ y: function(chart, e, options) { var position = getRelativePosition(e, chart); var items = []; var intersectsItem = false; parseVisibleItems(chart, function(element) { if (element.inYRange(position.y)) { items.push(element); } if (element.inRange(position.x, position.y)) { intersectsItem = true; } }); // If we want to trigger on an intersect and we don't have any items // that intersect the position, return nothing if (options.intersect && !intersectsItem) { items = []; } return items; } } }; },{"45":45}],29:[function(require,module,exports){ 'use strict'; var defaults = require(25); defaults._set('global', { responsive: true, responsiveAnimationDuration: 0, maintainAspectRatio: true, events: ['mousemove', 'mouseout', 'click', 'touchstart', 'touchmove'], hover: { onHover: null, mode: 'nearest', intersect: true, animationDuration: 400 }, onClick: null, defaultColor: 'rgba(0,0,0,0.1)', defaultFontColor: '#666', defaultFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif", defaultFontSize: 12, defaultFontStyle: 'normal', showLines: true, // Element defaults defined in element extensions elements: {}, // Layout options such as padding layout: { padding: { top: 0, right: 0, bottom: 0, left: 0 } } }); module.exports = function() { // Occupy the global variable of Chart, and create a simple base class var Chart = function(item, config) { this.construct(item, config); return this; }; Chart.Chart = Chart; return Chart; }; },{"25":25}],30:[function(require,module,exports){ 'use strict'; var helpers = require(45); module.exports = function(Chart) { function filterByPosition(array, position) { return helpers.where(array, function(v) { return v.position === position; }); } function sortByWeight(array, reverse) { array.forEach(function(v, i) { v._tmpIndex_ = i; return v; }); array.sort(function(a, b) { var v0 = reverse ? b : a; var v1 = reverse ? a : b; return v0.weight === v1.weight ? v0._tmpIndex_ - v1._tmpIndex_ : v0.weight - v1.weight; }); array.forEach(function(v) { delete v._tmpIndex_; }); } /** * @interface ILayoutItem * @prop {String} position - The position of the item in the chart layout. Possible values are * 'left', 'top', 'right', 'bottom', and 'chartArea' * @prop {Number} weight - The weight used to sort the item. Higher weights are further away from the chart area * @prop {Boolean} fullWidth - if true, and the item is horizontal, then push vertical boxes down * @prop {Function} isHorizontal - returns true if the layout item is horizontal (ie. top or bottom) * @prop {Function} update - Takes two parameters: width and height. Returns size of item * @prop {Function} getPadding - Returns an object with padding on the edges * @prop {Number} width - Width of item. Must be valid after update() * @prop {Number} height - Height of item. Must be valid after update() * @prop {Number} left - Left edge of the item. Set by layout system and cannot be used in update * @prop {Number} top - Top edge of the item. Set by layout system and cannot be used in update * @prop {Number} right - Right edge of the item. Set by layout system and cannot be used in update * @prop {Number} bottom - Bottom edge of the item. Set by layout system and cannot be used in update */ // The layout service is very self explanatory. It's responsible for the layout within a chart. // Scales, Legends and Plugins all rely on the layout service and can easily register to be placed anywhere they need // It is this service's responsibility of carrying out that layout. Chart.layoutService = { defaults: {}, /** * Register a box to a chart. * A box is simply a reference to an object that requires layout. eg. Scales, Legend, Title. * @param {Chart} chart - the chart to use * @param {ILayoutItem} item - the item to add to be layed out */ addBox: function(chart, item) { if (!chart.boxes) { chart.boxes = []; } // initialize item with default values item.fullWidth = item.fullWidth || false; item.position = item.position || 'top'; item.weight = item.weight || 0; chart.boxes.push(item); }, /** * Remove a layoutItem from a chart * @param {Chart} chart - the chart to remove the box from * @param {Object} layoutItem - the item to remove from the layout */ removeBox: function(chart, layoutItem) { var index = chart.boxes ? chart.boxes.indexOf(layoutItem) : -1; if (index !== -1) { chart.boxes.splice(index, 1); } }, /** * Sets (or updates) options on the given `item`. * @param {Chart} chart - the chart in which the item lives (or will be added to) * @param {Object} item - the item to configure with the given options * @param {Object} options - the new item options. */ configure: function(chart, item, options) { var props = ['fullWidth', 'position', 'weight']; var ilen = props.length; var i = 0; var prop; for (; i < ilen; ++i) { prop = props[i]; if (options.hasOwnProperty(prop)) { item[prop] = options[prop]; } } }, /** * Fits boxes of the given chart into the given size by having each box measure itself * then running a fitting algorithm * @param {Chart} chart - the chart * @param {Number} width - the width to fit into * @param {Number} height - the height to fit into */ update: function(chart, width, height) { if (!chart) { return; } var layoutOptions = chart.options.layout || {}; var padding = helpers.options.toPadding(layoutOptions.padding); var leftPadding = padding.left; var rightPadding = padding.right; var topPadding = padding.top; var bottomPadding = padding.bottom; var leftBoxes = filterByPosition(chart.boxes, 'left'); var rightBoxes = filterByPosition(chart.boxes, 'right'); var topBoxes = filterByPosition(chart.boxes, 'top'); var bottomBoxes = filterByPosition(chart.boxes, 'bottom'); var chartAreaBoxes = filterByPosition(chart.boxes, 'chartArea'); // Sort boxes by weight. A higher weight is further away from the chart area sortByWeight(leftBoxes, true); sortByWeight(rightBoxes, false); sortByWeight(topBoxes, true); sortByWeight(bottomBoxes, false); // Essentially we now have any number of boxes on each of the 4 sides. // Our canvas looks like the following. // The areas L1 and L2 are the left axes. R1 is the right axis, T1 is the top axis and // B1 is the bottom axis // There are also 4 quadrant-like locations (left to right instead of clockwise) reserved for chart overlays // These locations are single-box locations only, when trying to register a chartArea location that is already taken, // an error will be thrown. // // |----------------------------------------------------| // | T1 (Full Width) | // |----------------------------------------------------| // | | | T2 | | // | |----|-------------------------------------|----| // | | | C1 | | C2 | | // | | |----| |----| | // | | | | | // | L1 | L2 | ChartArea (C0) | R1 | // | | | | | // | | |----| |----| | // | | | C3 | | C4 | | // | |----|-------------------------------------|----| // | | | B1 | | // |----------------------------------------------------| // | B2 (Full Width) | // |----------------------------------------------------| // // What we do to find the best sizing, we do the following // 1. Determine the minimum size of the chart area. // 2. Split the remaining width equally between each vertical axis // 3. Split the remaining height equally between each horizontal axis // 4. Give each layout the maximum size it can be. The layout will return it's minimum size // 5. Adjust the sizes of each axis based on it's minimum reported size. // 6. Refit each axis // 7. Position each axis in the final location // 8. Tell the chart the final location of the chart area // 9. Tell any axes that overlay the chart area the positions of the chart area // Step 1 var chartWidth = width - leftPadding - rightPadding; var chartHeight = height - topPadding - bottomPadding; var chartAreaWidth = chartWidth / 2; // min 50% var chartAreaHeight = chartHeight / 2; // min 50% // Step 2 var verticalBoxWidth = (width - chartAreaWidth) / (leftBoxes.length + rightBoxes.length); // Step 3 var horizontalBoxHeight = (height - chartAreaHeight) / (topBoxes.length + bottomBoxes.length); // Step 4 var maxChartAreaWidth = chartWidth; var maxChartAreaHeight = chartHeight; var minBoxSizes = []; function getMinimumBoxSize(box) { var minSize; var isHorizontal = box.isHorizontal(); if (isHorizontal) { minSize = box.update(box.fullWidth ? chartWidth : maxChartAreaWidth, horizontalBoxHeight); maxChartAreaHeight -= minSize.height; } else { minSize = box.update(verticalBoxWidth, chartAreaHeight); maxChartAreaWidth -= minSize.width; } minBoxSizes.push({ horizontal: isHorizontal, minSize: minSize, box: box, }); } helpers.each(leftBoxes.concat(rightBoxes, topBoxes, bottomBoxes), getMinimumBoxSize); // If a horizontal box has padding, we move the left boxes over to avoid ugly charts (see issue #2478) var maxHorizontalLeftPadding = 0; var maxHorizontalRightPadding = 0; var maxVerticalTopPadding = 0; var maxVerticalBottomPadding = 0; helpers.each(topBoxes.concat(bottomBoxes), function(horizontalBox) { if (horizontalBox.getPadding) { var boxPadding = horizontalBox.getPadding(); maxHorizontalLeftPadding = Math.max(maxHorizontalLeftPadding, boxPadding.left); maxHorizontalRightPadding = Math.max(maxHorizontalRightPadding, boxPadding.right); } }); helpers.each(leftBoxes.concat(rightBoxes), function(verticalBox) { if (verticalBox.getPadding) { var boxPadding = verticalBox.getPadding(); maxVerticalTopPadding = Math.max(maxVerticalTopPadding, boxPadding.top); maxVerticalBottomPadding = Math.max(maxVerticalBottomPadding, boxPadding.bottom); } }); // At this point, maxChartAreaHeight and maxChartAreaWidth are the size the chart area could // be if the axes are drawn at their minimum sizes. // Steps 5 & 6 var totalLeftBoxesWidth = leftPadding; var totalRightBoxesWidth = rightPadding; var totalTopBoxesHeight = topPadding; var totalBottomBoxesHeight = bottomPadding; // Function to fit a box function fitBox(box) { var minBoxSize = helpers.findNextWhere(minBoxSizes, function(minBox) { return minBox.box === box; }); if (minBoxSize) { if (box.isHorizontal()) { var scaleMargin = { left: Math.max(totalLeftBoxesWidth, maxHorizontalLeftPadding), right: Math.max(totalRightBoxesWidth, maxHorizontalRightPadding), top: 0, bottom: 0 }; // Don't use min size here because of label rotation. When the labels are rotated, their rotation highly depends // on the margin. Sometimes they need to increase in size slightly box.update(box.fullWidth ? chartWidth : maxChartAreaWidth, chartHeight / 2, scaleMargin); } else { box.update(minBoxSize.minSize.width, maxChartAreaHeight); } } } // Update, and calculate the left and right margins for the horizontal boxes helpers.each(leftBoxes.concat(rightBoxes), fitBox); helpers.each(leftBoxes, function(box) { totalLeftBoxesWidth += box.width; }); helpers.each(rightBoxes, function(box) { totalRightBoxesWidth += box.width; }); // Set the Left and Right margins for the horizontal boxes helpers.each(topBoxes.concat(bottomBoxes), fitBox); // Figure out how much margin is on the top and bottom of the vertical boxes helpers.each(topBoxes, function(box) { totalTopBoxesHeight += box.height; }); helpers.each(bottomBoxes, function(box) { totalBottomBoxesHeight += box.height; }); function finalFitVerticalBox(box) { var minBoxSize = helpers.findNextWhere(minBoxSizes, function(minSize) { return minSize.box === box; }); var scaleMargin = { left: 0, right: 0, top: totalTopBoxesHeight, bottom: totalBottomBoxesHeight }; if (minBoxSize) { box.update(minBoxSize.minSize.width, maxChartAreaHeight, scaleMargin); } } // Let the left layout know the final margin helpers.each(leftBoxes.concat(rightBoxes), finalFitVerticalBox); // Recalculate because the size of each layout might have changed slightly due to the margins (label rotation for instance) totalLeftBoxesWidth = leftPadding; totalRightBoxesWidth = rightPadding; totalTopBoxesHeight = topPadding; totalBottomBoxesHeight = bottomPadding; helpers.each(leftBoxes, function(box) { totalLeftBoxesWidth += box.width; }); helpers.each(rightBoxes, function(box) { totalRightBoxesWidth += box.width; }); helpers.each(topBoxes, function(box) { totalTopBoxesHeight += box.height; }); helpers.each(bottomBoxes, function(box) { totalBottomBoxesHeight += box.height; }); // We may be adding some padding to account for rotated x axis labels var leftPaddingAddition = Math.max(maxHorizontalLeftPadding - totalLeftBoxesWidth, 0); totalLeftBoxesWidth += leftPaddingAddition; totalRightBoxesWidth += Math.max(maxHorizontalRightPadding - totalRightBoxesWidth, 0); var topPaddingAddition = Math.max(maxVerticalTopPadding - totalTopBoxesHeight, 0); totalTopBoxesHeight += topPaddingAddition; totalBottomBoxesHeight += Math.max(maxVerticalBottomPadding - totalBottomBoxesHeight, 0); // Figure out if our chart area changed. This would occur if the dataset layout label rotation // changed due to the application of the margins in step 6. Since we can only get bigger, this is safe to do // without calling `fit` again var newMaxChartAreaHeight = height - totalTopBoxesHeight - totalBottomBoxesHeight; var newMaxChartAreaWidth = width - totalLeftBoxesWidth - totalRightBoxesWidth; if (newMaxChartAreaWidth !== maxChartAreaWidth || newMaxChartAreaHeight !== maxChartAreaHeight) { helpers.each(leftBoxes, function(box) { box.height = newMaxChartAreaHeight; }); helpers.each(rightBoxes, function(box) { box.height = newMaxChartAreaHeight; }); helpers.each(topBoxes, function(box) { if (!box.fullWidth) { box.width = newMaxChartAreaWidth; } }); helpers.each(bottomBoxes, function(box) { if (!box.fullWidth) { box.width = newMaxChartAreaWidth; } }); maxChartAreaHeight = newMaxChartAreaHeight; maxChartAreaWidth = newMaxChartAreaWidth; } // Step 7 - Position the boxes var left = leftPadding + leftPaddingAddition; var top = topPadding + topPaddingAddition; function placeBox(box) { if (box.isHorizontal()) { box.left = box.fullWidth ? leftPadding : totalLeftBoxesWidth; box.right = box.fullWidth ? width - rightPadding : totalLeftBoxesWidth + maxChartAreaWidth; box.top = top; box.bottom = top + box.height; // Move to next point top = box.bottom; } else { box.left = left; box.right = left + box.width; box.top = totalTopBoxesHeight; box.bottom = totalTopBoxesHeight + maxChartAreaHeight; // Move to next point left = box.right; } } helpers.each(leftBoxes.concat(topBoxes), placeBox); // Account for chart width and height left += maxChartAreaWidth; top += maxChartAreaHeight; helpers.each(rightBoxes, placeBox); helpers.each(bottomBoxes, placeBox); // Step 8 chart.chartArea = { left: totalLeftBoxesWidth, top: totalTopBoxesHeight, right: totalLeftBoxesWidth + maxChartAreaWidth, bottom: totalTopBoxesHeight + maxChartAreaHeight }; // Step 9 helpers.each(chartAreaBoxes, function(box) { box.left = chart.chartArea.left; box.top = chart.chartArea.top; box.right = chart.chartArea.right; box.bottom = chart.chartArea.bottom; box.update(maxChartAreaWidth, maxChartAreaHeight); }); } }; }; },{"45":45}],31:[function(require,module,exports){ 'use strict'; var defaults = require(25); var Element = require(26); var helpers = require(45); defaults._set('global', { plugins: {} }); module.exports = function(Chart) { /** * The plugin service singleton * @namespace Chart.plugins * @since 2.1.0 */ Chart.plugins = { /** * Globally registered plugins. * @private */ _plugins: [], /** * This identifier is used to invalidate the descriptors cache attached to each chart * when a global plugin is registered or unregistered. In this case, the cache ID is * incremented and descriptors are regenerated during following API calls. * @private */ _cacheId: 0, /** * Registers the given plugin(s) if not already registered. * @param {Array|Object} plugins plugin instance(s). */ register: function(plugins) { var p = this._plugins; ([]).concat(plugins).forEach(function(plugin) { if (p.indexOf(plugin) === -1) { p.push(plugin); } }); this._cacheId++; }, /** * Unregisters the given plugin(s) only if registered. * @param {Array|Object} plugins plugin instance(s). */ unregister: function(plugins) { var p = this._plugins; ([]).concat(plugins).forEach(function(plugin) { var idx = p.indexOf(plugin); if (idx !== -1) { p.splice(idx, 1); } }); this._cacheId++; }, /** * Remove all registered plugins. * @since 2.1.5 */ clear: function() { this._plugins = []; this._cacheId++; }, /** * Returns the number of registered plugins? * @returns {Number} * @since 2.1.5 */ count: function() { return this._plugins.length; }, /** * Returns all registered plugin instances. * @returns {Array} array of plugin objects. * @since 2.1.5 */ getAll: function() { return this._plugins; }, /** * Calls enabled plugins for `chart` on the specified hook and with the given args. * This method immediately returns as soon as a plugin explicitly returns false. The * returned value can be used, for instance, to interrupt the current action. * @param {Object} chart - The chart instance for which plugins should be called. * @param {String} hook - The name of the plugin method to call (e.g. 'beforeUpdate'). * @param {Array} [args] - Extra arguments to apply to the hook call. * @returns {Boolean} false if any of the plugins return false, else returns true. */ notify: function(chart, hook, args) { var descriptors = this.descriptors(chart); var ilen = descriptors.length; var i, descriptor, plugin, params, method; for (i = 0; i < ilen; ++i) { descriptor = descriptors[i]; plugin = descriptor.plugin; method = plugin[hook]; if (typeof method === 'function') { params = [chart].concat(args || []); params.push(descriptor.options); if (method.apply(plugin, params) === false) { return false; } } } return true; }, /** * Returns descriptors of enabled plugins for the given chart. * @returns {Array} [{ plugin, options }] * @private */ descriptors: function(chart) { var cache = chart._plugins || (chart._plugins = {}); if (cache.id === this._cacheId) { return cache.descriptors; } var plugins = []; var descriptors = []; var config = (chart && chart.config) || {}; var options = (config.options && config.options.plugins) || {}; this._plugins.concat(config.plugins || []).forEach(function(plugin) { var idx = plugins.indexOf(plugin); if (idx !== -1) { return; } var id = plugin.id; var opts = options[id]; if (opts === false) { return; } if (opts === true) { opts = helpers.clone(defaults.global.plugins[id]); } plugins.push(plugin); descriptors.push({ plugin: plugin, options: opts || {} }); }); cache.descriptors = descriptors; cache.id = this._cacheId; return descriptors; } }; /** * Plugin extension hooks. * @interface IPlugin * @since 2.1.0 */ /** * @method IPlugin#beforeInit * @desc Called before initializing `chart`. * @param {Chart.Controller} chart - The chart instance. * @param {Object} options - The plugin options. */ /** * @method IPlugin#afterInit * @desc Called after `chart` has been initialized and before the first update. * @param {Chart.Controller} chart - The chart instance. * @param {Object} options - The plugin options. */ /** * @method IPlugin#beforeUpdate * @desc Called before updating `chart`. If any plugin returns `false`, the update * is cancelled (and thus subsequent render(s)) until another `update` is triggered. * @param {Chart.Controller} chart - The chart instance. * @param {Object} options - The plugin options. * @returns {Boolean} `false` to cancel the chart update. */ /** * @method IPlugin#afterUpdate * @desc Called after `chart` has been updated and before rendering. Note that this * hook will not be called if the chart update has been previously cancelled. * @param {Chart.Controller} chart - The chart instance. * @param {Object} options - The plugin options. */ /** * @method IPlugin#beforeDatasetsUpdate * @desc Called before updating the `chart` datasets. If any plugin returns `false`, * the datasets update is cancelled until another `update` is triggered. * @param {Chart.Controller} chart - The chart instance. * @param {Object} options - The plugin options. * @returns {Boolean} false to cancel the datasets update. * @since version 2.1.5 */ /** * @method IPlugin#afterDatasetsUpdate * @desc Called after the `chart` datasets have been updated. Note that this hook * will not be called if the datasets update has been previously cancelled. * @param {Chart.Controller} chart - The chart instance. * @param {Object} options - The plugin options. * @since version 2.1.5 */ /** * @method IPlugin#beforeDatasetUpdate * @desc Called before updating the `chart` dataset at the given `args.index`. If any plugin * returns `false`, the datasets update is cancelled until another `update` is triggered. * @param {Chart} chart - The chart instance. * @param {Object} args - The call arguments. * @param {Number} args.index - The dataset index. * @param {Object} args.meta - The dataset metadata. * @param {Object} options - The plugin options. * @returns {Boolean} `false` to cancel the chart datasets drawing. */ /** * @method IPlugin#afterDatasetUpdate * @desc Called after the `chart` datasets at the given `args.index` has been updated. Note * that this hook will not be called if the datasets update has been previously cancelled. * @param {Chart} chart - The chart instance. * @param {Object} args - The call arguments. * @param {Number} args.index - The dataset index. * @param {Object} args.meta - The dataset metadata. * @param {Object} options - The plugin options. */ /** * @method IPlugin#beforeLayout * @desc Called before laying out `chart`. If any plugin returns `false`, * the layout update is cancelled until another `update` is triggered. * @param {Chart.Controller} chart - The chart instance. * @param {Object} options - The plugin options. * @returns {Boolean} `false` to cancel the chart layout. */ /** * @method IPlugin#afterLayout * @desc Called after the `chart` has been layed out. Note that this hook will not * be called if the layout update has been previously cancelled. * @param {Chart.Controller} chart - The chart instance. * @param {Object} options - The plugin options. */ /** * @method IPlugin#beforeRender * @desc Called before rendering `chart`. If any plugin returns `false`, * the rendering is cancelled until another `render` is triggered. * @param {Chart.Controller} chart - The chart instance. * @param {Object} options - The plugin options. * @returns {Boolean} `false` to cancel the chart rendering. */ /** * @method IPlugin#afterRender * @desc Called after the `chart` has been fully rendered (and animation completed). Note * that this hook will not be called if the rendering has been previously cancelled. * @param {Chart.Controller} chart - The chart instance. * @param {Object} options - The plugin options. */ /** * @method IPlugin#beforeDraw * @desc Called before drawing `chart` at every animation frame specified by the given * easing value. If any plugin returns `false`, the frame drawing is cancelled until * another `render` is triggered. * @param {Chart.Controller} chart - The chart instance. * @param {Number} easingValue - The current animation value, between 0.0 and 1.0. * @param {Object} options - The plugin options. * @returns {Boolean} `false` to cancel the chart drawing. */ /** * @method IPlugin#afterDraw * @desc Called after the `chart` has been drawn for the specific easing value. Note * that this hook will not be called if the drawing has been previously cancelled. * @param {Chart.Controller} chart - The chart instance. * @param {Number} easingValue - The current animation value, between 0.0 and 1.0. * @param {Object} options - The plugin options. */ /** * @method IPlugin#beforeDatasetsDraw * @desc Called before drawing the `chart` datasets. If any plugin returns `false`, * the datasets drawing is cancelled until another `render` is triggered. * @param {Chart.Controller} chart - The chart instance. * @param {Number} easingValue - The current animation value, between 0.0 and 1.0. * @param {Object} options - The plugin options. * @returns {Boolean} `false` to cancel the chart datasets drawing. */ /** * @method IPlugin#afterDatasetsDraw * @desc Called after the `chart` datasets have been drawn. Note that this hook * will not be called if the datasets drawing has been previously cancelled. * @param {Chart.Controller} chart - The chart instance. * @param {Number} easingValue - The current animation value, between 0.0 and 1.0. * @param {Object} options - The plugin options. */ /** * @method IPlugin#beforeDatasetDraw * @desc Called before drawing the `chart` dataset at the given `args.index` (datasets * are drawn in the reverse order). If any plugin returns `false`, the datasets drawing * is cancelled until another `render` is triggered. * @param {Chart} chart - The chart instance. * @param {Object} args - The call arguments. * @param {Number} args.index - The dataset index. * @param {Object} args.meta - The dataset metadata. * @param {Number} args.easingValue - The current animation value, between 0.0 and 1.0. * @param {Object} options - The plugin options. * @returns {Boolean} `false` to cancel the chart datasets drawing. */ /** * @method IPlugin#afterDatasetDraw * @desc Called after the `chart` datasets at the given `args.index` have been drawn * (datasets are drawn in the reverse order). Note that this hook will not be called * if the datasets drawing has been previously cancelled. * @param {Chart} chart - The chart instance. * @param {Object} args - The call arguments. * @param {Number} args.index - The dataset index. * @param {Object} args.meta - The dataset metadata. * @param {Number} args.easingValue - The current animation value, between 0.0 and 1.0. * @param {Object} options - The plugin options. */ /** * @method IPlugin#beforeTooltipDraw * @desc Called before drawing the `tooltip`. If any plugin returns `false`, * the tooltip drawing is cancelled until another `render` is triggered. * @param {Chart} chart - The chart instance. * @param {Object} args - The call arguments. * @param {Object} args.tooltip - The tooltip. * @param {Number} args.easingValue - The current animation value, between 0.0 and 1.0. * @param {Object} options - The plugin options. * @returns {Boolean} `false` to cancel the chart tooltip drawing. */ /** * @method IPlugin#afterTooltipDraw * @desc Called after drawing the `tooltip`. Note that this hook will not * be called if the tooltip drawing has been previously cancelled. * @param {Chart} chart - The chart instance. * @param {Object} args - The call arguments. * @param {Object} args.tooltip - The tooltip. * @param {Number} args.easingValue - The current animation value, between 0.0 and 1.0. * @param {Object} options - The plugin options. */ /** * @method IPlugin#beforeEvent * @desc Called before processing the specified `event`. If any plugin returns `false`, * the event will be discarded. * @param {Chart.Controller} chart - The chart instance. * @param {IEvent} event - The event object. * @param {Object} options - The plugin options. */ /** * @method IPlugin#afterEvent * @desc Called after the `event` has been consumed. Note that this hook * will not be called if the `event` has been previously discarded. * @param {Chart.Controller} chart - The chart instance. * @param {IEvent} event - The event object. * @param {Object} options - The plugin options. */ /** * @method IPlugin#resize * @desc Called after the chart as been resized. * @param {Chart.Controller} chart - The chart instance. * @param {Number} size - The new canvas display size (eq. canvas.style width & height). * @param {Object} options - The plugin options. */ /** * @method IPlugin#destroy * @desc Called after the chart as been destroyed. * @param {Chart.Controller} chart - The chart instance. * @param {Object} options - The plugin options. */ /** * Provided for backward compatibility, use Chart.plugins instead * @namespace Chart.pluginService * @deprecated since version 2.1.5 * @todo remove at version 3 * @private */ Chart.pluginService = Chart.plugins; /** * Provided for backward compatibility, inheriting from Chart.PlugingBase has no * effect, instead simply create/register plugins via plain JavaScript objects. * @interface Chart.PluginBase * @deprecated since version 2.5.0 * @todo remove at version 3 * @private */ Chart.PluginBase = Element.extend({}); }; },{"25":25,"26":26,"45":45}],32:[function(require,module,exports){ 'use strict'; var defaults = require(25); var Element = require(26); var helpers = require(45); var Ticks = require(34); defaults._set('scale', { display: true, position: 'left', offset: false, // grid line settings gridLines: { display: true, color: 'rgba(0, 0, 0, 0.1)', lineWidth: 1, drawBorder: true, drawOnChartArea: true, drawTicks: true, tickMarkLength: 10, zeroLineWidth: 1, zeroLineColor: 'rgba(0,0,0,0.25)', zeroLineBorderDash: [], zeroLineBorderDashOffset: 0.0, offsetGridLines: false, borderDash: [], borderDashOffset: 0.0 }, // scale label scaleLabel: { // display property display: false, // actual label labelString: '', // line height lineHeight: 1.2, // top/bottom padding padding: { top: 4, bottom: 4 } }, // label settings ticks: { beginAtZero: false, minRotation: 0, maxRotation: 50, mirror: false, padding: 0, reverse: false, display: true, autoSkip: true, autoSkipPadding: 0, labelOffset: 0, // We pass through arrays to be rendered as multiline labels, we convert Others to strings here. callback: Ticks.formatters.values, minor: {}, major: {} } }); function labelsFromTicks(ticks) { var labels = []; var i, ilen; for (i = 0, ilen = ticks.length; i < ilen; ++i) { labels.push(ticks[i].label); } return labels; } function getLineValue(scale, index, offsetGridLines) { var lineValue = scale.getPixelForTick(index); if (offsetGridLines) { if (index === 0) { lineValue -= (scale.getPixelForTick(1) - lineValue) / 2; } else { lineValue -= (lineValue - scale.getPixelForTick(index - 1)) / 2; } } return lineValue; } module.exports = function(Chart) { function computeTextSize(context, tick, font) { return helpers.isArray(tick) ? helpers.longestText(context, font, tick) : context.measureText(tick).width; } function parseFontOptions(options) { var valueOrDefault = helpers.valueOrDefault; var globalDefaults = defaults.global; var size = valueOrDefault(options.fontSize, globalDefaults.defaultFontSize); var style = valueOrDefault(options.fontStyle, globalDefaults.defaultFontStyle); var family = valueOrDefault(options.fontFamily, globalDefaults.defaultFontFamily); return { size: size, style: style, family: family, font: helpers.fontString(size, style, family) }; } function parseLineHeight(options) { return helpers.options.toLineHeight( helpers.valueOrDefault(options.lineHeight, 1.2), helpers.valueOrDefault(options.fontSize, defaults.global.defaultFontSize)); } Chart.Scale = Element.extend({ /** * Get the padding needed for the scale * @method getPadding * @private * @returns {Padding} the necessary padding */ getPadding: function() { var me = this; return { left: me.paddingLeft || 0, top: me.paddingTop || 0, right: me.paddingRight || 0, bottom: me.paddingBottom || 0 }; }, /** * Returns the scale tick objects ({label, major}) * @since 2.7 */ getTicks: function() { return this._ticks; }, // These methods are ordered by lifecyle. Utilities then follow. // Any function defined here is inherited by all scale types. // Any function can be extended by the scale type mergeTicksOptions: function() { var ticks = this.options.ticks; if (ticks.minor === false) { ticks.minor = { display: false }; } if (ticks.major === false) { ticks.major = { display: false }; } for (var key in ticks) { if (key !== 'major' && key !== 'minor') { if (typeof ticks.minor[key] === 'undefined') { ticks.minor[key] = ticks[key]; } if (typeof ticks.major[key] === 'undefined') { ticks.major[key] = ticks[key]; } } } }, beforeUpdate: function() { helpers.callback(this.options.beforeUpdate, [this]); }, update: function(maxWidth, maxHeight, margins) { var me = this; var i, ilen, labels, label, ticks, tick; // Update Lifecycle - Probably don't want to ever extend or overwrite this function ;) me.beforeUpdate(); // Absorb the master measurements me.maxWidth = maxWidth; me.maxHeight = maxHeight; me.margins = helpers.extend({ left: 0, right: 0, top: 0, bottom: 0 }, margins); me.longestTextCache = me.longestTextCache || {}; // Dimensions me.beforeSetDimensions(); me.setDimensions(); me.afterSetDimensions(); // Data min/max me.beforeDataLimits(); me.determineDataLimits(); me.afterDataLimits(); // Ticks - `this.ticks` is now DEPRECATED! // Internal ticks are now stored as objects in the PRIVATE `this._ticks` member // and must not be accessed directly from outside this class. `this.ticks` being // around for long time and not marked as private, we can't change its structure // without unexpected breaking changes. If you need to access the scale ticks, // use scale.getTicks() instead. me.beforeBuildTicks(); // New implementations should return an array of objects but for BACKWARD COMPAT, // we still support no return (`this.ticks` internally set by calling this method). ticks = me.buildTicks() || []; me.afterBuildTicks(); me.beforeTickToLabelConversion(); // New implementations should return the formatted tick labels but for BACKWARD // COMPAT, we still support no return (`this.ticks` internally changed by calling // this method and supposed to contain only string values). labels = me.convertTicksToLabels(ticks) || me.ticks; me.afterTickToLabelConversion(); me.ticks = labels; // BACKWARD COMPATIBILITY // IMPORTANT: from this point, we consider that `this.ticks` will NEVER change! // BACKWARD COMPAT: synchronize `_ticks` with labels (so potentially `this.ticks`) for (i = 0, ilen = labels.length; i < ilen; ++i) { label = labels[i]; tick = ticks[i]; if (!tick) { ticks.push(tick = { label: label, major: false }); } else { tick.label = label; } } me._ticks = ticks; // Tick Rotation me.beforeCalculateTickRotation(); me.calculateTickRotation(); me.afterCalculateTickRotation(); // Fit me.beforeFit(); me.fit(); me.afterFit(); // me.afterUpdate(); return me.minSize; }, afterUpdate: function() { helpers.callback(this.options.afterUpdate, [this]); }, // beforeSetDimensions: function() { helpers.callback(this.options.beforeSetDimensions, [this]); }, setDimensions: function() { var me = this; // Set the unconstrained dimension before label rotation if (me.isHorizontal()) { // Reset position before calculating rotation me.width = me.maxWidth; me.left = 0; me.right = me.width; } else { me.height = me.maxHeight; // Reset position before calculating rotation me.top = 0; me.bottom = me.height; } // Reset padding me.paddingLeft = 0; me.paddingTop = 0; me.paddingRight = 0; me.paddingBottom = 0; }, afterSetDimensions: function() { helpers.callback(this.options.afterSetDimensions, [this]); }, // Data limits beforeDataLimits: function() { helpers.callback(this.options.beforeDataLimits, [this]); }, determineDataLimits: helpers.noop, afterDataLimits: function() { helpers.callback(this.options.afterDataLimits, [this]); }, // beforeBuildTicks: function() { helpers.callback(this.options.beforeBuildTicks, [this]); }, buildTicks: helpers.noop, afterBuildTicks: function() { helpers.callback(this.options.afterBuildTicks, [this]); }, beforeTickToLabelConversion: function() { helpers.callback(this.options.beforeTickToLabelConversion, [this]); }, convertTicksToLabels: function() { var me = this; // Convert ticks to strings var tickOpts = me.options.ticks; me.ticks = me.ticks.map(tickOpts.userCallback || tickOpts.callback, this); }, afterTickToLabelConversion: function() { helpers.callback(this.options.afterTickToLabelConversion, [this]); }, // beforeCalculateTickRotation: function() { helpers.callback(this.options.beforeCalculateTickRotation, [this]); }, calculateTickRotation: function() { var me = this; var context = me.ctx; var tickOpts = me.options.ticks; var labels = labelsFromTicks(me._ticks); // Get the width of each grid by calculating the difference // between x offsets between 0 and 1. var tickFont = parseFontOptions(tickOpts); context.font = tickFont.font; var labelRotation = tickOpts.minRotation || 0; if (labels.length && me.options.display && me.isHorizontal()) { var originalLabelWidth = helpers.longestText(context, tickFont.font, labels, me.longestTextCache); var labelWidth = originalLabelWidth; var cosRotation, sinRotation; // Allow 3 pixels x2 padding either side for label readability var tickWidth = me.getPixelForTick(1) - me.getPixelForTick(0) - 6; // Max label rotation can be set or default to 90 - also act as a loop counter while (labelWidth > tickWidth && labelRotation < tickOpts.maxRotation) { var angleRadians = helpers.toRadians(labelRotation); cosRotation = Math.cos(angleRadians); sinRotation = Math.sin(angleRadians); if (sinRotation * originalLabelWidth > me.maxHeight) { // go back one step labelRotation--; break; } labelRotation++; labelWidth = cosRotation * originalLabelWidth; } } me.labelRotation = labelRotation; }, afterCalculateTickRotation: function() { helpers.callback(this.options.afterCalculateTickRotation, [this]); }, // beforeFit: function() { helpers.callback(this.options.beforeFit, [this]); }, fit: function() { var me = this; // Reset var minSize = me.minSize = { width: 0, height: 0 }; var labels = labelsFromTicks(me._ticks); var opts = me.options; var tickOpts = opts.ticks; var scaleLabelOpts = opts.scaleLabel; var gridLineOpts = opts.gridLines; var display = opts.display; var isHorizontal = me.isHorizontal(); var tickFont = parseFontOptions(tickOpts); var tickMarkLength = opts.gridLines.tickMarkLength; // Width if (isHorizontal) { // subtract the margins to line up with the chartArea if we are a full width scale minSize.width = me.isFullWidth() ? me.maxWidth - me.margins.left - me.margins.right : me.maxWidth; } else { minSize.width = display && gridLineOpts.drawTicks ? tickMarkLength : 0; } // height if (isHorizontal) { minSize.height = display && gridLineOpts.drawTicks ? tickMarkLength : 0; } else { minSize.height = me.maxHeight; // fill all the height } // Are we showing a title for the scale? if (scaleLabelOpts.display && display) { var scaleLabelLineHeight = parseLineHeight(scaleLabelOpts); var scaleLabelPadding = helpers.options.toPadding(scaleLabelOpts.padding); var deltaHeight = scaleLabelLineHeight + scaleLabelPadding.height; if (isHorizontal) { minSize.height += deltaHeight; } else { minSize.width += deltaHeight; } } // Don't bother fitting the ticks if we are not showing them if (tickOpts.display && display) { var largestTextWidth = helpers.longestText(me.ctx, tickFont.font, labels, me.longestTextCache); var tallestLabelHeightInLines = helpers.numberOfLabelLines(labels); var lineSpace = tickFont.size * 0.5; var tickPadding = me.options.ticks.padding; if (isHorizontal) { // A horizontal axis is more constrained by the height. me.longestLabelWidth = largestTextWidth; var angleRadians = helpers.toRadians(me.labelRotation); var cosRotation = Math.cos(angleRadians); var sinRotation = Math.sin(angleRadians); // TODO - improve this calculation var labelHeight = (sinRotation * largestTextWidth) + (tickFont.size * tallestLabelHeightInLines) + (lineSpace * (tallestLabelHeightInLines - 1)) + lineSpace; // padding minSize.height = Math.min(me.maxHeight, minSize.height + labelHeight + tickPadding); me.ctx.font = tickFont.font; var firstLabelWidth = computeTextSize(me.ctx, labels[0], tickFont.font); var lastLabelWidth = computeTextSize(me.ctx, labels[labels.length - 1], tickFont.font); // Ensure that our ticks are always inside the canvas. When rotated, ticks are right aligned // which means that the right padding is dominated by the font height if (me.labelRotation !== 0) { me.paddingLeft = opts.position === 'bottom' ? (cosRotation * firstLabelWidth) + 3 : (cosRotation * lineSpace) + 3; // add 3 px to move away from canvas edges me.paddingRight = opts.position === 'bottom' ? (cosRotation * lineSpace) + 3 : (cosRotation * lastLabelWidth) + 3; } else { me.paddingLeft = firstLabelWidth / 2 + 3; // add 3 px to move away from canvas edges me.paddingRight = lastLabelWidth / 2 + 3; } } else { // A vertical axis is more constrained by the width. Labels are the // dominant factor here, so get that length first and account for padding if (tickOpts.mirror) { largestTextWidth = 0; } else { // use lineSpace for consistency with horizontal axis // tickPadding is not implemented for horizontal largestTextWidth += tickPadding + lineSpace; } minSize.width = Math.min(me.maxWidth, minSize.width + largestTextWidth); me.paddingTop = tickFont.size / 2; me.paddingBottom = tickFont.size / 2; } } me.handleMargins(); me.width = minSize.width; me.height = minSize.height; }, /** * Handle margins and padding interactions * @private */ handleMargins: function() { var me = this; if (me.margins) { me.paddingLeft = Math.max(me.paddingLeft - me.margins.left, 0); me.paddingTop = Math.max(me.paddingTop - me.margins.top, 0); me.paddingRight = Math.max(me.paddingRight - me.margins.right, 0); me.paddingBottom = Math.max(me.paddingBottom - me.margins.bottom, 0); } }, afterFit: function() { helpers.callback(this.options.afterFit, [this]); }, // Shared Methods isHorizontal: function() { return this.options.position === 'top' || this.options.position === 'bottom'; }, isFullWidth: function() { return (this.options.fullWidth); }, // Get the correct value. NaN bad inputs, If the value type is object get the x or y based on whether we are horizontal or not getRightValue: function(rawValue) { // Null and undefined values first if (helpers.isNullOrUndef(rawValue)) { return NaN; } // isNaN(object) returns true, so make sure NaN is checking for a number; Discard Infinite values if (typeof rawValue === 'number' && !isFinite(rawValue)) { return NaN; } // If it is in fact an object, dive in one more level if (rawValue) { if (this.isHorizontal()) { if (rawValue.x !== undefined) { return this.getRightValue(rawValue.x); } } else if (rawValue.y !== undefined) { return this.getRightValue(rawValue.y); } } // Value is good, return it return rawValue; }, /** * Used to get the value to display in the tooltip for the data at the given index * @param index * @param datasetIndex */ getLabelForIndex: helpers.noop, /** * Returns the location of the given data point. Value can either be an index or a numerical value * The coordinate (0, 0) is at the upper-left corner of the canvas * @param value * @param index * @param datasetIndex */ getPixelForValue: helpers.noop, /** * Used to get the data value from a given pixel. This is the inverse of getPixelForValue * The coordinate (0, 0) is at the upper-left corner of the canvas * @param pixel */ getValueForPixel: helpers.noop, /** * Returns the location of the tick at the given index * The coordinate (0, 0) is at the upper-left corner of the canvas */ getPixelForTick: function(index) { var me = this; var offset = me.options.offset; if (me.isHorizontal()) { var innerWidth = me.width - (me.paddingLeft + me.paddingRight); var tickWidth = innerWidth / Math.max((me._ticks.length - (offset ? 0 : 1)), 1); var pixel = (tickWidth * index) + me.paddingLeft; if (offset) { pixel += tickWidth / 2; } var finalVal = me.left + Math.round(pixel); finalVal += me.isFullWidth() ? me.margins.left : 0; return finalVal; } var innerHeight = me.height - (me.paddingTop + me.paddingBottom); return me.top + (index * (innerHeight / (me._ticks.length - 1))); }, /** * Utility for getting the pixel location of a percentage of scale * The coordinate (0, 0) is at the upper-left corner of the canvas */ getPixelForDecimal: function(decimal) { var me = this; if (me.isHorizontal()) { var innerWidth = me.width - (me.paddingLeft + me.paddingRight); var valueOffset = (innerWidth * decimal) + me.paddingLeft; var finalVal = me.left + Math.round(valueOffset); finalVal += me.isFullWidth() ? me.margins.left : 0; return finalVal; } return me.top + (decimal * me.height); }, /** * Returns the pixel for the minimum chart value * The coordinate (0, 0) is at the upper-left corner of the canvas */ getBasePixel: function() { return this.getPixelForValue(this.getBaseValue()); }, getBaseValue: function() { var me = this; var min = me.min; var max = me.max; return me.beginAtZero ? 0 : min < 0 && max < 0 ? max : min > 0 && max > 0 ? min : 0; }, /** * Returns a subset of ticks to be plotted to avoid overlapping labels. * @private */ _autoSkip: function(ticks) { var skipRatio; var me = this; var isHorizontal = me.isHorizontal(); var optionTicks = me.options.ticks.minor; var tickCount = ticks.length; var labelRotationRadians = helpers.toRadians(me.labelRotation); var cosRotation = Math.cos(labelRotationRadians); var longestRotatedLabel = me.longestLabelWidth * cosRotation; var result = []; var i, tick, shouldSkip; // figure out the maximum number of gridlines to show var maxTicks; if (optionTicks.maxTicksLimit) { maxTicks = optionTicks.maxTicksLimit; } if (isHorizontal) { skipRatio = false; if ((longestRotatedLabel + optionTicks.autoSkipPadding) * tickCount > (me.width - (me.paddingLeft + me.paddingRight))) { skipRatio = 1 + Math.floor(((longestRotatedLabel + optionTicks.autoSkipPadding) * tickCount) / (me.width - (me.paddingLeft + me.paddingRight))); } // if they defined a max number of optionTicks, // increase skipRatio until that number is met if (maxTicks && tickCount > maxTicks) { skipRatio = Math.max(skipRatio, Math.floor(tickCount / maxTicks)); } } for (i = 0; i < tickCount; i++) { tick = ticks[i]; // Since we always show the last tick,we need may need to hide the last shown one before shouldSkip = (skipRatio > 1 && i % skipRatio > 0) || (i % skipRatio === 0 && i + skipRatio >= tickCount); if (shouldSkip && i !== tickCount - 1) { // leave tick in place but make sure it's not displayed (#4635) delete tick.label; } result.push(tick); } return result; }, // Actually draw the scale on the canvas // @param {rectangle} chartArea : the area of the chart to draw full grid lines on draw: function(chartArea) { var me = this; var options = me.options; if (!options.display) { return; } var context = me.ctx; var globalDefaults = defaults.global; var optionTicks = options.ticks.minor; var optionMajorTicks = options.ticks.major || optionTicks; var gridLines = options.gridLines; var scaleLabel = options.scaleLabel; var isRotated = me.labelRotation !== 0; var isHorizontal = me.isHorizontal(); var ticks = optionTicks.autoSkip ? me._autoSkip(me.getTicks()) : me.getTicks(); var tickFontColor = helpers.valueOrDefault(optionTicks.fontColor, globalDefaults.defaultFontColor); var tickFont = parseFontOptions(optionTicks); var majorTickFontColor = helpers.valueOrDefault(optionMajorTicks.fontColor, globalDefaults.defaultFontColor); var majorTickFont = parseFontOptions(optionMajorTicks); var tl = gridLines.drawTicks ? gridLines.tickMarkLength : 0; var scaleLabelFontColor = helpers.valueOrDefault(scaleLabel.fontColor, globalDefaults.defaultFontColor); var scaleLabelFont = parseFontOptions(scaleLabel); var scaleLabelPadding = helpers.options.toPadding(scaleLabel.padding); var labelRotationRadians = helpers.toRadians(me.labelRotation); var itemsToDraw = []; var xTickStart = options.position === 'right' ? me.left : me.right - tl; var xTickEnd = options.position === 'right' ? me.left + tl : me.right; var yTickStart = options.position === 'bottom' ? me.top : me.bottom - tl; var yTickEnd = options.position === 'bottom' ? me.top + tl : me.bottom; helpers.each(ticks, function(tick, index) { // autoskipper skipped this tick (#4635) if (helpers.isNullOrUndef(tick.label)) { return; } var label = tick.label; var lineWidth, lineColor, borderDash, borderDashOffset; if (index === me.zeroLineIndex && options.offset === gridLines.offsetGridLines) { // Draw the first index specially lineWidth = gridLines.zeroLineWidth; lineColor = gridLines.zeroLineColor; borderDash = gridLines.zeroLineBorderDash; borderDashOffset = gridLines.zeroLineBorderDashOffset; } else { lineWidth = helpers.valueAtIndexOrDefault(gridLines.lineWidth, index); lineColor = helpers.valueAtIndexOrDefault(gridLines.color, index); borderDash = helpers.valueOrDefault(gridLines.borderDash, globalDefaults.borderDash); borderDashOffset = helpers.valueOrDefault(gridLines.borderDashOffset, globalDefaults.borderDashOffset); } // Common properties var tx1, ty1, tx2, ty2, x1, y1, x2, y2, labelX, labelY; var textAlign = 'middle'; var textBaseline = 'middle'; var tickPadding = optionTicks.padding; if (isHorizontal) { var labelYOffset = tl + tickPadding; if (options.position === 'bottom') { // bottom textBaseline = !isRotated ? 'top' : 'middle'; textAlign = !isRotated ? 'center' : 'right'; labelY = me.top + labelYOffset; } else { // top textBaseline = !isRotated ? 'bottom' : 'middle'; textAlign = !isRotated ? 'center' : 'left'; labelY = me.bottom - labelYOffset; } var xLineValue = getLineValue(me, index, gridLines.offsetGridLines && ticks.length > 1); if (xLineValue < me.left) { lineColor = 'rgba(0,0,0,0)'; } xLineValue += helpers.aliasPixel(lineWidth); labelX = me.getPixelForTick(index) + optionTicks.labelOffset; // x values for optionTicks (need to consider offsetLabel option) tx1 = tx2 = x1 = x2 = xLineValue; ty1 = yTickStart; ty2 = yTickEnd; y1 = chartArea.top; y2 = chartArea.bottom; } else { var isLeft = options.position === 'left'; var labelXOffset; if (optionTicks.mirror) { textAlign = isLeft ? 'left' : 'right'; labelXOffset = tickPadding; } else { textAlign = isLeft ? 'right' : 'left'; labelXOffset = tl + tickPadding; } labelX = isLeft ? me.right - labelXOffset : me.left + labelXOffset; var yLineValue = getLineValue(me, index, gridLines.offsetGridLines && ticks.length > 1); if (yLineValue < me.top) { lineColor = 'rgba(0,0,0,0)'; } yLineValue += helpers.aliasPixel(lineWidth); labelY = me.getPixelForTick(index) + optionTicks.labelOffset; tx1 = xTickStart; tx2 = xTickEnd; x1 = chartArea.left; x2 = chartArea.right; ty1 = ty2 = y1 = y2 = yLineValue; } itemsToDraw.push({ tx1: tx1, ty1: ty1, tx2: tx2, ty2: ty2, x1: x1, y1: y1, x2: x2, y2: y2, labelX: labelX, labelY: labelY, glWidth: lineWidth, glColor: lineColor, glBorderDash: borderDash, glBorderDashOffset: borderDashOffset, rotation: -1 * labelRotationRadians, label: label, major: tick.major, textBaseline: textBaseline, textAlign: textAlign }); }); // Draw all of the tick labels, tick marks, and grid lines at the correct places helpers.each(itemsToDraw, function(itemToDraw) { if (gridLines.display) { context.save(); context.lineWidth = itemToDraw.glWidth; context.strokeStyle = itemToDraw.glColor; if (context.setLineDash) { context.setLineDash(itemToDraw.glBorderDash); context.lineDashOffset = itemToDraw.glBorderDashOffset; } context.beginPath(); if (gridLines.drawTicks) { context.moveTo(itemToDraw.tx1, itemToDraw.ty1); context.lineTo(itemToDraw.tx2, itemToDraw.ty2); } if (gridLines.drawOnChartArea) { context.moveTo(itemToDraw.x1, itemToDraw.y1); context.lineTo(itemToDraw.x2, itemToDraw.y2); } context.stroke(); context.restore(); } if (optionTicks.display) { // Make sure we draw text in the correct color and font context.save(); context.translate(itemToDraw.labelX, itemToDraw.labelY); context.rotate(itemToDraw.rotation); context.font = itemToDraw.major ? majorTickFont.font : tickFont.font; context.fillStyle = itemToDraw.major ? majorTickFontColor : tickFontColor; context.textBaseline = itemToDraw.textBaseline; context.textAlign = itemToDraw.textAlign; var label = itemToDraw.label; if (helpers.isArray(label)) { for (var i = 0, y = 0; i < label.length; ++i) { // We just make sure the multiline element is a string here.. context.fillText('' + label[i], 0, y); // apply same lineSpacing as calculated @ L#320 y += (tickFont.size * 1.5); } } else { context.fillText(label, 0, 0); } context.restore(); } }); if (scaleLabel.display) { // Draw the scale label var scaleLabelX; var scaleLabelY; var rotation = 0; var halfLineHeight = parseLineHeight(scaleLabel) / 2; if (isHorizontal) { scaleLabelX = me.left + ((me.right - me.left) / 2); // midpoint of the width scaleLabelY = options.position === 'bottom' ? me.bottom - halfLineHeight - scaleLabelPadding.bottom : me.top + halfLineHeight + scaleLabelPadding.top; } else { var isLeft = options.position === 'left'; scaleLabelX = isLeft ? me.left + halfLineHeight + scaleLabelPadding.top : me.right - halfLineHeight - scaleLabelPadding.top; scaleLabelY = me.top + ((me.bottom - me.top) / 2); rotation = isLeft ? -0.5 * Math.PI : 0.5 * Math.PI; } context.save(); context.translate(scaleLabelX, scaleLabelY); context.rotate(rotation); context.textAlign = 'center'; context.textBaseline = 'middle'; context.fillStyle = scaleLabelFontColor; // render in correct colour context.font = scaleLabelFont.font; context.fillText(scaleLabel.labelString, 0, 0); context.restore(); } if (gridLines.drawBorder) { // Draw the line at the edge of the axis context.lineWidth = helpers.valueAtIndexOrDefault(gridLines.lineWidth, 0); context.strokeStyle = helpers.valueAtIndexOrDefault(gridLines.color, 0); var x1 = me.left; var x2 = me.right; var y1 = me.top; var y2 = me.bottom; var aliasPixel = helpers.aliasPixel(context.lineWidth); if (isHorizontal) { y1 = y2 = options.position === 'top' ? me.bottom : me.top; y1 += aliasPixel; y2 += aliasPixel; } else { x1 = x2 = options.position === 'left' ? me.right : me.left; x1 += aliasPixel; x2 += aliasPixel; } context.beginPath(); context.moveTo(x1, y1); context.lineTo(x2, y2); context.stroke(); } } }); }; },{"25":25,"26":26,"34":34,"45":45}],33:[function(require,module,exports){ 'use strict'; var defaults = require(25); var helpers = require(45); module.exports = function(Chart) { Chart.scaleService = { // Scale registration object. Extensions can register new scale types (such as log or DB scales) and then // use the new chart options to grab the correct scale constructors: {}, // Use a registration function so that we can move to an ES6 map when we no longer need to support // old browsers // Scale config defaults defaults: {}, registerScaleType: function(type, scaleConstructor, scaleDefaults) { this.constructors[type] = scaleConstructor; this.defaults[type] = helpers.clone(scaleDefaults); }, getScaleConstructor: function(type) { return this.constructors.hasOwnProperty(type) ? this.constructors[type] : undefined; }, getScaleDefaults: function(type) { // Return the scale defaults merged with the global settings so that we always use the latest ones return this.defaults.hasOwnProperty(type) ? helpers.merge({}, [defaults.scale, this.defaults[type]]) : {}; }, updateScaleDefaults: function(type, additions) { var me = this; if (me.defaults.hasOwnProperty(type)) { me.defaults[type] = helpers.extend(me.defaults[type], additions); } }, addScalesToLayout: function(chart) { // Adds each scale to the chart.boxes array to be sized accordingly helpers.each(chart.scales, function(scale) { // Set ILayoutItem parameters for backwards compatibility scale.fullWidth = scale.options.fullWidth; scale.position = scale.options.position; scale.weight = scale.options.weight; Chart.layoutService.addBox(chart, scale); }); } }; }; },{"25":25,"45":45}],34:[function(require,module,exports){ 'use strict'; var helpers = require(45); /** * Namespace to hold static tick generation functions * @namespace Chart.Ticks */ module.exports = { /** * Namespace to hold generators for different types of ticks * @namespace Chart.Ticks.generators */ generators: { /** * Interface for the options provided to the numeric tick generator * @interface INumericTickGenerationOptions */ /** * The maximum number of ticks to display * @name INumericTickGenerationOptions#maxTicks * @type Number */ /** * The distance between each tick. * @name INumericTickGenerationOptions#stepSize * @type Number * @optional */ /** * Forced minimum for the ticks. If not specified, the minimum of the data range is used to calculate the tick minimum * @name INumericTickGenerationOptions#min * @type Number * @optional */ /** * The maximum value of the ticks. If not specified, the maximum of the data range is used to calculate the tick maximum * @name INumericTickGenerationOptions#max * @type Number * @optional */ /** * Generate a set of linear ticks * @method Chart.Ticks.generators.linear * @param generationOptions {INumericTickGenerationOptions} the options used to generate the ticks * @param dataRange {IRange} the range of the data * @returns {Array} array of tick values */ linear: function(generationOptions, dataRange) { var ticks = []; // To get a "nice" value for the tick spacing, we will use the appropriately named // "nice number" algorithm. See http://stackoverflow.com/questions/8506881/nice-label-algorithm-for-charts-with-minimum-ticks // for details. var spacing; if (generationOptions.stepSize && generationOptions.stepSize > 0) { spacing = generationOptions.stepSize; } else { var niceRange = helpers.niceNum(dataRange.max - dataRange.min, false); spacing = helpers.niceNum(niceRange / (generationOptions.maxTicks - 1), true); } var niceMin = Math.floor(dataRange.min / spacing) * spacing; var niceMax = Math.ceil(dataRange.max / spacing) * spacing; // If min, max and stepSize is set and they make an evenly spaced scale use it. if (generationOptions.min && generationOptions.max && generationOptions.stepSize) { // If very close to our whole number, use it. if (helpers.almostWhole((generationOptions.max - generationOptions.min) / generationOptions.stepSize, spacing / 1000)) { niceMin = generationOptions.min; niceMax = generationOptions.max; } } var numSpaces = (niceMax - niceMin) / spacing; // If very close to our rounded value, use it. if (helpers.almostEquals(numSpaces, Math.round(numSpaces), spacing / 1000)) { numSpaces = Math.round(numSpaces); } else { numSpaces = Math.ceil(numSpaces); } // Put the values into the ticks array ticks.push(generationOptions.min !== undefined ? generationOptions.min : niceMin); for (var j = 1; j < numSpaces; ++j) { ticks.push(niceMin + (j * spacing)); } ticks.push(generationOptions.max !== undefined ? generationOptions.max : niceMax); return ticks; }, /** * Generate a set of logarithmic ticks * @method Chart.Ticks.generators.logarithmic * @param generationOptions {INumericTickGenerationOptions} the options used to generate the ticks * @param dataRange {IRange} the range of the data * @returns {Array} array of tick values */ logarithmic: function(generationOptions, dataRange) { var ticks = []; var valueOrDefault = helpers.valueOrDefault; // Figure out what the max number of ticks we can support it is based on the size of // the axis area. For now, we say that the minimum tick spacing in pixels must be 50 // We also limit the maximum number of ticks to 11 which gives a nice 10 squares on // the graph var tickVal = valueOrDefault(generationOptions.min, Math.pow(10, Math.floor(helpers.log10(dataRange.min)))); var endExp = Math.floor(helpers.log10(dataRange.max)); var endSignificand = Math.ceil(dataRange.max / Math.pow(10, endExp)); var exp, significand; if (tickVal === 0) { exp = Math.floor(helpers.log10(dataRange.minNotZero)); significand = Math.floor(dataRange.minNotZero / Math.pow(10, exp)); ticks.push(tickVal); tickVal = significand * Math.pow(10, exp); } else { exp = Math.floor(helpers.log10(tickVal)); significand = Math.floor(tickVal / Math.pow(10, exp)); } do { ticks.push(tickVal); ++significand; if (significand === 10) { significand = 1; ++exp; } tickVal = significand * Math.pow(10, exp); } while (exp < endExp || (exp === endExp && significand < endSignificand)); var lastTick = valueOrDefault(generationOptions.max, tickVal); ticks.push(lastTick); return ticks; } }, /** * Namespace to hold formatters for different types of ticks * @namespace Chart.Ticks.formatters */ formatters: { /** * Formatter for value labels * @method Chart.Ticks.formatters.values * @param value the value to display * @return {String|Array} the label to display */ values: function(value) { return helpers.isArray(value) ? value : '' + value; }, /** * Formatter for linear numeric ticks * @method Chart.Ticks.formatters.linear * @param tickValue {Number} the value to be formatted * @param index {Number} the position of the tickValue parameter in the ticks array * @param ticks {Array} the list of ticks being converted * @return {String} string representation of the tickValue parameter */ linear: function(tickValue, index, ticks) { // If we have lots of ticks, don't use the ones var delta = ticks.length > 3 ? ticks[2] - ticks[1] : ticks[1] - ticks[0]; // If we have a number like 2.5 as the delta, figure out how many decimal places we need if (Math.abs(delta) > 1) { if (tickValue !== Math.floor(tickValue)) { // not an integer delta = tickValue - Math.floor(tickValue); } } var logDelta = helpers.log10(Math.abs(delta)); var tickString = ''; if (tickValue !== 0) { var numDecimal = -1 * Math.floor(logDelta); numDecimal = Math.max(Math.min(numDecimal, 20), 0); // toFixed has a max of 20 decimal places tickString = tickValue.toFixed(numDecimal); } else { tickString = '0'; // never show decimal places for 0 } return tickString; }, logarithmic: function(tickValue, index, ticks) { var remain = tickValue / (Math.pow(10, Math.floor(helpers.log10(tickValue)))); if (tickValue === 0) { return '0'; } else if (remain === 1 || remain === 2 || remain === 5 || index === 0 || index === ticks.length - 1) { return tickValue.toExponential(); } return ''; } } }; },{"45":45}],35:[function(require,module,exports){ 'use strict'; var defaults = require(25); var Element = require(26); var helpers = require(45); defaults._set('global', { tooltips: { enabled: true, custom: null, mode: 'nearest', position: 'average', intersect: true, backgroundColor: 'rgba(0,0,0,0.8)', titleFontStyle: 'bold', titleSpacing: 2, titleMarginBottom: 6, titleFontColor: '#fff', titleAlign: 'left', bodySpacing: 2, bodyFontColor: '#fff', bodyAlign: 'left', footerFontStyle: 'bold', footerSpacing: 2, footerMarginTop: 6, footerFontColor: '#fff', footerAlign: 'left', yPadding: 6, xPadding: 6, caretPadding: 2, caretSize: 5, cornerRadius: 6, multiKeyBackground: '#fff', displayColors: true, borderColor: 'rgba(0,0,0,0)', borderWidth: 0, callbacks: { // Args are: (tooltipItems, data) beforeTitle: helpers.noop, title: function(tooltipItems, data) { // Pick first xLabel for now var title = ''; var labels = data.labels; var labelCount = labels ? labels.length : 0; if (tooltipItems.length > 0) { var item = tooltipItems[0]; if (item.xLabel) { title = item.xLabel; } else if (labelCount > 0 && item.index < labelCount) { title = labels[item.index]; } } return title; }, afterTitle: helpers.noop, // Args are: (tooltipItems, data) beforeBody: helpers.noop, // Args are: (tooltipItem, data) beforeLabel: helpers.noop, label: function(tooltipItem, data) { var label = data.datasets[tooltipItem.datasetIndex].label || ''; if (label) { label += ': '; } label += tooltipItem.yLabel; return label; }, labelColor: function(tooltipItem, chart) { var meta = chart.getDatasetMeta(tooltipItem.datasetIndex); var activeElement = meta.data[tooltipItem.index]; var view = activeElement._view; return { borderColor: view.borderColor, backgroundColor: view.backgroundColor }; }, labelTextColor: function() { return this._options.bodyFontColor; }, afterLabel: helpers.noop, // Args are: (tooltipItems, data) afterBody: helpers.noop, // Args are: (tooltipItems, data) beforeFooter: helpers.noop, footer: helpers.noop, afterFooter: helpers.noop } } }); module.exports = function(Chart) { /** * Helper method to merge the opacity into a color */ function mergeOpacity(colorString, opacity) { var color = helpers.color(colorString); return color.alpha(opacity * color.alpha()).rgbaString(); } // Helper to push or concat based on if the 2nd parameter is an array or not function pushOrConcat(base, toPush) { if (toPush) { if (helpers.isArray(toPush)) { // base = base.concat(toPush); Array.prototype.push.apply(base, toPush); } else { base.push(toPush); } } return base; } // Private helper to create a tooltip item model // @param element : the chart element (point, arc, bar) to create the tooltip item for // @return : new tooltip item function createTooltipItem(element) { var xScale = element._xScale; var yScale = element._yScale || element._scale; // handle radar || polarArea charts var index = element._index; var datasetIndex = element._datasetIndex; return { xLabel: xScale ? xScale.getLabelForIndex(index, datasetIndex) : '', yLabel: yScale ? yScale.getLabelForIndex(index, datasetIndex) : '', index: index, datasetIndex: datasetIndex, x: element._model.x, y: element._model.y }; } /** * Helper to get the reset model for the tooltip * @param tooltipOpts {Object} the tooltip options */ function getBaseModel(tooltipOpts) { var globalDefaults = defaults.global; var valueOrDefault = helpers.valueOrDefault; return { // Positioning xPadding: tooltipOpts.xPadding, yPadding: tooltipOpts.yPadding, xAlign: tooltipOpts.xAlign, yAlign: tooltipOpts.yAlign, // Body bodyFontColor: tooltipOpts.bodyFontColor, _bodyFontFamily: valueOrDefault(tooltipOpts.bodyFontFamily, globalDefaults.defaultFontFamily), _bodyFontStyle: valueOrDefault(tooltipOpts.bodyFontStyle, globalDefaults.defaultFontStyle), _bodyAlign: tooltipOpts.bodyAlign, bodyFontSize: valueOrDefault(tooltipOpts.bodyFontSize, globalDefaults.defaultFontSize), bodySpacing: tooltipOpts.bodySpacing, // Title titleFontColor: tooltipOpts.titleFontColor, _titleFontFamily: valueOrDefault(tooltipOpts.titleFontFamily, globalDefaults.defaultFontFamily), _titleFontStyle: valueOrDefault(tooltipOpts.titleFontStyle, globalDefaults.defaultFontStyle), titleFontSize: valueOrDefault(tooltipOpts.titleFontSize, globalDefaults.defaultFontSize), _titleAlign: tooltipOpts.titleAlign, titleSpacing: tooltipOpts.titleSpacing, titleMarginBottom: tooltipOpts.titleMarginBottom, // Footer footerFontColor: tooltipOpts.footerFontColor, _footerFontFamily: valueOrDefault(tooltipOpts.footerFontFamily, globalDefaults.defaultFontFamily), _footerFontStyle: valueOrDefault(tooltipOpts.footerFontStyle, globalDefaults.defaultFontStyle), footerFontSize: valueOrDefault(tooltipOpts.footerFontSize, globalDefaults.defaultFontSize), _footerAlign: tooltipOpts.footerAlign, footerSpacing: tooltipOpts.footerSpacing, footerMarginTop: tooltipOpts.footerMarginTop, // Appearance caretSize: tooltipOpts.caretSize, cornerRadius: tooltipOpts.cornerRadius, backgroundColor: tooltipOpts.backgroundColor, opacity: 0, legendColorBackground: tooltipOpts.multiKeyBackground, displayColors: tooltipOpts.displayColors, borderColor: tooltipOpts.borderColor, borderWidth: tooltipOpts.borderWidth }; } /** * Get the size of the tooltip */ function getTooltipSize(tooltip, model) { var ctx = tooltip._chart.ctx; var height = model.yPadding * 2; // Tooltip Padding var width = 0; // Count of all lines in the body var body = model.body; var combinedBodyLength = body.reduce(function(count, bodyItem) { return count + bodyItem.before.length + bodyItem.lines.length + bodyItem.after.length; }, 0); combinedBodyLength += model.beforeBody.length + model.afterBody.length; var titleLineCount = model.title.length; var footerLineCount = model.footer.length; var titleFontSize = model.titleFontSize; var bodyFontSize = model.bodyFontSize; var footerFontSize = model.footerFontSize; height += titleLineCount * titleFontSize; // Title Lines height += titleLineCount ? (titleLineCount - 1) * model.titleSpacing : 0; // Title Line Spacing height += titleLineCount ? model.titleMarginBottom : 0; // Title's bottom Margin height += combinedBodyLength * bodyFontSize; // Body Lines height += combinedBodyLength ? (combinedBodyLength - 1) * model.bodySpacing : 0; // Body Line Spacing height += footerLineCount ? model.footerMarginTop : 0; // Footer Margin height += footerLineCount * (footerFontSize); // Footer Lines height += footerLineCount ? (footerLineCount - 1) * model.footerSpacing : 0; // Footer Line Spacing // Title width var widthPadding = 0; var maxLineWidth = function(line) { width = Math.max(width, ctx.measureText(line).width + widthPadding); }; ctx.font = helpers.fontString(titleFontSize, model._titleFontStyle, model._titleFontFamily); helpers.each(model.title, maxLineWidth); // Body width ctx.font = helpers.fontString(bodyFontSize, model._bodyFontStyle, model._bodyFontFamily); helpers.each(model.beforeBody.concat(model.afterBody), maxLineWidth); // Body lines may include some extra width due to the color box widthPadding = model.displayColors ? (bodyFontSize + 2) : 0; helpers.each(body, function(bodyItem) { helpers.each(bodyItem.before, maxLineWidth); helpers.each(bodyItem.lines, maxLineWidth); helpers.each(bodyItem.after, maxLineWidth); }); // Reset back to 0 widthPadding = 0; // Footer width ctx.font = helpers.fontString(footerFontSize, model._footerFontStyle, model._footerFontFamily); helpers.each(model.footer, maxLineWidth); // Add padding width += 2 * model.xPadding; return { width: width, height: height }; } /** * Helper to get the alignment of a tooltip given the size */ function determineAlignment(tooltip, size) { var model = tooltip._model; var chart = tooltip._chart; var chartArea = tooltip._chart.chartArea; var xAlign = 'center'; var yAlign = 'center'; if (model.y < size.height) { yAlign = 'top'; } else if (model.y > (chart.height - size.height)) { yAlign = 'bottom'; } var lf, rf; // functions to determine left, right alignment var olf, orf; // functions to determine if left/right alignment causes tooltip to go outside chart var yf; // function to get the y alignment if the tooltip goes outside of the left or right edges var midX = (chartArea.left + chartArea.right) / 2; var midY = (chartArea.top + chartArea.bottom) / 2; if (yAlign === 'center') { lf = function(x) { return x <= midX; }; rf = function(x) { return x > midX; }; } else { lf = function(x) { return x <= (size.width / 2); }; rf = function(x) { return x >= (chart.width - (size.width / 2)); }; } olf = function(x) { return x + size.width > chart.width; }; orf = function(x) { return x - size.width < 0; }; yf = function(y) { return y <= midY ? 'top' : 'bottom'; }; if (lf(model.x)) { xAlign = 'left'; // Is tooltip too wide and goes over the right side of the chart.? if (olf(model.x)) { xAlign = 'center'; yAlign = yf(model.y); } } else if (rf(model.x)) { xAlign = 'right'; // Is tooltip too wide and goes outside left edge of canvas? if (orf(model.x)) { xAlign = 'center'; yAlign = yf(model.y); } } var opts = tooltip._options; return { xAlign: opts.xAlign ? opts.xAlign : xAlign, yAlign: opts.yAlign ? opts.yAlign : yAlign }; } /** * @Helper to get the location a tooltip needs to be placed at given the initial position (via the vm) and the size and alignment */ function getBackgroundPoint(vm, size, alignment) { // Background Position var x = vm.x; var y = vm.y; var caretSize = vm.caretSize; var caretPadding = vm.caretPadding; var cornerRadius = vm.cornerRadius; var xAlign = alignment.xAlign; var yAlign = alignment.yAlign; var paddingAndSize = caretSize + caretPadding; var radiusAndPadding = cornerRadius + caretPadding; if (xAlign === 'right') { x -= size.width; } else if (xAlign === 'center') { x -= (size.width / 2); } if (yAlign === 'top') { y += paddingAndSize; } else if (yAlign === 'bottom') { y -= size.height + paddingAndSize; } else { y -= (size.height / 2); } if (yAlign === 'center') { if (xAlign === 'left') { x += paddingAndSize; } else if (xAlign === 'right') { x -= paddingAndSize; } } else if (xAlign === 'left') { x -= radiusAndPadding; } else if (xAlign === 'right') { x += radiusAndPadding; } return { x: x, y: y }; } Chart.Tooltip = Element.extend({ initialize: function() { this._model = getBaseModel(this._options); this._lastActive = []; }, // Get the title // Args are: (tooltipItem, data) getTitle: function() { var me = this; var opts = me._options; var callbacks = opts.callbacks; var beforeTitle = callbacks.beforeTitle.apply(me, arguments); var title = callbacks.title.apply(me, arguments); var afterTitle = callbacks.afterTitle.apply(me, arguments); var lines = []; lines = pushOrConcat(lines, beforeTitle); lines = pushOrConcat(lines, title); lines = pushOrConcat(lines, afterTitle); return lines; }, // Args are: (tooltipItem, data) getBeforeBody: function() { var lines = this._options.callbacks.beforeBody.apply(this, arguments); return helpers.isArray(lines) ? lines : lines !== undefined ? [lines] : []; }, // Args are: (tooltipItem, data) getBody: function(tooltipItems, data) { var me = this; var callbacks = me._options.callbacks; var bodyItems = []; helpers.each(tooltipItems, function(tooltipItem) { var bodyItem = { before: [], lines: [], after: [] }; pushOrConcat(bodyItem.before, callbacks.beforeLabel.call(me, tooltipItem, data)); pushOrConcat(bodyItem.lines, callbacks.label.call(me, tooltipItem, data)); pushOrConcat(bodyItem.after, callbacks.afterLabel.call(me, tooltipItem, data)); bodyItems.push(bodyItem); }); return bodyItems; }, // Args are: (tooltipItem, data) getAfterBody: function() { var lines = this._options.callbacks.afterBody.apply(this, arguments); return helpers.isArray(lines) ? lines : lines !== undefined ? [lines] : []; }, // Get the footer and beforeFooter and afterFooter lines // Args are: (tooltipItem, data) getFooter: function() { var me = this; var callbacks = me._options.callbacks; var beforeFooter = callbacks.beforeFooter.apply(me, arguments); var footer = callbacks.footer.apply(me, arguments); var afterFooter = callbacks.afterFooter.apply(me, arguments); var lines = []; lines = pushOrConcat(lines, beforeFooter); lines = pushOrConcat(lines, footer); lines = pushOrConcat(lines, afterFooter); return lines; }, update: function(changed) { var me = this; var opts = me._options; // Need to regenerate the model because its faster than using extend and it is necessary due to the optimization in Chart.Element.transition // that does _view = _model if ease === 1. This causes the 2nd tooltip update to set properties in both the view and model at the same time // which breaks any animations. var existingModel = me._model; var model = me._model = getBaseModel(opts); var active = me._active; var data = me._data; // In the case where active.length === 0 we need to keep these at existing values for good animations var alignment = { xAlign: existingModel.xAlign, yAlign: existingModel.yAlign }; var backgroundPoint = { x: existingModel.x, y: existingModel.y }; var tooltipSize = { width: existingModel.width, height: existingModel.height }; var tooltipPosition = { x: existingModel.caretX, y: existingModel.caretY }; var i, len; if (active.length) { model.opacity = 1; var labelColors = []; var labelTextColors = []; tooltipPosition = Chart.Tooltip.positioners[opts.position].call(me, active, me._eventPosition); var tooltipItems = []; for (i = 0, len = active.length; i < len; ++i) { tooltipItems.push(createTooltipItem(active[i])); } // If the user provided a filter function, use it to modify the tooltip items if (opts.filter) { tooltipItems = tooltipItems.filter(function(a) { return opts.filter(a, data); }); } // If the user provided a sorting function, use it to modify the tooltip items if (opts.itemSort) { tooltipItems = tooltipItems.sort(function(a, b) { return opts.itemSort(a, b, data); }); } // Determine colors for boxes helpers.each(tooltipItems, function(tooltipItem) { labelColors.push(opts.callbacks.labelColor.call(me, tooltipItem, me._chart)); labelTextColors.push(opts.callbacks.labelTextColor.call(me, tooltipItem, me._chart)); }); // Build the Text Lines model.title = me.getTitle(tooltipItems, data); model.beforeBody = me.getBeforeBody(tooltipItems, data); model.body = me.getBody(tooltipItems, data); model.afterBody = me.getAfterBody(tooltipItems, data); model.footer = me.getFooter(tooltipItems, data); // Initial positioning and colors model.x = Math.round(tooltipPosition.x); model.y = Math.round(tooltipPosition.y); model.caretPadding = opts.caretPadding; model.labelColors = labelColors; model.labelTextColors = labelTextColors; // data points model.dataPoints = tooltipItems; // We need to determine alignment of the tooltip tooltipSize = getTooltipSize(this, model); alignment = determineAlignment(this, tooltipSize); // Final Size and Position backgroundPoint = getBackgroundPoint(model, tooltipSize, alignment); } else { model.opacity = 0; } model.xAlign = alignment.xAlign; model.yAlign = alignment.yAlign; model.x = backgroundPoint.x; model.y = backgroundPoint.y; model.width = tooltipSize.width; model.height = tooltipSize.height; // Point where the caret on the tooltip points to model.caretX = tooltipPosition.x; model.caretY = tooltipPosition.y; me._model = model; if (changed && opts.custom) { opts.custom.call(me, model); } return me; }, drawCaret: function(tooltipPoint, size) { var ctx = this._chart.ctx; var vm = this._view; var caretPosition = this.getCaretPosition(tooltipPoint, size, vm); ctx.lineTo(caretPosition.x1, caretPosition.y1); ctx.lineTo(caretPosition.x2, caretPosition.y2); ctx.lineTo(caretPosition.x3, caretPosition.y3); }, getCaretPosition: function(tooltipPoint, size, vm) { var x1, x2, x3, y1, y2, y3; var caretSize = vm.caretSize; var cornerRadius = vm.cornerRadius; var xAlign = vm.xAlign; var yAlign = vm.yAlign; var ptX = tooltipPoint.x; var ptY = tooltipPoint.y; var width = size.width; var height = size.height; if (yAlign === 'center') { y2 = ptY + (height / 2); if (xAlign === 'left') { x1 = ptX; x2 = x1 - caretSize; x3 = x1; y1 = y2 + caretSize; y3 = y2 - caretSize; } else { x1 = ptX + width; x2 = x1 + caretSize; x3 = x1; y1 = y2 - caretSize; y3 = y2 + caretSize; } } else { if (xAlign === 'left') { x2 = ptX + cornerRadius + (caretSize); x1 = x2 - caretSize; x3 = x2 + caretSize; } else if (xAlign === 'right') { x2 = ptX + width - cornerRadius - caretSize; x1 = x2 - caretSize; x3 = x2 + caretSize; } else { x2 = ptX + (width / 2); x1 = x2 - caretSize; x3 = x2 + caretSize; } if (yAlign === 'top') { y1 = ptY; y2 = y1 - caretSize; y3 = y1; } else { y1 = ptY + height; y2 = y1 + caretSize; y3 = y1; // invert drawing order var tmp = x3; x3 = x1; x1 = tmp; } } return {x1: x1, x2: x2, x3: x3, y1: y1, y2: y2, y3: y3}; }, drawTitle: function(pt, vm, ctx, opacity) { var title = vm.title; if (title.length) { ctx.textAlign = vm._titleAlign; ctx.textBaseline = 'top'; var titleFontSize = vm.titleFontSize; var titleSpacing = vm.titleSpacing; ctx.fillStyle = mergeOpacity(vm.titleFontColor, opacity); ctx.font = helpers.fontString(titleFontSize, vm._titleFontStyle, vm._titleFontFamily); var i, len; for (i = 0, len = title.length; i < len; ++i) { ctx.fillText(title[i], pt.x, pt.y); pt.y += titleFontSize + titleSpacing; // Line Height and spacing if (i + 1 === title.length) { pt.y += vm.titleMarginBottom - titleSpacing; // If Last, add margin, remove spacing } } } }, drawBody: function(pt, vm, ctx, opacity) { var bodyFontSize = vm.bodyFontSize; var bodySpacing = vm.bodySpacing; var body = vm.body; ctx.textAlign = vm._bodyAlign; ctx.textBaseline = 'top'; ctx.font = helpers.fontString(bodyFontSize, vm._bodyFontStyle, vm._bodyFontFamily); // Before Body var xLinePadding = 0; var fillLineOfText = function(line) { ctx.fillText(line, pt.x + xLinePadding, pt.y); pt.y += bodyFontSize + bodySpacing; }; // Before body lines ctx.fillStyle = mergeOpacity(vm.bodyFontColor, opacity); helpers.each(vm.beforeBody, fillLineOfText); var drawColorBoxes = vm.displayColors; xLinePadding = drawColorBoxes ? (bodyFontSize + 2) : 0; // Draw body lines now helpers.each(body, function(bodyItem, i) { var textColor = mergeOpacity(vm.labelTextColors[i], opacity); ctx.fillStyle = textColor; helpers.each(bodyItem.before, fillLineOfText); helpers.each(bodyItem.lines, function(line) { // Draw Legend-like boxes if needed if (drawColorBoxes) { // Fill a white rect so that colours merge nicely if the opacity is < 1 ctx.fillStyle = mergeOpacity(vm.legendColorBackground, opacity); ctx.fillRect(pt.x, pt.y, bodyFontSize, bodyFontSize); // Border ctx.lineWidth = 1; ctx.strokeStyle = mergeOpacity(vm.labelColors[i].borderColor, opacity); ctx.strokeRect(pt.x, pt.y, bodyFontSize, bodyFontSize); // Inner square ctx.fillStyle = mergeOpacity(vm.labelColors[i].backgroundColor, opacity); ctx.fillRect(pt.x + 1, pt.y + 1, bodyFontSize - 2, bodyFontSize - 2); ctx.fillStyle = textColor; } fillLineOfText(line); }); helpers.each(bodyItem.after, fillLineOfText); }); // Reset back to 0 for after body xLinePadding = 0; // After body lines helpers.each(vm.afterBody, fillLineOfText); pt.y -= bodySpacing; // Remove last body spacing }, drawFooter: function(pt, vm, ctx, opacity) { var footer = vm.footer; if (footer.length) { pt.y += vm.footerMarginTop; ctx.textAlign = vm._footerAlign; ctx.textBaseline = 'top'; ctx.fillStyle = mergeOpacity(vm.footerFontColor, opacity); ctx.font = helpers.fontString(vm.footerFontSize, vm._footerFontStyle, vm._footerFontFamily); helpers.each(footer, function(line) { ctx.fillText(line, pt.x, pt.y); pt.y += vm.footerFontSize + vm.footerSpacing; }); } }, drawBackground: function(pt, vm, ctx, tooltipSize, opacity) { ctx.fillStyle = mergeOpacity(vm.backgroundColor, opacity); ctx.strokeStyle = mergeOpacity(vm.borderColor, opacity); ctx.lineWidth = vm.borderWidth; var xAlign = vm.xAlign; var yAlign = vm.yAlign; var x = pt.x; var y = pt.y; var width = tooltipSize.width; var height = tooltipSize.height; var radius = vm.cornerRadius; ctx.beginPath(); ctx.moveTo(x + radius, y); if (yAlign === 'top') { this.drawCaret(pt, tooltipSize); } ctx.lineTo(x + width - radius, y); ctx.quadraticCurveTo(x + width, y, x + width, y + radius); if (yAlign === 'center' && xAlign === 'right') { this.drawCaret(pt, tooltipSize); } ctx.lineTo(x + width, y + height - radius); ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height); if (yAlign === 'bottom') { this.drawCaret(pt, tooltipSize); } ctx.lineTo(x + radius, y + height); ctx.quadraticCurveTo(x, y + height, x, y + height - radius); if (yAlign === 'center' && xAlign === 'left') { this.drawCaret(pt, tooltipSize); } ctx.lineTo(x, y + radius); ctx.quadraticCurveTo(x, y, x + radius, y); ctx.closePath(); ctx.fill(); if (vm.borderWidth > 0) { ctx.stroke(); } }, draw: function() { var ctx = this._chart.ctx; var vm = this._view; if (vm.opacity === 0) { return; } var tooltipSize = { width: vm.width, height: vm.height }; var pt = { x: vm.x, y: vm.y }; // IE11/Edge does not like very small opacities, so snap to 0 var opacity = Math.abs(vm.opacity < 1e-3) ? 0 : vm.opacity; // Truthy/falsey value for empty tooltip var hasTooltipContent = vm.title.length || vm.beforeBody.length || vm.body.length || vm.afterBody.length || vm.footer.length; if (this._options.enabled && hasTooltipContent) { // Draw Background this.drawBackground(pt, vm, ctx, tooltipSize, opacity); // Draw Title, Body, and Footer pt.x += vm.xPadding; pt.y += vm.yPadding; // Titles this.drawTitle(pt, vm, ctx, opacity); // Body this.drawBody(pt, vm, ctx, opacity); // Footer this.drawFooter(pt, vm, ctx, opacity); } }, /** * Handle an event * @private * @param {IEvent} event - The event to handle * @returns {Boolean} true if the tooltip changed */ handleEvent: function(e) { var me = this; var options = me._options; var changed = false; me._lastActive = me._lastActive || []; // Find Active Elements for tooltips if (e.type === 'mouseout') { me._active = []; } else { me._active = me._chart.getElementsAtEventForMode(e, options.mode, options); } // Remember Last Actives changed = !helpers.arrayEquals(me._active, me._lastActive); // If tooltip didn't change, do not handle the target event if (!changed) { return false; } me._lastActive = me._active; if (options.enabled || options.custom) { me._eventPosition = { x: e.x, y: e.y }; var model = me._model; me.update(true); me.pivot(); // See if our tooltip position changed changed |= (model.x !== me._model.x) || (model.y !== me._model.y); } return changed; } }); /** * @namespace Chart.Tooltip.positioners */ Chart.Tooltip.positioners = { /** * Average mode places the tooltip at the average position of the elements shown * @function Chart.Tooltip.positioners.average * @param elements {ChartElement[]} the elements being displayed in the tooltip * @returns {Point} tooltip position */ average: function(elements) { if (!elements.length) { return false; } var i, len; var x = 0; var y = 0; var count = 0; for (i = 0, len = elements.length; i < len; ++i) { var el = elements[i]; if (el && el.hasValue()) { var pos = el.tooltipPosition(); x += pos.x; y += pos.y; ++count; } } return { x: Math.round(x / count), y: Math.round(y / count) }; }, /** * Gets the tooltip position nearest of the item nearest to the event position * @function Chart.Tooltip.positioners.nearest * @param elements {Chart.Element[]} the tooltip elements * @param eventPosition {Point} the position of the event in canvas coordinates * @returns {Point} the tooltip position */ nearest: function(elements, eventPosition) { var x = eventPosition.x; var y = eventPosition.y; var minDistance = Number.POSITIVE_INFINITY; var i, len, nearestElement; for (i = 0, len = elements.length; i < len; ++i) { var el = elements[i]; if (el && el.hasValue()) { var center = el.getCenterPoint(); var d = helpers.distanceBetweenPoints(eventPosition, center); if (d < minDistance) { minDistance = d; nearestElement = el; } } } if (nearestElement) { var tp = nearestElement.tooltipPosition(); x = tp.x; y = tp.y; } return { x: x, y: y }; } }; }; },{"25":25,"26":26,"45":45}],36:[function(require,module,exports){ 'use strict'; var defaults = require(25); var Element = require(26); var helpers = require(45); defaults._set('global', { elements: { arc: { backgroundColor: defaults.global.defaultColor, borderColor: '#fff', borderWidth: 2 } } }); module.exports = Element.extend({ inLabelRange: function(mouseX) { var vm = this._view; if (vm) { return (Math.pow(mouseX - vm.x, 2) < Math.pow(vm.radius + vm.hoverRadius, 2)); } return false; }, inRange: function(chartX, chartY) { var vm = this._view; if (vm) { var pointRelativePosition = helpers.getAngleFromPoint(vm, {x: chartX, y: chartY}); var angle = pointRelativePosition.angle; var distance = pointRelativePosition.distance; // Sanitise angle range var startAngle = vm.startAngle; var endAngle = vm.endAngle; while (endAngle < startAngle) { endAngle += 2.0 * Math.PI; } while (angle > endAngle) { angle -= 2.0 * Math.PI; } while (angle < startAngle) { angle += 2.0 * Math.PI; } // Check if within the range of the open/close angle var betweenAngles = (angle >= startAngle && angle <= endAngle); var withinRadius = (distance >= vm.innerRadius && distance <= vm.outerRadius); return (betweenAngles && withinRadius); } return false; }, getCenterPoint: function() { var vm = this._view; var halfAngle = (vm.startAngle + vm.endAngle) / 2; var halfRadius = (vm.innerRadius + vm.outerRadius) / 2; return { x: vm.x + Math.cos(halfAngle) * halfRadius, y: vm.y + Math.sin(halfAngle) * halfRadius }; }, getArea: function() { var vm = this._view; return Math.PI * ((vm.endAngle - vm.startAngle) / (2 * Math.PI)) * (Math.pow(vm.outerRadius, 2) - Math.pow(vm.innerRadius, 2)); }, tooltipPosition: function() { var vm = this._view; var centreAngle = vm.startAngle + ((vm.endAngle - vm.startAngle) / 2); var rangeFromCentre = (vm.outerRadius - vm.innerRadius) / 2 + vm.innerRadius; return { x: vm.x + (Math.cos(centreAngle) * rangeFromCentre), y: vm.y + (Math.sin(centreAngle) * rangeFromCentre) }; }, draw: function() { var ctx = this._chart.ctx; var vm = this._view; var sA = vm.startAngle; var eA = vm.endAngle; ctx.beginPath(); ctx.arc(vm.x, vm.y, vm.outerRadius, sA, eA); ctx.arc(vm.x, vm.y, vm.innerRadius, eA, sA, true); ctx.closePath(); ctx.strokeStyle = vm.borderColor; ctx.lineWidth = vm.borderWidth; ctx.fillStyle = vm.backgroundColor; ctx.fill(); ctx.lineJoin = 'bevel'; if (vm.borderWidth) { ctx.stroke(); } } }); },{"25":25,"26":26,"45":45}],37:[function(require,module,exports){ 'use strict'; var defaults = require(25); var Element = require(26); var helpers = require(45); var globalDefaults = defaults.global; defaults._set('global', { elements: { line: { tension: 0.4, backgroundColor: globalDefaults.defaultColor, borderWidth: 3, borderColor: globalDefaults.defaultColor, borderCapStyle: 'butt', borderDash: [], borderDashOffset: 0.0, borderJoinStyle: 'miter', capBezierPoints: true, fill: true, // do we fill in the area between the line and its base axis } } }); module.exports = Element.extend({ draw: function() { var me = this; var vm = me._view; var ctx = me._chart.ctx; var spanGaps = vm.spanGaps; var points = me._children.slice(); // clone array var globalOptionLineElements = globalDefaults.elements.line; var lastDrawnIndex = -1; var index, current, previous, currentVM; // If we are looping, adding the first point again if (me._loop && points.length) { points.push(points[0]); } ctx.save(); // Stroke Line Options ctx.lineCap = vm.borderCapStyle || globalOptionLineElements.borderCapStyle; // IE 9 and 10 do not support line dash if (ctx.setLineDash) { ctx.setLineDash(vm.borderDash || globalOptionLineElements.borderDash); } ctx.lineDashOffset = vm.borderDashOffset || globalOptionLineElements.borderDashOffset; ctx.lineJoin = vm.borderJoinStyle || globalOptionLineElements.borderJoinStyle; ctx.lineWidth = vm.borderWidth || globalOptionLineElements.borderWidth; ctx.strokeStyle = vm.borderColor || globalDefaults.defaultColor; // Stroke Line ctx.beginPath(); lastDrawnIndex = -1; for (index = 0; index < points.length; ++index) { current = points[index]; previous = helpers.previousItem(points, index); currentVM = current._view; // First point moves to it's starting position no matter what if (index === 0) { if (!currentVM.skip) { ctx.moveTo(currentVM.x, currentVM.y); lastDrawnIndex = index; } } else { previous = lastDrawnIndex === -1 ? previous : points[lastDrawnIndex]; if (!currentVM.skip) { if ((lastDrawnIndex !== (index - 1) && !spanGaps) || lastDrawnIndex === -1) { // There was a gap and this is the first point after the gap ctx.moveTo(currentVM.x, currentVM.y); } else { // Line to next point helpers.canvas.lineTo(ctx, previous._view, current._view); } lastDrawnIndex = index; } } } ctx.stroke(); ctx.restore(); } }); },{"25":25,"26":26,"45":45}],38:[function(require,module,exports){ 'use strict'; var defaults = require(25); var Element = require(26); var helpers = require(45); var defaultColor = defaults.global.defaultColor; defaults._set('global', { elements: { point: { radius: 3, pointStyle: 'circle', backgroundColor: defaultColor, borderColor: defaultColor, borderWidth: 1, // Hover hitRadius: 1, hoverRadius: 4, hoverBorderWidth: 1 } } }); function xRange(mouseX) { var vm = this._view; return vm ? (Math.pow(mouseX - vm.x, 2) < Math.pow(vm.radius + vm.hitRadius, 2)) : false; } function yRange(mouseY) { var vm = this._view; return vm ? (Math.pow(mouseY - vm.y, 2) < Math.pow(vm.radius + vm.hitRadius, 2)) : false; } module.exports = Element.extend({ inRange: function(mouseX, mouseY) { var vm = this._view; return vm ? ((Math.pow(mouseX - vm.x, 2) + Math.pow(mouseY - vm.y, 2)) < Math.pow(vm.hitRadius + vm.radius, 2)) : false; }, inLabelRange: xRange, inXRange: xRange, inYRange: yRange, getCenterPoint: function() { var vm = this._view; return { x: vm.x, y: vm.y }; }, getArea: function() { return Math.PI * Math.pow(this._view.radius, 2); }, tooltipPosition: function() { var vm = this._view; return { x: vm.x, y: vm.y, padding: vm.radius + vm.borderWidth }; }, draw: function(chartArea) { var vm = this._view; var model = this._model; var ctx = this._chart.ctx; var pointStyle = vm.pointStyle; var radius = vm.radius; var x = vm.x; var y = vm.y; var color = helpers.color; var errMargin = 1.01; // 1.01 is margin for Accumulated error. (Especially Edge, IE.) var ratio = 0; if (vm.skip) { return; } ctx.strokeStyle = vm.borderColor || defaultColor; ctx.lineWidth = helpers.valueOrDefault(vm.borderWidth, defaults.global.elements.point.borderWidth); ctx.fillStyle = vm.backgroundColor || defaultColor; // Cliping for Points. // going out from inner charArea? if ((chartArea !== undefined) && ((model.x < chartArea.left) || (chartArea.right * errMargin < model.x) || (model.y < chartArea.top) || (chartArea.bottom * errMargin < model.y))) { // Point fade out if (model.x < chartArea.left) { ratio = (x - model.x) / (chartArea.left - model.x); } else if (chartArea.right * errMargin < model.x) { ratio = (model.x - x) / (model.x - chartArea.right); } else if (model.y < chartArea.top) { ratio = (y - model.y) / (chartArea.top - model.y); } else if (chartArea.bottom * errMargin < model.y) { ratio = (model.y - y) / (model.y - chartArea.bottom); } ratio = Math.round(ratio * 100) / 100; ctx.strokeStyle = color(ctx.strokeStyle).alpha(ratio).rgbString(); ctx.fillStyle = color(ctx.fillStyle).alpha(ratio).rgbString(); } helpers.canvas.drawPoint(ctx, pointStyle, radius, x, y); } }); },{"25":25,"26":26,"45":45}],39:[function(require,module,exports){ 'use strict'; var defaults = require(25); var Element = require(26); defaults._set('global', { elements: { rectangle: { backgroundColor: defaults.global.defaultColor, borderColor: defaults.global.defaultColor, borderSkipped: 'bottom', borderWidth: 0 } } }); function isVertical(bar) { return bar._view.width !== undefined; } /** * Helper function to get the bounds of the bar regardless of the orientation * @param bar {Chart.Element.Rectangle} the bar * @return {Bounds} bounds of the bar * @private */ function getBarBounds(bar) { var vm = bar._view; var x1, x2, y1, y2; if (isVertical(bar)) { // vertical var halfWidth = vm.width / 2; x1 = vm.x - halfWidth; x2 = vm.x + halfWidth; y1 = Math.min(vm.y, vm.base); y2 = Math.max(vm.y, vm.base); } else { // horizontal bar var halfHeight = vm.height / 2; x1 = Math.min(vm.x, vm.base); x2 = Math.max(vm.x, vm.base); y1 = vm.y - halfHeight; y2 = vm.y + halfHeight; } return { left: x1, top: y1, right: x2, bottom: y2 }; } module.exports = Element.extend({ draw: function() { var ctx = this._chart.ctx; var vm = this._view; var left, right, top, bottom, signX, signY, borderSkipped; var borderWidth = vm.borderWidth; if (!vm.horizontal) { // bar left = vm.x - vm.width / 2; right = vm.x + vm.width / 2; top = vm.y; bottom = vm.base; signX = 1; signY = bottom > top ? 1 : -1; borderSkipped = vm.borderSkipped || 'bottom'; } else { // horizontal bar left = vm.base; right = vm.x; top = vm.y - vm.height / 2; bottom = vm.y + vm.height / 2; signX = right > left ? 1 : -1; signY = 1; borderSkipped = vm.borderSkipped || 'left'; } // Canvas doesn't allow us to stroke inside the width so we can // adjust the sizes to fit if we're setting a stroke on the line if (borderWidth) { // borderWidth shold be less than bar width and bar height. var barSize = Math.min(Math.abs(left - right), Math.abs(top - bottom)); borderWidth = borderWidth > barSize ? barSize : borderWidth; var halfStroke = borderWidth / 2; // Adjust borderWidth when bar top position is near vm.base(zero). var borderLeft = left + (borderSkipped !== 'left' ? halfStroke * signX : 0); var borderRight = right + (borderSkipped !== 'right' ? -halfStroke * signX : 0); var borderTop = top + (borderSkipped !== 'top' ? halfStroke * signY : 0); var borderBottom = bottom + (borderSkipped !== 'bottom' ? -halfStroke * signY : 0); // not become a vertical line? if (borderLeft !== borderRight) { top = borderTop; bottom = borderBottom; } // not become a horizontal line? if (borderTop !== borderBottom) { left = borderLeft; right = borderRight; } } ctx.beginPath(); ctx.fillStyle = vm.backgroundColor; ctx.strokeStyle = vm.borderColor; ctx.lineWidth = borderWidth; // Corner points, from bottom-left to bottom-right clockwise // | 1 2 | // | 0 3 | var corners = [ [left, bottom], [left, top], [right, top], [right, bottom] ]; // Find first (starting) corner with fallback to 'bottom' var borders = ['bottom', 'left', 'top', 'right']; var startCorner = borders.indexOf(borderSkipped, 0); if (startCorner === -1) { startCorner = 0; } function cornerAt(index) { return corners[(startCorner + index) % 4]; } // Draw rectangle from 'startCorner' var corner = cornerAt(0); ctx.moveTo(corner[0], corner[1]); for (var i = 1; i < 4; i++) { corner = cornerAt(i); ctx.lineTo(corner[0], corner[1]); } ctx.fill(); if (borderWidth) { ctx.stroke(); } }, height: function() { var vm = this._view; return vm.base - vm.y; }, inRange: function(mouseX, mouseY) { var inRange = false; if (this._view) { var bounds = getBarBounds(this); inRange = mouseX >= bounds.left && mouseX <= bounds.right && mouseY >= bounds.top && mouseY <= bounds.bottom; } return inRange; }, inLabelRange: function(mouseX, mouseY) { var me = this; if (!me._view) { return false; } var inRange = false; var bounds = getBarBounds(me); if (isVertical(me)) { inRange = mouseX >= bounds.left && mouseX <= bounds.right; } else { inRange = mouseY >= bounds.top && mouseY <= bounds.bottom; } return inRange; }, inXRange: function(mouseX) { var bounds = getBarBounds(this); return mouseX >= bounds.left && mouseX <= bounds.right; }, inYRange: function(mouseY) { var bounds = getBarBounds(this); return mouseY >= bounds.top && mouseY <= bounds.bottom; }, getCenterPoint: function() { var vm = this._view; var x, y; if (isVertical(this)) { x = vm.x; y = (vm.y + vm.base) / 2; } else { x = (vm.x + vm.base) / 2; y = vm.y; } return {x: x, y: y}; }, getArea: function() { var vm = this._view; return vm.width * Math.abs(vm.y - vm.base); }, tooltipPosition: function() { var vm = this._view; return { x: vm.x, y: vm.y }; } }); },{"25":25,"26":26}],40:[function(require,module,exports){ 'use strict'; module.exports = {}; module.exports.Arc = require(36); module.exports.Line = require(37); module.exports.Point = require(38); module.exports.Rectangle = require(39); },{"36":36,"37":37,"38":38,"39":39}],41:[function(require,module,exports){ 'use strict'; var helpers = require(42); /** * @namespace Chart.helpers.canvas */ var exports = module.exports = { /** * Clears the entire canvas associated to the given `chart`. * @param {Chart} chart - The chart for which to clear the canvas. */ clear: function(chart) { chart.ctx.clearRect(0, 0, chart.width, chart.height); }, /** * Creates a "path" for a rectangle with rounded corners at position (x, y) with a * given size (width, height) and the same `radius` for all corners. * @param {CanvasRenderingContext2D} ctx - The canvas 2D Context. * @param {Number} x - The x axis of the coordinate for the rectangle starting point. * @param {Number} y - The y axis of the coordinate for the rectangle starting point. * @param {Number} width - The rectangle's width. * @param {Number} height - The rectangle's height. * @param {Number} radius - The rounded amount (in pixels) for the four corners. * @todo handle `radius` as top-left, top-right, bottom-right, bottom-left array/object? */ roundedRect: function(ctx, x, y, width, height, radius) { if (radius) { var rx = Math.min(radius, width / 2); var ry = Math.min(radius, height / 2); ctx.moveTo(x + rx, y); ctx.lineTo(x + width - rx, y); ctx.quadraticCurveTo(x + width, y, x + width, y + ry); ctx.lineTo(x + width, y + height - ry); ctx.quadraticCurveTo(x + width, y + height, x + width - rx, y + height); ctx.lineTo(x + rx, y + height); ctx.quadraticCurveTo(x, y + height, x, y + height - ry); ctx.lineTo(x, y + ry); ctx.quadraticCurveTo(x, y, x + rx, y); } else { ctx.rect(x, y, width, height); } }, drawPoint: function(ctx, style, radius, x, y) { var type, edgeLength, xOffset, yOffset, height, size; if (style && typeof style === 'object') { type = style.toString(); if (type === '[object HTMLImageElement]' || type === '[object HTMLCanvasElement]') { ctx.drawImage(style, x - style.width / 2, y - style.height / 2, style.width, style.height); return; } } if (isNaN(radius) || radius <= 0) { return; } switch (style) { // Default includes circle default: ctx.beginPath(); ctx.arc(x, y, radius, 0, Math.PI * 2); ctx.closePath(); ctx.fill(); break; case 'triangle': ctx.beginPath(); edgeLength = 3 * radius / Math.sqrt(3); height = edgeLength * Math.sqrt(3) / 2; ctx.moveTo(x - edgeLength / 2, y + height / 3); ctx.lineTo(x + edgeLength / 2, y + height / 3); ctx.lineTo(x, y - 2 * height / 3); ctx.closePath(); ctx.fill(); break; case 'rect': size = 1 / Math.SQRT2 * radius; ctx.beginPath(); ctx.fillRect(x - size, y - size, 2 * size, 2 * size); ctx.strokeRect(x - size, y - size, 2 * size, 2 * size); break; case 'rectRounded': var offset = radius / Math.SQRT2; var leftX = x - offset; var topY = y - offset; var sideSize = Math.SQRT2 * radius; ctx.beginPath(); this.roundedRect(ctx, leftX, topY, sideSize, sideSize, radius / 2); ctx.closePath(); ctx.fill(); break; case 'rectRot': size = 1 / Math.SQRT2 * radius; ctx.beginPath(); ctx.moveTo(x - size, y); ctx.lineTo(x, y + size); ctx.lineTo(x + size, y); ctx.lineTo(x, y - size); ctx.closePath(); ctx.fill(); break; case 'cross': ctx.beginPath(); ctx.moveTo(x, y + radius); ctx.lineTo(x, y - radius); ctx.moveTo(x - radius, y); ctx.lineTo(x + radius, y); ctx.closePath(); break; case 'crossRot': ctx.beginPath(); xOffset = Math.cos(Math.PI / 4) * radius; yOffset = Math.sin(Math.PI / 4) * radius; ctx.moveTo(x - xOffset, y - yOffset); ctx.lineTo(x + xOffset, y + yOffset); ctx.moveTo(x - xOffset, y + yOffset); ctx.lineTo(x + xOffset, y - yOffset); ctx.closePath(); break; case 'star': ctx.beginPath(); ctx.moveTo(x, y + radius); ctx.lineTo(x, y - radius); ctx.moveTo(x - radius, y); ctx.lineTo(x + radius, y); xOffset = Math.cos(Math.PI / 4) * radius; yOffset = Math.sin(Math.PI / 4) * radius; ctx.moveTo(x - xOffset, y - yOffset); ctx.lineTo(x + xOffset, y + yOffset); ctx.moveTo(x - xOffset, y + yOffset); ctx.lineTo(x + xOffset, y - yOffset); ctx.closePath(); break; case 'line': ctx.beginPath(); ctx.moveTo(x - radius, y); ctx.lineTo(x + radius, y); ctx.closePath(); break; case 'dash': ctx.beginPath(); ctx.moveTo(x, y); ctx.lineTo(x + radius, y); ctx.closePath(); break; } ctx.stroke(); }, clipArea: function(ctx, area) { ctx.save(); ctx.beginPath(); ctx.rect(area.left, area.top, area.right - area.left, area.bottom - area.top); ctx.clip(); }, unclipArea: function(ctx) { ctx.restore(); }, lineTo: function(ctx, previous, target, flip) { if (target.steppedLine) { if ((target.steppedLine === 'after' && !flip) || (target.steppedLine !== 'after' && flip)) { ctx.lineTo(previous.x, target.y); } else { ctx.lineTo(target.x, previous.y); } ctx.lineTo(target.x, target.y); return; } if (!target.tension) { ctx.lineTo(target.x, target.y); return; } ctx.bezierCurveTo( flip ? previous.controlPointPreviousX : previous.controlPointNextX, flip ? previous.controlPointPreviousY : previous.controlPointNextY, flip ? target.controlPointNextX : target.controlPointPreviousX, flip ? target.controlPointNextY : target.controlPointPreviousY, target.x, target.y); } }; // DEPRECATIONS /** * Provided for backward compatibility, use Chart.helpers.canvas.clear instead. * @namespace Chart.helpers.clear * @deprecated since version 2.7.0 * @todo remove at version 3 * @private */ helpers.clear = exports.clear; /** * Provided for backward compatibility, use Chart.helpers.canvas.roundedRect instead. * @namespace Chart.helpers.drawRoundedRectangle * @deprecated since version 2.7.0 * @todo remove at version 3 * @private */ helpers.drawRoundedRectangle = function(ctx) { ctx.beginPath(); exports.roundedRect.apply(exports, arguments); ctx.closePath(); }; },{"42":42}],42:[function(require,module,exports){ 'use strict'; /** * @namespace Chart.helpers */ var helpers = { /** * An empty function that can be used, for example, for optional callback. */ noop: function() {}, /** * Returns a unique id, sequentially generated from a global variable. * @returns {Number} * @function */ uid: (function() { var id = 0; return function() { return id++; }; }()), /** * Returns true if `value` is neither null nor undefined, else returns false. * @param {*} value - The value to test. * @returns {Boolean} * @since 2.7.0 */ isNullOrUndef: function(value) { return value === null || typeof value === 'undefined'; }, /** * Returns true if `value` is an array, else returns false. * @param {*} value - The value to test. * @returns {Boolean} * @function */ isArray: Array.isArray ? Array.isArray : function(value) { return Object.prototype.toString.call(value) === '[object Array]'; }, /** * Returns true if `value` is an object (excluding null), else returns false. * @param {*} value - The value to test. * @returns {Boolean} * @since 2.7.0 */ isObject: function(value) { return value !== null && Object.prototype.toString.call(value) === '[object Object]'; }, /** * Returns `value` if defined, else returns `defaultValue`. * @param {*} value - The value to return if defined. * @param {*} defaultValue - The value to return if `value` is undefined. * @returns {*} */ valueOrDefault: function(value, defaultValue) { return typeof value === 'undefined' ? defaultValue : value; }, /** * Returns value at the given `index` in array if defined, else returns `defaultValue`. * @param {Array} value - The array to lookup for value at `index`. * @param {Number} index - The index in `value` to lookup for value. * @param {*} defaultValue - The value to return if `value[index]` is undefined. * @returns {*} */ valueAtIndexOrDefault: function(value, index, defaultValue) { return helpers.valueOrDefault(helpers.isArray(value) ? value[index] : value, defaultValue); }, /** * Calls `fn` with the given `args` in the scope defined by `thisArg` and returns the * value returned by `fn`. If `fn` is not a function, this method returns undefined. * @param {Function} fn - The function to call. * @param {Array|undefined|null} args - The arguments with which `fn` should be called. * @param {Object} [thisArg] - The value of `this` provided for the call to `fn`. * @returns {*} */ callback: function(fn, args, thisArg) { if (fn && typeof fn.call === 'function') { return fn.apply(thisArg, args); } }, /** * Note(SB) for performance sake, this method should only be used when loopable type * is unknown or in none intensive code (not called often and small loopable). Else * it's preferable to use a regular for() loop and save extra function calls. * @param {Object|Array} loopable - The object or array to be iterated. * @param {Function} fn - The function to call for each item. * @param {Object} [thisArg] - The value of `this` provided for the call to `fn`. * @param {Boolean} [reverse] - If true, iterates backward on the loopable. */ each: function(loopable, fn, thisArg, reverse) { var i, len, keys; if (helpers.isArray(loopable)) { len = loopable.length; if (reverse) { for (i = len - 1; i >= 0; i--) { fn.call(thisArg, loopable[i], i); } } else { for (i = 0; i < len; i++) { fn.call(thisArg, loopable[i], i); } } } else if (helpers.isObject(loopable)) { keys = Object.keys(loopable); len = keys.length; for (i = 0; i < len; i++) { fn.call(thisArg, loopable[keys[i]], keys[i]); } } }, /** * Returns true if the `a0` and `a1` arrays have the same content, else returns false. * @see http://stackoverflow.com/a/14853974 * @param {Array} a0 - The array to compare * @param {Array} a1 - The array to compare * @returns {Boolean} */ arrayEquals: function(a0, a1) { var i, ilen, v0, v1; if (!a0 || !a1 || a0.length !== a1.length) { return false; } for (i = 0, ilen = a0.length; i < ilen; ++i) { v0 = a0[i]; v1 = a1[i]; if (v0 instanceof Array && v1 instanceof Array) { if (!helpers.arrayEquals(v0, v1)) { return false; } } else if (v0 !== v1) { // NOTE: two different object instances will never be equal: {x:20} != {x:20} return false; } } return true; }, /** * Returns a deep copy of `source` without keeping references on objects and arrays. * @param {*} source - The value to clone. * @returns {*} */ clone: function(source) { if (helpers.isArray(source)) { return source.map(helpers.clone); } if (helpers.isObject(source)) { var target = {}; var keys = Object.keys(source); var klen = keys.length; var k = 0; for (; k < klen; ++k) { target[keys[k]] = helpers.clone(source[keys[k]]); } return target; } return source; }, /** * The default merger when Chart.helpers.merge is called without merger option. * Note(SB): this method is also used by configMerge and scaleMerge as fallback. * @private */ _merger: function(key, target, source, options) { var tval = target[key]; var sval = source[key]; if (helpers.isObject(tval) && helpers.isObject(sval)) { helpers.merge(tval, sval, options); } else { target[key] = helpers.clone(sval); } }, /** * Merges source[key] in target[key] only if target[key] is undefined. * @private */ _mergerIf: function(key, target, source) { var tval = target[key]; var sval = source[key]; if (helpers.isObject(tval) && helpers.isObject(sval)) { helpers.mergeIf(tval, sval); } else if (!target.hasOwnProperty(key)) { target[key] = helpers.clone(sval); } }, /** * Recursively deep copies `source` properties into `target` with the given `options`. * IMPORTANT: `target` is not cloned and will be updated with `source` properties. * @param {Object} target - The target object in which all sources are merged into. * @param {Object|Array(Object)} source - Object(s) to merge into `target`. * @param {Object} [options] - Merging options: * @param {Function} [options.merger] - The merge method (key, target, source, options) * @returns {Object} The `target` object. */ merge: function(target, source, options) { var sources = helpers.isArray(source) ? source : [source]; var ilen = sources.length; var merge, i, keys, klen, k; if (!helpers.isObject(target)) { return target; } options = options || {}; merge = options.merger || helpers._merger; for (i = 0; i < ilen; ++i) { source = sources[i]; if (!helpers.isObject(source)) { continue; } keys = Object.keys(source); for (k = 0, klen = keys.length; k < klen; ++k) { merge(keys[k], target, source, options); } } return target; }, /** * Recursively deep copies `source` properties into `target` *only* if not defined in target. * IMPORTANT: `target` is not cloned and will be updated with `source` properties. * @param {Object} target - The target object in which all sources are merged into. * @param {Object|Array(Object)} source - Object(s) to merge into `target`. * @returns {Object} The `target` object. */ mergeIf: function(target, source) { return helpers.merge(target, source, {merger: helpers._mergerIf}); }, /** * Applies the contents of two or more objects together into the first object. * @param {Object} target - The target object in which all objects are merged into. * @param {Object} arg1 - Object containing additional properties to merge in target. * @param {Object} argN - Additional objects containing properties to merge in target. * @returns {Object} The `target` object. */ extend: function(target) { var setFn = function(value, key) { target[key] = value; }; for (var i = 1, ilen = arguments.length; i < ilen; ++i) { helpers.each(arguments[i], setFn); } return target; }, /** * Basic javascript inheritance based on the model created in Backbone.js */ inherits: function(extensions) { var me = this; var ChartElement = (extensions && extensions.hasOwnProperty('constructor')) ? extensions.constructor : function() { return me.apply(this, arguments); }; var Surrogate = function() { this.constructor = ChartElement; }; Surrogate.prototype = me.prototype; ChartElement.prototype = new Surrogate(); ChartElement.extend = helpers.inherits; if (extensions) { helpers.extend(ChartElement.prototype, extensions); } ChartElement.__super__ = me.prototype; return ChartElement; } }; module.exports = helpers; // DEPRECATIONS /** * Provided for backward compatibility, use Chart.helpers.callback instead. * @function Chart.helpers.callCallback * @deprecated since version 2.6.0 * @todo remove at version 3 * @private */ helpers.callCallback = helpers.callback; /** * Provided for backward compatibility, use Array.prototype.indexOf instead. * Array.prototype.indexOf compatibility: Chrome, Opera, Safari, FF1.5+, IE9+ * @function Chart.helpers.indexOf * @deprecated since version 2.7.0 * @todo remove at version 3 * @private */ helpers.indexOf = function(array, item, fromIndex) { return Array.prototype.indexOf.call(array, item, fromIndex); }; /** * Provided for backward compatibility, use Chart.helpers.valueOrDefault instead. * @function Chart.helpers.getValueOrDefault * @deprecated since version 2.7.0 * @todo remove at version 3 * @private */ helpers.getValueOrDefault = helpers.valueOrDefault; /** * Provided for backward compatibility, use Chart.helpers.valueAtIndexOrDefault instead. * @function Chart.helpers.getValueAtIndexOrDefault * @deprecated since version 2.7.0 * @todo remove at version 3 * @private */ helpers.getValueAtIndexOrDefault = helpers.valueAtIndexOrDefault; },{}],43:[function(require,module,exports){ 'use strict'; var helpers = require(42); /** * Easing functions adapted from Robert Penner's easing equations. * @namespace Chart.helpers.easingEffects * @see http://www.robertpenner.com/easing/ */ var effects = { linear: function(t) { return t; }, easeInQuad: function(t) { return t * t; }, easeOutQuad: function(t) { return -t * (t - 2); }, easeInOutQuad: function(t) { if ((t /= 0.5) < 1) { return 0.5 * t * t; } return -0.5 * ((--t) * (t - 2) - 1); }, easeInCubic: function(t) { return t * t * t; }, easeOutCubic: function(t) { return (t = t - 1) * t * t + 1; }, easeInOutCubic: function(t) { if ((t /= 0.5) < 1) { return 0.5 * t * t * t; } return 0.5 * ((t -= 2) * t * t + 2); }, easeInQuart: function(t) { return t * t * t * t; }, easeOutQuart: function(t) { return -((t = t - 1) * t * t * t - 1); }, easeInOutQuart: function(t) { if ((t /= 0.5) < 1) { return 0.5 * t * t * t * t; } return -0.5 * ((t -= 2) * t * t * t - 2); }, easeInQuint: function(t) { return t * t * t * t * t; }, easeOutQuint: function(t) { return (t = t - 1) * t * t * t * t + 1; }, easeInOutQuint: function(t) { if ((t /= 0.5) < 1) { return 0.5 * t * t * t * t * t; } return 0.5 * ((t -= 2) * t * t * t * t + 2); }, easeInSine: function(t) { return -Math.cos(t * (Math.PI / 2)) + 1; }, easeOutSine: function(t) { return Math.sin(t * (Math.PI / 2)); }, easeInOutSine: function(t) { return -0.5 * (Math.cos(Math.PI * t) - 1); }, easeInExpo: function(t) { return (t === 0) ? 0 : Math.pow(2, 10 * (t - 1)); }, easeOutExpo: function(t) { return (t === 1) ? 1 : -Math.pow(2, -10 * t) + 1; }, easeInOutExpo: function(t) { if (t === 0) { return 0; } if (t === 1) { return 1; } if ((t /= 0.5) < 1) { return 0.5 * Math.pow(2, 10 * (t - 1)); } return 0.5 * (-Math.pow(2, -10 * --t) + 2); }, easeInCirc: function(t) { if (t >= 1) { return t; } return -(Math.sqrt(1 - t * t) - 1); }, easeOutCirc: function(t) { return Math.sqrt(1 - (t = t - 1) * t); }, easeInOutCirc: function(t) { if ((t /= 0.5) < 1) { return -0.5 * (Math.sqrt(1 - t * t) - 1); } return 0.5 * (Math.sqrt(1 - (t -= 2) * t) + 1); }, easeInElastic: function(t) { var s = 1.70158; var p = 0; var a = 1; if (t === 0) { return 0; } if (t === 1) { return 1; } if (!p) { p = 0.3; } if (a < 1) { a = 1; s = p / 4; } else { s = p / (2 * Math.PI) * Math.asin(1 / a); } return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p)); }, easeOutElastic: function(t) { var s = 1.70158; var p = 0; var a = 1; if (t === 0) { return 0; } if (t === 1) { return 1; } if (!p) { p = 0.3; } if (a < 1) { a = 1; s = p / 4; } else { s = p / (2 * Math.PI) * Math.asin(1 / a); } return a * Math.pow(2, -10 * t) * Math.sin((t - s) * (2 * Math.PI) / p) + 1; }, easeInOutElastic: function(t) { var s = 1.70158; var p = 0; var a = 1; if (t === 0) { return 0; } if ((t /= 0.5) === 2) { return 1; } if (!p) { p = 0.45; } if (a < 1) { a = 1; s = p / 4; } else { s = p / (2 * Math.PI) * Math.asin(1 / a); } if (t < 1) { return -0.5 * (a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p)); } return a * Math.pow(2, -10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p) * 0.5 + 1; }, easeInBack: function(t) { var s = 1.70158; return t * t * ((s + 1) * t - s); }, easeOutBack: function(t) { var s = 1.70158; return (t = t - 1) * t * ((s + 1) * t + s) + 1; }, easeInOutBack: function(t) { var s = 1.70158; if ((t /= 0.5) < 1) { return 0.5 * (t * t * (((s *= (1.525)) + 1) * t - s)); } return 0.5 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2); }, easeInBounce: function(t) { return 1 - effects.easeOutBounce(1 - t); }, easeOutBounce: function(t) { if (t < (1 / 2.75)) { return 7.5625 * t * t; } if (t < (2 / 2.75)) { return 7.5625 * (t -= (1.5 / 2.75)) * t + 0.75; } if (t < (2.5 / 2.75)) { return 7.5625 * (t -= (2.25 / 2.75)) * t + 0.9375; } return 7.5625 * (t -= (2.625 / 2.75)) * t + 0.984375; }, easeInOutBounce: function(t) { if (t < 0.5) { return effects.easeInBounce(t * 2) * 0.5; } return effects.easeOutBounce(t * 2 - 1) * 0.5 + 0.5; } }; module.exports = { effects: effects }; // DEPRECATIONS /** * Provided for backward compatibility, use Chart.helpers.easing.effects instead. * @function Chart.helpers.easingEffects * @deprecated since version 2.7.0 * @todo remove at version 3 * @private */ helpers.easingEffects = effects; },{"42":42}],44:[function(require,module,exports){ 'use strict'; var helpers = require(42); /** * @alias Chart.helpers.options * @namespace */ module.exports = { /** * Converts the given line height `value` in pixels for a specific font `size`. * @param {Number|String} value - The lineHeight to parse (eg. 1.6, '14px', '75%', '1.6em'). * @param {Number} size - The font size (in pixels) used to resolve relative `value`. * @returns {Number} The effective line height in pixels (size * 1.2 if value is invalid). * @see https://developer.mozilla.org/en-US/docs/Web/CSS/line-height * @since 2.7.0 */ toLineHeight: function(value, size) { var matches = ('' + value).match(/^(normal|(\d+(?:\.\d+)?)(px|em|%)?)$/); if (!matches || matches[1] === 'normal') { return size * 1.2; } value = +matches[2]; switch (matches[3]) { case 'px': return value; case '%': value /= 100; break; default: break; } return size * value; }, /** * Converts the given value into a padding object with pre-computed width/height. * @param {Number|Object} value - If a number, set the value to all TRBL component, * else, if and object, use defined properties and sets undefined ones to 0. * @returns {Object} The padding values (top, right, bottom, left, width, height) * @since 2.7.0 */ toPadding: function(value) { var t, r, b, l; if (helpers.isObject(value)) { t = +value.top || 0; r = +value.right || 0; b = +value.bottom || 0; l = +value.left || 0; } else { t = r = b = l = +value || 0; } return { top: t, right: r, bottom: b, left: l, height: t + b, width: l + r }; }, /** * Evaluates the given `inputs` sequentially and returns the first defined value. * @param {Array[]} inputs - An array of values, falling back to the last value. * @param {Object} [context] - If defined and the current value is a function, the value * is called with `context` as first argument and the result becomes the new input. * @param {Number} [index] - If defined and the current value is an array, the value * at `index` become the new input. * @since 2.7.0 */ resolve: function(inputs, context, index) { var i, ilen, value; for (i = 0, ilen = inputs.length; i < ilen; ++i) { value = inputs[i]; if (value === undefined) { continue; } if (context !== undefined && typeof value === 'function') { value = value(context); } if (index !== undefined && helpers.isArray(value)) { value = value[index]; } if (value !== undefined) { return value; } } } }; },{"42":42}],45:[function(require,module,exports){ 'use strict'; module.exports = require(42); module.exports.easing = require(43); module.exports.canvas = require(41); module.exports.options = require(44); },{"41":41,"42":42,"43":43,"44":44}],46:[function(require,module,exports){ /** * Platform fallback implementation (minimal). * @see https://github.com/chartjs/Chart.js/pull/4591#issuecomment-319575939 */ module.exports = { acquireContext: function(item) { if (item && item.canvas) { // Support for any object associated to a canvas (including a context2d) item = item.canvas; } return item && item.getContext('2d') || null; } }; },{}],47:[function(require,module,exports){ /** * Chart.Platform implementation for targeting a web browser */ 'use strict'; var helpers = require(45); var EXPANDO_KEY = '$chartjs'; var CSS_PREFIX = 'chartjs-'; var CSS_RENDER_MONITOR = CSS_PREFIX + 'render-monitor'; var CSS_RENDER_ANIMATION = CSS_PREFIX + 'render-animation'; var ANIMATION_START_EVENTS = ['animationstart', 'webkitAnimationStart']; /** * DOM event types -> Chart.js event types. * Note: only events with different types are mapped. * @see https://developer.mozilla.org/en-US/docs/Web/Events */ var EVENT_TYPES = { touchstart: 'mousedown', touchmove: 'mousemove', touchend: 'mouseup', pointerenter: 'mouseenter', pointerdown: 'mousedown', pointermove: 'mousemove', pointerup: 'mouseup', pointerleave: 'mouseout', pointerout: 'mouseout' }; /** * The "used" size is the final value of a dimension property after all calculations have * been performed. This method uses the computed style of `element` but returns undefined * if the computed style is not expressed in pixels. That can happen in some cases where * `element` has a size relative to its parent and this last one is not yet displayed, * for example because of `display: none` on a parent node. * @see https://developer.mozilla.org/en-US/docs/Web/CSS/used_value * @returns {Number} Size in pixels or undefined if unknown. */ function readUsedSize(element, property) { var value = helpers.getStyle(element, property); var matches = value && value.match(/^(\d+)(\.\d+)?px$/); return matches ? Number(matches[1]) : undefined; } /** * Initializes the canvas style and render size without modifying the canvas display size, * since responsiveness is handled by the controller.resize() method. The config is used * to determine the aspect ratio to apply in case no explicit height has been specified. */ function initCanvas(canvas, config) { var style = canvas.style; // NOTE(SB) canvas.getAttribute('width') !== canvas.width: in the first case it // returns null or '' if no explicit value has been set to the canvas attribute. var renderHeight = canvas.getAttribute('height'); var renderWidth = canvas.getAttribute('width'); // Chart.js modifies some canvas values that we want to restore on destroy canvas[EXPANDO_KEY] = { initial: { height: renderHeight, width: renderWidth, style: { display: style.display, height: style.height, width: style.width } } }; // Force canvas to display as block to avoid extra space caused by inline // elements, which would interfere with the responsive resize process. // https://github.com/chartjs/Chart.js/issues/2538 style.display = style.display || 'block'; if (renderWidth === null || renderWidth === '') { var displayWidth = readUsedSize(canvas, 'width'); if (displayWidth !== undefined) { canvas.width = displayWidth; } } if (renderHeight === null || renderHeight === '') { if (canvas.style.height === '') { // If no explicit render height and style height, let's apply the aspect ratio, // which one can be specified by the user but also by charts as default option // (i.e. options.aspectRatio). If not specified, use canvas aspect ratio of 2. canvas.height = canvas.width / (config.options.aspectRatio || 2); } else { var displayHeight = readUsedSize(canvas, 'height'); if (displayWidth !== undefined) { canvas.height = displayHeight; } } } return canvas; } /** * Detects support for options object argument in addEventListener. * https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Safely_detecting_option_support * @private */ var supportsEventListenerOptions = (function() { var supports = false; try { var options = Object.defineProperty({}, 'passive', { get: function() { supports = true; } }); window.addEventListener('e', null, options); } catch (e) { // continue regardless of error } return supports; }()); // Default passive to true as expected by Chrome for 'touchstart' and 'touchend' events. // https://github.com/chartjs/Chart.js/issues/4287 var eventListenerOptions = supportsEventListenerOptions ? {passive: true} : false; function addEventListener(node, type, listener) { node.addEventListener(type, listener, eventListenerOptions); } function removeEventListener(node, type, listener) { node.removeEventListener(type, listener, eventListenerOptions); } function createEvent(type, chart, x, y, nativeEvent) { return { type: type, chart: chart, native: nativeEvent || null, x: x !== undefined ? x : null, y: y !== undefined ? y : null, }; } function fromNativeEvent(event, chart) { var type = EVENT_TYPES[event.type] || event.type; var pos = helpers.getRelativePosition(event, chart); return createEvent(type, chart, pos.x, pos.y, event); } function throttled(fn, thisArg) { var ticking = false; var args = []; return function() { args = Array.prototype.slice.call(arguments); thisArg = thisArg || this; if (!ticking) { ticking = true; helpers.requestAnimFrame.call(window, function() { ticking = false; fn.apply(thisArg, args); }); } }; } // Implementation based on https://github.com/marcj/css-element-queries function createResizer(handler) { var resizer = document.createElement('div'); var cls = CSS_PREFIX + 'size-monitor'; var maxSize = 1000000; var style = 'position:absolute;' + 'left:0;' + 'top:0;' + 'right:0;' + 'bottom:0;' + 'overflow:hidden;' + 'pointer-events:none;' + 'visibility:hidden;' + 'z-index:-1;'; resizer.style.cssText = style; resizer.className = cls; resizer.innerHTML = '
' + '
' + '
' + '
' + '
' + '
' + '
' + '
'; var expand = resizer.childNodes[0]; var shrink = resizer.childNodes[1]; resizer._reset = function() { expand.scrollLeft = maxSize; expand.scrollTop = maxSize; shrink.scrollLeft = maxSize; shrink.scrollTop = maxSize; }; var onScroll = function() { resizer._reset(); handler(); }; addEventListener(expand, 'scroll', onScroll.bind(expand, 'expand')); addEventListener(shrink, 'scroll', onScroll.bind(shrink, 'shrink')); return resizer; } // https://davidwalsh.name/detect-node-insertion function watchForRender(node, handler) { var expando = node[EXPANDO_KEY] || (node[EXPANDO_KEY] = {}); var proxy = expando.renderProxy = function(e) { if (e.animationName === CSS_RENDER_ANIMATION) { handler(); } }; helpers.each(ANIMATION_START_EVENTS, function(type) { addEventListener(node, type, proxy); }); // #4737: Chrome might skip the CSS animation when the CSS_RENDER_MONITOR class // is removed then added back immediately (same animation frame?). Accessing the // `offsetParent` property will force a reflow and re-evaluate the CSS animation. // https://gist.github.com/paulirish/5d52fb081b3570c81e3a#box-metrics // https://github.com/chartjs/Chart.js/issues/4737 expando.reflow = !!node.offsetParent; node.classList.add(CSS_RENDER_MONITOR); } function unwatchForRender(node) { var expando = node[EXPANDO_KEY] || {}; var proxy = expando.renderProxy; if (proxy) { helpers.each(ANIMATION_START_EVENTS, function(type) { removeEventListener(node, type, proxy); }); delete expando.renderProxy; } node.classList.remove(CSS_RENDER_MONITOR); } function addResizeListener(node, listener, chart) { var expando = node[EXPANDO_KEY] || (node[EXPANDO_KEY] = {}); // Let's keep track of this added resizer and thus avoid DOM query when removing it. var resizer = expando.resizer = createResizer(throttled(function() { if (expando.resizer) { return listener(createEvent('resize', chart)); } })); // The resizer needs to be attached to the node parent, so we first need to be // sure that `node` is attached to the DOM before injecting the resizer element. watchForRender(node, function() { if (expando.resizer) { var container = node.parentNode; if (container && container !== resizer.parentNode) { container.insertBefore(resizer, container.firstChild); } // The container size might have changed, let's reset the resizer state. resizer._reset(); } }); } function removeResizeListener(node) { var expando = node[EXPANDO_KEY] || {}; var resizer = expando.resizer; delete expando.resizer; unwatchForRender(node); if (resizer && resizer.parentNode) { resizer.parentNode.removeChild(resizer); } } function injectCSS(platform, css) { // http://stackoverflow.com/q/3922139 var style = platform._style || document.createElement('style'); if (!platform._style) { platform._style = style; css = '/* Chart.js */\n' + css; style.setAttribute('type', 'text/css'); document.getElementsByTagName('head')[0].appendChild(style); } style.appendChild(document.createTextNode(css)); } module.exports = { /** * This property holds whether this platform is enabled for the current environment. * Currently used by platform.js to select the proper implementation. * @private */ _enabled: typeof window !== 'undefined' && typeof document !== 'undefined', initialize: function() { var keyframes = 'from{opacity:0.99}to{opacity:1}'; injectCSS(this, // DOM rendering detection // https://davidwalsh.name/detect-node-insertion '@-webkit-keyframes ' + CSS_RENDER_ANIMATION + '{' + keyframes + '}' + '@keyframes ' + CSS_RENDER_ANIMATION + '{' + keyframes + '}' + '.' + CSS_RENDER_MONITOR + '{' + '-webkit-animation:' + CSS_RENDER_ANIMATION + ' 0.001s;' + 'animation:' + CSS_RENDER_ANIMATION + ' 0.001s;' + '}' ); }, acquireContext: function(item, config) { if (typeof item === 'string') { item = document.getElementById(item); } else if (item.length) { // Support for array based queries (such as jQuery) item = item[0]; } if (item && item.canvas) { // Support for any object associated to a canvas (including a context2d) item = item.canvas; } // To prevent canvas fingerprinting, some add-ons undefine the getContext // method, for example: https://github.com/kkapsner/CanvasBlocker // https://github.com/chartjs/Chart.js/issues/2807 var context = item && item.getContext && item.getContext('2d'); // `instanceof HTMLCanvasElement/CanvasRenderingContext2D` fails when the item is // inside an iframe or when running in a protected environment. We could guess the // types from their toString() value but let's keep things flexible and assume it's // a sufficient condition if the item has a context2D which has item as `canvas`. // https://github.com/chartjs/Chart.js/issues/3887 // https://github.com/chartjs/Chart.js/issues/4102 // https://github.com/chartjs/Chart.js/issues/4152 if (context && context.canvas === item) { initCanvas(item, config); return context; } return null; }, releaseContext: function(context) { var canvas = context.canvas; if (!canvas[EXPANDO_KEY]) { return; } var initial = canvas[EXPANDO_KEY].initial; ['height', 'width'].forEach(function(prop) { var value = initial[prop]; if (helpers.isNullOrUndef(value)) { canvas.removeAttribute(prop); } else { canvas.setAttribute(prop, value); } }); helpers.each(initial.style || {}, function(value, key) { canvas.style[key] = value; }); // The canvas render size might have been changed (and thus the state stack discarded), // we can't use save() and restore() to restore the initial state. So make sure that at // least the canvas context is reset to the default state by setting the canvas width. // https://www.w3.org/TR/2011/WD-html5-20110525/the-canvas-element.html canvas.width = canvas.width; delete canvas[EXPANDO_KEY]; }, addEventListener: function(chart, type, listener) { var canvas = chart.canvas; if (type === 'resize') { // Note: the resize event is not supported on all browsers. addResizeListener(canvas, listener, chart); return; } var expando = listener[EXPANDO_KEY] || (listener[EXPANDO_KEY] = {}); var proxies = expando.proxies || (expando.proxies = {}); var proxy = proxies[chart.id + '_' + type] = function(event) { listener(fromNativeEvent(event, chart)); }; addEventListener(canvas, type, proxy); }, removeEventListener: function(chart, type, listener) { var canvas = chart.canvas; if (type === 'resize') { // Note: the resize event is not supported on all browsers. removeResizeListener(canvas, listener); return; } var expando = listener[EXPANDO_KEY] || {}; var proxies = expando.proxies || {}; var proxy = proxies[chart.id + '_' + type]; if (!proxy) { return; } removeEventListener(canvas, type, proxy); } }; // DEPRECATIONS /** * Provided for backward compatibility, use EventTarget.addEventListener instead. * EventTarget.addEventListener compatibility: Chrome, Opera 7, Safari, FF1.5+, IE9+ * @see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener * @function Chart.helpers.addEvent * @deprecated since version 2.7.0 * @todo remove at version 3 * @private */ helpers.addEvent = addEventListener; /** * Provided for backward compatibility, use EventTarget.removeEventListener instead. * EventTarget.removeEventListener compatibility: Chrome, Opera 7, Safari, FF1.5+, IE9+ * @see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener * @function Chart.helpers.removeEvent * @deprecated since version 2.7.0 * @todo remove at version 3 * @private */ helpers.removeEvent = removeEventListener; },{"45":45}],48:[function(require,module,exports){ 'use strict'; var helpers = require(45); var basic = require(46); var dom = require(47); // @TODO Make possible to select another platform at build time. var implementation = dom._enabled ? dom : basic; /** * @namespace Chart.platform * @see https://chartjs.gitbooks.io/proposals/content/Platform.html * @since 2.4.0 */ module.exports = helpers.extend({ /** * @since 2.7.0 */ initialize: function() {}, /** * Called at chart construction time, returns a context2d instance implementing * the [W3C Canvas 2D Context API standard]{@link https://www.w3.org/TR/2dcontext/}. * @param {*} item - The native item from which to acquire context (platform specific) * @param {Object} options - The chart options * @returns {CanvasRenderingContext2D} context2d instance */ acquireContext: function() {}, /** * Called at chart destruction time, releases any resources associated to the context * previously returned by the acquireContext() method. * @param {CanvasRenderingContext2D} context - The context2d instance * @returns {Boolean} true if the method succeeded, else false */ releaseContext: function() {}, /** * Registers the specified listener on the given chart. * @param {Chart} chart - Chart from which to listen for event * @param {String} type - The ({@link IEvent}) type to listen for * @param {Function} listener - Receives a notification (an object that implements * the {@link IEvent} interface) when an event of the specified type occurs. */ addEventListener: function() {}, /** * Removes the specified listener previously registered with addEventListener. * @param {Chart} chart -Chart from which to remove the listener * @param {String} type - The ({@link IEvent}) type to remove * @param {Function} listener - The listener function to remove from the event target. */ removeEventListener: function() {} }, implementation); /** * @interface IPlatform * Allows abstracting platform dependencies away from the chart * @borrows Chart.platform.acquireContext as acquireContext * @borrows Chart.platform.releaseContext as releaseContext * @borrows Chart.platform.addEventListener as addEventListener * @borrows Chart.platform.removeEventListener as removeEventListener */ /** * @interface IEvent * @prop {String} type - The event type name, possible values are: * 'contextmenu', 'mouseenter', 'mousedown', 'mousemove', 'mouseup', 'mouseout', * 'click', 'dblclick', 'keydown', 'keypress', 'keyup' and 'resize' * @prop {*} native - The original native event (null for emulated events, e.g. 'resize') * @prop {Number} x - The mouse x position, relative to the canvas (null for incompatible events) * @prop {Number} y - The mouse y position, relative to the canvas (null for incompatible events) */ },{"45":45,"46":46,"47":47}],49:[function(require,module,exports){ /** * Plugin based on discussion from the following Chart.js issues: * @see https://github.com/chartjs/Chart.js/issues/2380#issuecomment-279961569 * @see https://github.com/chartjs/Chart.js/issues/2440#issuecomment-256461897 */ 'use strict'; var defaults = require(25); var elements = require(40); var helpers = require(45); defaults._set('global', { plugins: { filler: { propagate: true } } }); module.exports = function() { var mappers = { dataset: function(source) { var index = source.fill; var chart = source.chart; var meta = chart.getDatasetMeta(index); var visible = meta && chart.isDatasetVisible(index); var points = (visible && meta.dataset._children) || []; var length = points.length || 0; return !length ? null : function(point, i) { return (i < length && points[i]._view) || null; }; }, boundary: function(source) { var boundary = source.boundary; var x = boundary ? boundary.x : null; var y = boundary ? boundary.y : null; return function(point) { return { x: x === null ? point.x : x, y: y === null ? point.y : y, }; }; } }; // @todo if (fill[0] === '#') function decodeFill(el, index, count) { var model = el._model || {}; var fill = model.fill; var target; if (fill === undefined) { fill = !!model.backgroundColor; } if (fill === false || fill === null) { return false; } if (fill === true) { return 'origin'; } target = parseFloat(fill, 10); if (isFinite(target) && Math.floor(target) === target) { if (fill[0] === '-' || fill[0] === '+') { target = index + target; } if (target === index || target < 0 || target >= count) { return false; } return target; } switch (fill) { // compatibility case 'bottom': return 'start'; case 'top': return 'end'; case 'zero': return 'origin'; // supported boundaries case 'origin': case 'start': case 'end': return fill; // invalid fill values default: return false; } } function computeBoundary(source) { var model = source.el._model || {}; var scale = source.el._scale || {}; var fill = source.fill; var target = null; var horizontal; if (isFinite(fill)) { return null; } // Backward compatibility: until v3, we still need to support boundary values set on // the model (scaleTop, scaleBottom and scaleZero) because some external plugins and // controllers might still use it (e.g. the Smith chart). if (fill === 'start') { target = model.scaleBottom === undefined ? scale.bottom : model.scaleBottom; } else if (fill === 'end') { target = model.scaleTop === undefined ? scale.top : model.scaleTop; } else if (model.scaleZero !== undefined) { target = model.scaleZero; } else if (scale.getBasePosition) { target = scale.getBasePosition(); } else if (scale.getBasePixel) { target = scale.getBasePixel(); } if (target !== undefined && target !== null) { if (target.x !== undefined && target.y !== undefined) { return target; } if (typeof target === 'number' && isFinite(target)) { horizontal = scale.isHorizontal(); return { x: horizontal ? target : null, y: horizontal ? null : target }; } } return null; } function resolveTarget(sources, index, propagate) { var source = sources[index]; var fill = source.fill; var visited = [index]; var target; if (!propagate) { return fill; } while (fill !== false && visited.indexOf(fill) === -1) { if (!isFinite(fill)) { return fill; } target = sources[fill]; if (!target) { return false; } if (target.visible) { return fill; } visited.push(fill); fill = target.fill; } return false; } function createMapper(source) { var fill = source.fill; var type = 'dataset'; if (fill === false) { return null; } if (!isFinite(fill)) { type = 'boundary'; } return mappers[type](source); } function isDrawable(point) { return point && !point.skip; } function drawArea(ctx, curve0, curve1, len0, len1) { var i; if (!len0 || !len1) { return; } // building first area curve (normal) ctx.moveTo(curve0[0].x, curve0[0].y); for (i = 1; i < len0; ++i) { helpers.canvas.lineTo(ctx, curve0[i - 1], curve0[i]); } // joining the two area curves ctx.lineTo(curve1[len1 - 1].x, curve1[len1 - 1].y); // building opposite area curve (reverse) for (i = len1 - 1; i > 0; --i) { helpers.canvas.lineTo(ctx, curve1[i], curve1[i - 1], true); } } function doFill(ctx, points, mapper, view, color, loop) { var count = points.length; var span = view.spanGaps; var curve0 = []; var curve1 = []; var len0 = 0; var len1 = 0; var i, ilen, index, p0, p1, d0, d1; ctx.beginPath(); for (i = 0, ilen = (count + !!loop); i < ilen; ++i) { index = i % count; p0 = points[index]._view; p1 = mapper(p0, index, view); d0 = isDrawable(p0); d1 = isDrawable(p1); if (d0 && d1) { len0 = curve0.push(p0); len1 = curve1.push(p1); } else if (len0 && len1) { if (!span) { drawArea(ctx, curve0, curve1, len0, len1); len0 = len1 = 0; curve0 = []; curve1 = []; } else { if (d0) { curve0.push(p0); } if (d1) { curve1.push(p1); } } } } drawArea(ctx, curve0, curve1, len0, len1); ctx.closePath(); ctx.fillStyle = color; ctx.fill(); } return { id: 'filler', afterDatasetsUpdate: function(chart, options) { var count = (chart.data.datasets || []).length; var propagate = options.propagate; var sources = []; var meta, i, el, source; for (i = 0; i < count; ++i) { meta = chart.getDatasetMeta(i); el = meta.dataset; source = null; if (el && el._model && el instanceof elements.Line) { source = { visible: chart.isDatasetVisible(i), fill: decodeFill(el, i, count), chart: chart, el: el }; } meta.$filler = source; sources.push(source); } for (i = 0; i < count; ++i) { source = sources[i]; if (!source) { continue; } source.fill = resolveTarget(sources, i, propagate); source.boundary = computeBoundary(source); source.mapper = createMapper(source); } }, beforeDatasetDraw: function(chart, args) { var meta = args.meta.$filler; if (!meta) { return; } var ctx = chart.ctx; var el = meta.el; var view = el._view; var points = el._children || []; var mapper = meta.mapper; var color = view.backgroundColor || defaults.global.defaultColor; if (mapper && color && points.length) { helpers.canvas.clipArea(ctx, chart.chartArea); doFill(ctx, points, mapper, view, color, el._loop); helpers.canvas.unclipArea(ctx); } } }; }; },{"25":25,"40":40,"45":45}],50:[function(require,module,exports){ 'use strict'; var defaults = require(25); var Element = require(26); var helpers = require(45); defaults._set('global', { legend: { display: true, position: 'top', fullWidth: true, reverse: false, weight: 1000, // a callback that will handle onClick: function(e, legendItem) { var index = legendItem.datasetIndex; var ci = this.chart; var meta = ci.getDatasetMeta(index); // See controller.isDatasetVisible comment meta.hidden = meta.hidden === null ? !ci.data.datasets[index].hidden : null; // We hid a dataset ... rerender the chart ci.update(); }, onHover: null, labels: { boxWidth: 40, padding: 10, // Generates labels shown in the legend // Valid properties to return: // text : text to display // fillStyle : fill of coloured box // strokeStyle: stroke of coloured box // hidden : if this legend item refers to a hidden item // lineCap : cap style for line // lineDash // lineDashOffset : // lineJoin : // lineWidth : generateLabels: function(chart) { var data = chart.data; return helpers.isArray(data.datasets) ? data.datasets.map(function(dataset, i) { return { text: dataset.label, fillStyle: (!helpers.isArray(dataset.backgroundColor) ? dataset.backgroundColor : dataset.backgroundColor[0]), hidden: !chart.isDatasetVisible(i), lineCap: dataset.borderCapStyle, lineDash: dataset.borderDash, lineDashOffset: dataset.borderDashOffset, lineJoin: dataset.borderJoinStyle, lineWidth: dataset.borderWidth, strokeStyle: dataset.borderColor, pointStyle: dataset.pointStyle, // Below is extra data used for toggling the datasets datasetIndex: i }; }, this) : []; } } }, legendCallback: function(chart) { var text = []; text.push('
    '); for (var i = 0; i < chart.data.datasets.length; i++) { text.push('
  • '); if (chart.data.datasets[i].label) { text.push(chart.data.datasets[i].label); } text.push('
  • '); } text.push('
'); return text.join(''); } }); module.exports = function(Chart) { var layout = Chart.layoutService; var noop = helpers.noop; /** * Helper function to get the box width based on the usePointStyle option * @param labelopts {Object} the label options on the legend * @param fontSize {Number} the label font size * @return {Number} width of the color box area */ function getBoxWidth(labelOpts, fontSize) { return labelOpts.usePointStyle ? fontSize * Math.SQRT2 : labelOpts.boxWidth; } Chart.Legend = Element.extend({ initialize: function(config) { helpers.extend(this, config); // Contains hit boxes for each dataset (in dataset order) this.legendHitBoxes = []; // Are we in doughnut mode which has a different data type this.doughnutMode = false; }, // These methods are ordered by lifecycle. Utilities then follow. // Any function defined here is inherited by all legend types. // Any function can be extended by the legend type beforeUpdate: noop, update: function(maxWidth, maxHeight, margins) { var me = this; // Update Lifecycle - Probably don't want to ever extend or overwrite this function ;) me.beforeUpdate(); // Absorb the master measurements me.maxWidth = maxWidth; me.maxHeight = maxHeight; me.margins = margins; // Dimensions me.beforeSetDimensions(); me.setDimensions(); me.afterSetDimensions(); // Labels me.beforeBuildLabels(); me.buildLabels(); me.afterBuildLabels(); // Fit me.beforeFit(); me.fit(); me.afterFit(); // me.afterUpdate(); return me.minSize; }, afterUpdate: noop, // beforeSetDimensions: noop, setDimensions: function() { var me = this; // Set the unconstrained dimension before label rotation if (me.isHorizontal()) { // Reset position before calculating rotation me.width = me.maxWidth; me.left = 0; me.right = me.width; } else { me.height = me.maxHeight; // Reset position before calculating rotation me.top = 0; me.bottom = me.height; } // Reset padding me.paddingLeft = 0; me.paddingTop = 0; me.paddingRight = 0; me.paddingBottom = 0; // Reset minSize me.minSize = { width: 0, height: 0 }; }, afterSetDimensions: noop, // beforeBuildLabels: noop, buildLabels: function() { var me = this; var labelOpts = me.options.labels || {}; var legendItems = helpers.callback(labelOpts.generateLabels, [me.chart], me) || []; if (labelOpts.filter) { legendItems = legendItems.filter(function(item) { return labelOpts.filter(item, me.chart.data); }); } if (me.options.reverse) { legendItems.reverse(); } me.legendItems = legendItems; }, afterBuildLabels: noop, // beforeFit: noop, fit: function() { var me = this; var opts = me.options; var labelOpts = opts.labels; var display = opts.display; var ctx = me.ctx; var globalDefault = defaults.global; var valueOrDefault = helpers.valueOrDefault; var fontSize = valueOrDefault(labelOpts.fontSize, globalDefault.defaultFontSize); var fontStyle = valueOrDefault(labelOpts.fontStyle, globalDefault.defaultFontStyle); var fontFamily = valueOrDefault(labelOpts.fontFamily, globalDefault.defaultFontFamily); var labelFont = helpers.fontString(fontSize, fontStyle, fontFamily); // Reset hit boxes var hitboxes = me.legendHitBoxes = []; var minSize = me.minSize; var isHorizontal = me.isHorizontal(); if (isHorizontal) { minSize.width = me.maxWidth; // fill all the width minSize.height = display ? 10 : 0; } else { minSize.width = display ? 10 : 0; minSize.height = me.maxHeight; // fill all the height } // Increase sizes here if (display) { ctx.font = labelFont; if (isHorizontal) { // Labels // Width of each line of legend boxes. Labels wrap onto multiple lines when there are too many to fit on one var lineWidths = me.lineWidths = [0]; var totalHeight = me.legendItems.length ? fontSize + (labelOpts.padding) : 0; ctx.textAlign = 'left'; ctx.textBaseline = 'top'; helpers.each(me.legendItems, function(legendItem, i) { var boxWidth = getBoxWidth(labelOpts, fontSize); var width = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width; if (lineWidths[lineWidths.length - 1] + width + labelOpts.padding >= me.width) { totalHeight += fontSize + (labelOpts.padding); lineWidths[lineWidths.length] = me.left; } // Store the hitbox width and height here. Final position will be updated in `draw` hitboxes[i] = { left: 0, top: 0, width: width, height: fontSize }; lineWidths[lineWidths.length - 1] += width + labelOpts.padding; }); minSize.height += totalHeight; } else { var vPadding = labelOpts.padding; var columnWidths = me.columnWidths = []; var totalWidth = labelOpts.padding; var currentColWidth = 0; var currentColHeight = 0; var itemHeight = fontSize + vPadding; helpers.each(me.legendItems, function(legendItem, i) { var boxWidth = getBoxWidth(labelOpts, fontSize); var itemWidth = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width; // If too tall, go to new column if (currentColHeight + itemHeight > minSize.height) { totalWidth += currentColWidth + labelOpts.padding; columnWidths.push(currentColWidth); // previous column width currentColWidth = 0; currentColHeight = 0; } // Get max width currentColWidth = Math.max(currentColWidth, itemWidth); currentColHeight += itemHeight; // Store the hitbox width and height here. Final position will be updated in `draw` hitboxes[i] = { left: 0, top: 0, width: itemWidth, height: fontSize }; }); totalWidth += currentColWidth; columnWidths.push(currentColWidth); minSize.width += totalWidth; } } me.width = minSize.width; me.height = minSize.height; }, afterFit: noop, // Shared Methods isHorizontal: function() { return this.options.position === 'top' || this.options.position === 'bottom'; }, // Actually draw the legend on the canvas draw: function() { var me = this; var opts = me.options; var labelOpts = opts.labels; var globalDefault = defaults.global; var lineDefault = globalDefault.elements.line; var legendWidth = me.width; var lineWidths = me.lineWidths; if (opts.display) { var ctx = me.ctx; var valueOrDefault = helpers.valueOrDefault; var fontColor = valueOrDefault(labelOpts.fontColor, globalDefault.defaultFontColor); var fontSize = valueOrDefault(labelOpts.fontSize, globalDefault.defaultFontSize); var fontStyle = valueOrDefault(labelOpts.fontStyle, globalDefault.defaultFontStyle); var fontFamily = valueOrDefault(labelOpts.fontFamily, globalDefault.defaultFontFamily); var labelFont = helpers.fontString(fontSize, fontStyle, fontFamily); var cursor; // Canvas setup ctx.textAlign = 'left'; ctx.textBaseline = 'middle'; ctx.lineWidth = 0.5; ctx.strokeStyle = fontColor; // for strikethrough effect ctx.fillStyle = fontColor; // render in correct colour ctx.font = labelFont; var boxWidth = getBoxWidth(labelOpts, fontSize); var hitboxes = me.legendHitBoxes; // current position var drawLegendBox = function(x, y, legendItem) { if (isNaN(boxWidth) || boxWidth <= 0) { return; } // Set the ctx for the box ctx.save(); ctx.fillStyle = valueOrDefault(legendItem.fillStyle, globalDefault.defaultColor); ctx.lineCap = valueOrDefault(legendItem.lineCap, lineDefault.borderCapStyle); ctx.lineDashOffset = valueOrDefault(legendItem.lineDashOffset, lineDefault.borderDashOffset); ctx.lineJoin = valueOrDefault(legendItem.lineJoin, lineDefault.borderJoinStyle); ctx.lineWidth = valueOrDefault(legendItem.lineWidth, lineDefault.borderWidth); ctx.strokeStyle = valueOrDefault(legendItem.strokeStyle, globalDefault.defaultColor); var isLineWidthZero = (valueOrDefault(legendItem.lineWidth, lineDefault.borderWidth) === 0); if (ctx.setLineDash) { // IE 9 and 10 do not support line dash ctx.setLineDash(valueOrDefault(legendItem.lineDash, lineDefault.borderDash)); } if (opts.labels && opts.labels.usePointStyle) { // Recalculate x and y for drawPoint() because its expecting // x and y to be center of figure (instead of top left) var radius = fontSize * Math.SQRT2 / 2; var offSet = radius / Math.SQRT2; var centerX = x + offSet; var centerY = y + offSet; // Draw pointStyle as legend symbol helpers.canvas.drawPoint(ctx, legendItem.pointStyle, radius, centerX, centerY); } else { // Draw box as legend symbol if (!isLineWidthZero) { ctx.strokeRect(x, y, boxWidth, fontSize); } ctx.fillRect(x, y, boxWidth, fontSize); } ctx.restore(); }; var fillText = function(x, y, legendItem, textWidth) { var halfFontSize = fontSize / 2; var xLeft = boxWidth + halfFontSize + x; var yMiddle = y + halfFontSize; ctx.fillText(legendItem.text, xLeft, yMiddle); if (legendItem.hidden) { // Strikethrough the text if hidden ctx.beginPath(); ctx.lineWidth = 2; ctx.moveTo(xLeft, yMiddle); ctx.lineTo(xLeft + textWidth, yMiddle); ctx.stroke(); } }; // Horizontal var isHorizontal = me.isHorizontal(); if (isHorizontal) { cursor = { x: me.left + ((legendWidth - lineWidths[0]) / 2), y: me.top + labelOpts.padding, line: 0 }; } else { cursor = { x: me.left + labelOpts.padding, y: me.top + labelOpts.padding, line: 0 }; } var itemHeight = fontSize + labelOpts.padding; helpers.each(me.legendItems, function(legendItem, i) { var textWidth = ctx.measureText(legendItem.text).width; var width = boxWidth + (fontSize / 2) + textWidth; var x = cursor.x; var y = cursor.y; if (isHorizontal) { if (x + width >= legendWidth) { y = cursor.y += itemHeight; cursor.line++; x = cursor.x = me.left + ((legendWidth - lineWidths[cursor.line]) / 2); } } else if (y + itemHeight > me.bottom) { x = cursor.x = x + me.columnWidths[cursor.line] + labelOpts.padding; y = cursor.y = me.top + labelOpts.padding; cursor.line++; } drawLegendBox(x, y, legendItem); hitboxes[i].left = x; hitboxes[i].top = y; // Fill the actual label fillText(x, y, legendItem, textWidth); if (isHorizontal) { cursor.x += width + (labelOpts.padding); } else { cursor.y += itemHeight; } }); } }, /** * Handle an event * @private * @param {IEvent} event - The event to handle * @return {Boolean} true if a change occured */ handleEvent: function(e) { var me = this; var opts = me.options; var type = e.type === 'mouseup' ? 'click' : e.type; var changed = false; if (type === 'mousemove') { if (!opts.onHover) { return; } } else if (type === 'click') { if (!opts.onClick) { return; } } else { return; } // Chart event already has relative position in it var x = e.x; var y = e.y; if (x >= me.left && x <= me.right && y >= me.top && y <= me.bottom) { // See if we are touching one of the dataset boxes var lh = me.legendHitBoxes; for (var i = 0; i < lh.length; ++i) { var hitBox = lh[i]; if (x >= hitBox.left && x <= hitBox.left + hitBox.width && y >= hitBox.top && y <= hitBox.top + hitBox.height) { // Touching an element if (type === 'click') { // use e.native for backwards compatibility opts.onClick.call(me, e.native, me.legendItems[i]); changed = true; break; } else if (type === 'mousemove') { // use e.native for backwards compatibility opts.onHover.call(me, e.native, me.legendItems[i]); changed = true; break; } } } } return changed; } }); function createNewLegendAndAttach(chart, legendOpts) { var legend = new Chart.Legend({ ctx: chart.ctx, options: legendOpts, chart: chart }); layout.configure(chart, legend, legendOpts); layout.addBox(chart, legend); chart.legend = legend; } return { id: 'legend', beforeInit: function(chart) { var legendOpts = chart.options.legend; if (legendOpts) { createNewLegendAndAttach(chart, legendOpts); } }, beforeUpdate: function(chart) { var legendOpts = chart.options.legend; var legend = chart.legend; if (legendOpts) { helpers.mergeIf(legendOpts, defaults.global.legend); if (legend) { layout.configure(chart, legend, legendOpts); legend.options = legendOpts; } else { createNewLegendAndAttach(chart, legendOpts); } } else if (legend) { layout.removeBox(chart, legend); delete chart.legend; } }, afterEvent: function(chart, e) { var legend = chart.legend; if (legend) { legend.handleEvent(e); } } }; }; },{"25":25,"26":26,"45":45}],51:[function(require,module,exports){ 'use strict'; var defaults = require(25); var Element = require(26); var helpers = require(45); defaults._set('global', { title: { display: false, fontStyle: 'bold', fullWidth: true, lineHeight: 1.2, padding: 10, position: 'top', text: '', weight: 2000 // by default greater than legend (1000) to be above } }); module.exports = function(Chart) { var layout = Chart.layoutService; var noop = helpers.noop; Chart.Title = Element.extend({ initialize: function(config) { var me = this; helpers.extend(me, config); // Contains hit boxes for each dataset (in dataset order) me.legendHitBoxes = []; }, // These methods are ordered by lifecycle. Utilities then follow. beforeUpdate: noop, update: function(maxWidth, maxHeight, margins) { var me = this; // Update Lifecycle - Probably don't want to ever extend or overwrite this function ;) me.beforeUpdate(); // Absorb the master measurements me.maxWidth = maxWidth; me.maxHeight = maxHeight; me.margins = margins; // Dimensions me.beforeSetDimensions(); me.setDimensions(); me.afterSetDimensions(); // Labels me.beforeBuildLabels(); me.buildLabels(); me.afterBuildLabels(); // Fit me.beforeFit(); me.fit(); me.afterFit(); // me.afterUpdate(); return me.minSize; }, afterUpdate: noop, // beforeSetDimensions: noop, setDimensions: function() { var me = this; // Set the unconstrained dimension before label rotation if (me.isHorizontal()) { // Reset position before calculating rotation me.width = me.maxWidth; me.left = 0; me.right = me.width; } else { me.height = me.maxHeight; // Reset position before calculating rotation me.top = 0; me.bottom = me.height; } // Reset padding me.paddingLeft = 0; me.paddingTop = 0; me.paddingRight = 0; me.paddingBottom = 0; // Reset minSize me.minSize = { width: 0, height: 0 }; }, afterSetDimensions: noop, // beforeBuildLabels: noop, buildLabels: noop, afterBuildLabels: noop, // beforeFit: noop, fit: function() { var me = this; var valueOrDefault = helpers.valueOrDefault; var opts = me.options; var display = opts.display; var fontSize = valueOrDefault(opts.fontSize, defaults.global.defaultFontSize); var minSize = me.minSize; var lineCount = helpers.isArray(opts.text) ? opts.text.length : 1; var lineHeight = helpers.options.toLineHeight(opts.lineHeight, fontSize); var textSize = display ? (lineCount * lineHeight) + (opts.padding * 2) : 0; if (me.isHorizontal()) { minSize.width = me.maxWidth; // fill all the width minSize.height = textSize; } else { minSize.width = textSize; minSize.height = me.maxHeight; // fill all the height } me.width = minSize.width; me.height = minSize.height; }, afterFit: noop, // Shared Methods isHorizontal: function() { var pos = this.options.position; return pos === 'top' || pos === 'bottom'; }, // Actually draw the title block on the canvas draw: function() { var me = this; var ctx = me.ctx; var valueOrDefault = helpers.valueOrDefault; var opts = me.options; var globalDefaults = defaults.global; if (opts.display) { var fontSize = valueOrDefault(opts.fontSize, globalDefaults.defaultFontSize); var fontStyle = valueOrDefault(opts.fontStyle, globalDefaults.defaultFontStyle); var fontFamily = valueOrDefault(opts.fontFamily, globalDefaults.defaultFontFamily); var titleFont = helpers.fontString(fontSize, fontStyle, fontFamily); var lineHeight = helpers.options.toLineHeight(opts.lineHeight, fontSize); var offset = lineHeight / 2 + opts.padding; var rotation = 0; var top = me.top; var left = me.left; var bottom = me.bottom; var right = me.right; var maxWidth, titleX, titleY; ctx.fillStyle = valueOrDefault(opts.fontColor, globalDefaults.defaultFontColor); // render in correct colour ctx.font = titleFont; // Horizontal if (me.isHorizontal()) { titleX = left + ((right - left) / 2); // midpoint of the width titleY = top + offset; maxWidth = right - left; } else { titleX = opts.position === 'left' ? left + offset : right - offset; titleY = top + ((bottom - top) / 2); maxWidth = bottom - top; rotation = Math.PI * (opts.position === 'left' ? -0.5 : 0.5); } ctx.save(); ctx.translate(titleX, titleY); ctx.rotate(rotation); ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; var text = opts.text; if (helpers.isArray(text)) { var y = 0; for (var i = 0; i < text.length; ++i) { ctx.fillText(text[i], 0, y, maxWidth); y += lineHeight; } } else { ctx.fillText(text, 0, 0, maxWidth); } ctx.restore(); } } }); function createNewTitleBlockAndAttach(chart, titleOpts) { var title = new Chart.Title({ ctx: chart.ctx, options: titleOpts, chart: chart }); layout.configure(chart, title, titleOpts); layout.addBox(chart, title); chart.titleBlock = title; } return { id: 'title', beforeInit: function(chart) { var titleOpts = chart.options.title; if (titleOpts) { createNewTitleBlockAndAttach(chart, titleOpts); } }, beforeUpdate: function(chart) { var titleOpts = chart.options.title; var titleBlock = chart.titleBlock; if (titleOpts) { helpers.mergeIf(titleOpts, defaults.global.title); if (titleBlock) { layout.configure(chart, titleBlock, titleOpts); titleBlock.options = titleOpts; } else { createNewTitleBlockAndAttach(chart, titleOpts); } } else if (titleBlock) { Chart.layoutService.removeBox(chart, titleBlock); delete chart.titleBlock; } } }; }; },{"25":25,"26":26,"45":45}],52:[function(require,module,exports){ 'use strict'; module.exports = function(Chart) { // Default config for a category scale var defaultConfig = { position: 'bottom' }; var DatasetScale = Chart.Scale.extend({ /** * Internal function to get the correct labels. If data.xLabels or data.yLabels are defined, use those * else fall back to data.labels * @private */ getLabels: function() { var data = this.chart.data; return this.options.labels || (this.isHorizontal() ? data.xLabels : data.yLabels) || data.labels; }, determineDataLimits: function() { var me = this; var labels = me.getLabels(); me.minIndex = 0; me.maxIndex = labels.length - 1; var findIndex; if (me.options.ticks.min !== undefined) { // user specified min value findIndex = labels.indexOf(me.options.ticks.min); me.minIndex = findIndex !== -1 ? findIndex : me.minIndex; } if (me.options.ticks.max !== undefined) { // user specified max value findIndex = labels.indexOf(me.options.ticks.max); me.maxIndex = findIndex !== -1 ? findIndex : me.maxIndex; } me.min = labels[me.minIndex]; me.max = labels[me.maxIndex]; }, buildTicks: function() { var me = this; var labels = me.getLabels(); // If we are viewing some subset of labels, slice the original array me.ticks = (me.minIndex === 0 && me.maxIndex === labels.length - 1) ? labels : labels.slice(me.minIndex, me.maxIndex + 1); }, getLabelForIndex: function(index, datasetIndex) { var me = this; var data = me.chart.data; var isHorizontal = me.isHorizontal(); if (data.yLabels && !isHorizontal) { return me.getRightValue(data.datasets[datasetIndex].data[index]); } return me.ticks[index - me.minIndex]; }, // Used to get data value locations. Value can either be an index or a numerical value getPixelForValue: function(value, index) { var me = this; var offset = me.options.offset; // 1 is added because we need the length but we have the indexes var offsetAmt = Math.max((me.maxIndex + 1 - me.minIndex - (offset ? 0 : 1)), 1); // If value is a data object, then index is the index in the data array, // not the index of the scale. We need to change that. var valueCategory; if (value !== undefined && value !== null) { valueCategory = me.isHorizontal() ? value.x : value.y; } if (valueCategory !== undefined || (value !== undefined && isNaN(index))) { var labels = me.getLabels(); value = valueCategory || value; var idx = labels.indexOf(value); index = idx !== -1 ? idx : index; } if (me.isHorizontal()) { var valueWidth = me.width / offsetAmt; var widthOffset = (valueWidth * (index - me.minIndex)); if (offset) { widthOffset += (valueWidth / 2); } return me.left + Math.round(widthOffset); } var valueHeight = me.height / offsetAmt; var heightOffset = (valueHeight * (index - me.minIndex)); if (offset) { heightOffset += (valueHeight / 2); } return me.top + Math.round(heightOffset); }, getPixelForTick: function(index) { return this.getPixelForValue(this.ticks[index], index + this.minIndex, null); }, getValueForPixel: function(pixel) { var me = this; var offset = me.options.offset; var value; var offsetAmt = Math.max((me._ticks.length - (offset ? 0 : 1)), 1); var horz = me.isHorizontal(); var valueDimension = (horz ? me.width : me.height) / offsetAmt; pixel -= horz ? me.left : me.top; if (offset) { pixel -= (valueDimension / 2); } if (pixel <= 0) { value = 0; } else { value = Math.round(pixel / valueDimension); } return value + me.minIndex; }, getBasePixel: function() { return this.bottom; } }); Chart.scaleService.registerScaleType('category', DatasetScale, defaultConfig); }; },{}],53:[function(require,module,exports){ 'use strict'; var defaults = require(25); var helpers = require(45); var Ticks = require(34); module.exports = function(Chart) { var defaultConfig = { position: 'left', ticks: { callback: Ticks.formatters.linear } }; var LinearScale = Chart.LinearScaleBase.extend({ determineDataLimits: function() { var me = this; var opts = me.options; var chart = me.chart; var data = chart.data; var datasets = data.datasets; var isHorizontal = me.isHorizontal(); var DEFAULT_MIN = 0; var DEFAULT_MAX = 1; function IDMatches(meta) { return isHorizontal ? meta.xAxisID === me.id : meta.yAxisID === me.id; } // First Calculate the range me.min = null; me.max = null; var hasStacks = opts.stacked; if (hasStacks === undefined) { helpers.each(datasets, function(dataset, datasetIndex) { if (hasStacks) { return; } var meta = chart.getDatasetMeta(datasetIndex); if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta) && meta.stack !== undefined) { hasStacks = true; } }); } if (opts.stacked || hasStacks) { var valuesPerStack = {}; helpers.each(datasets, function(dataset, datasetIndex) { var meta = chart.getDatasetMeta(datasetIndex); var key = [ meta.type, // we have a separate stack for stack=undefined datasets when the opts.stacked is undefined ((opts.stacked === undefined && meta.stack === undefined) ? datasetIndex : ''), meta.stack ].join('.'); if (valuesPerStack[key] === undefined) { valuesPerStack[key] = { positiveValues: [], negativeValues: [] }; } // Store these per type var positiveValues = valuesPerStack[key].positiveValues; var negativeValues = valuesPerStack[key].negativeValues; if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) { helpers.each(dataset.data, function(rawValue, index) { var value = +me.getRightValue(rawValue); if (isNaN(value) || meta.data[index].hidden) { return; } positiveValues[index] = positiveValues[index] || 0; negativeValues[index] = negativeValues[index] || 0; if (opts.relativePoints) { positiveValues[index] = 100; } else if (value < 0) { negativeValues[index] += value; } else { positiveValues[index] += value; } }); } }); helpers.each(valuesPerStack, function(valuesForType) { var values = valuesForType.positiveValues.concat(valuesForType.negativeValues); var minVal = helpers.min(values); var maxVal = helpers.max(values); me.min = me.min === null ? minVal : Math.min(me.min, minVal); me.max = me.max === null ? maxVal : Math.max(me.max, maxVal); }); } else { helpers.each(datasets, function(dataset, datasetIndex) { var meta = chart.getDatasetMeta(datasetIndex); if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) { helpers.each(dataset.data, function(rawValue, index) { var value = +me.getRightValue(rawValue); if (isNaN(value) || meta.data[index].hidden) { return; } if (me.min === null) { me.min = value; } else if (value < me.min) { me.min = value; } if (me.max === null) { me.max = value; } else if (value > me.max) { me.max = value; } }); } }); } me.min = isFinite(me.min) && !isNaN(me.min) ? me.min : DEFAULT_MIN; me.max = isFinite(me.max) && !isNaN(me.max) ? me.max : DEFAULT_MAX; // Common base implementation to handle ticks.min, ticks.max, ticks.beginAtZero this.handleTickRangeOptions(); }, getTickLimit: function() { var maxTicks; var me = this; var tickOpts = me.options.ticks; if (me.isHorizontal()) { maxTicks = Math.min(tickOpts.maxTicksLimit ? tickOpts.maxTicksLimit : 11, Math.ceil(me.width / 50)); } else { // The factor of 2 used to scale the font size has been experimentally determined. var tickFontSize = helpers.valueOrDefault(tickOpts.fontSize, defaults.global.defaultFontSize); maxTicks = Math.min(tickOpts.maxTicksLimit ? tickOpts.maxTicksLimit : 11, Math.ceil(me.height / (2 * tickFontSize))); } return maxTicks; }, // Called after the ticks are built. We need handleDirectionalChanges: function() { if (!this.isHorizontal()) { // We are in a vertical orientation. The top value is the highest. So reverse the array this.ticks.reverse(); } }, getLabelForIndex: function(index, datasetIndex) { return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]); }, // Utils getPixelForValue: function(value) { // This must be called after fit has been run so that // this.left, this.top, this.right, and this.bottom have been defined var me = this; var start = me.start; var rightValue = +me.getRightValue(value); var pixel; var range = me.end - start; if (me.isHorizontal()) { pixel = me.left + (me.width / range * (rightValue - start)); return Math.round(pixel); } pixel = me.bottom - (me.height / range * (rightValue - start)); return Math.round(pixel); }, getValueForPixel: function(pixel) { var me = this; var isHorizontal = me.isHorizontal(); var innerDimension = isHorizontal ? me.width : me.height; var offset = (isHorizontal ? pixel - me.left : me.bottom - pixel) / innerDimension; return me.start + ((me.end - me.start) * offset); }, getPixelForTick: function(index) { return this.getPixelForValue(this.ticksAsNumbers[index]); } }); Chart.scaleService.registerScaleType('linear', LinearScale, defaultConfig); }; },{"25":25,"34":34,"45":45}],54:[function(require,module,exports){ 'use strict'; var helpers = require(45); var Ticks = require(34); module.exports = function(Chart) { var noop = helpers.noop; Chart.LinearScaleBase = Chart.Scale.extend({ getRightValue: function(value) { if (typeof value === 'string') { return +value; } return Chart.Scale.prototype.getRightValue.call(this, value); }, handleTickRangeOptions: function() { var me = this; var opts = me.options; var tickOpts = opts.ticks; // If we are forcing it to begin at 0, but 0 will already be rendered on the chart, // do nothing since that would make the chart weird. If the user really wants a weird chart // axis, they can manually override it if (tickOpts.beginAtZero) { var minSign = helpers.sign(me.min); var maxSign = helpers.sign(me.max); if (minSign < 0 && maxSign < 0) { // move the top up to 0 me.max = 0; } else if (minSign > 0 && maxSign > 0) { // move the bottom down to 0 me.min = 0; } } var setMin = tickOpts.min !== undefined || tickOpts.suggestedMin !== undefined; var setMax = tickOpts.max !== undefined || tickOpts.suggestedMax !== undefined; if (tickOpts.min !== undefined) { me.min = tickOpts.min; } else if (tickOpts.suggestedMin !== undefined) { if (me.min === null) { me.min = tickOpts.suggestedMin; } else { me.min = Math.min(me.min, tickOpts.suggestedMin); } } if (tickOpts.max !== undefined) { me.max = tickOpts.max; } else if (tickOpts.suggestedMax !== undefined) { if (me.max === null) { me.max = tickOpts.suggestedMax; } else { me.max = Math.max(me.max, tickOpts.suggestedMax); } } if (setMin !== setMax) { // We set the min or the max but not both. // So ensure that our range is good // Inverted or 0 length range can happen when // ticks.min is set, and no datasets are visible if (me.min >= me.max) { if (setMin) { me.max = me.min + 1; } else { me.min = me.max - 1; } } } if (me.min === me.max) { me.max++; if (!tickOpts.beginAtZero) { me.min--; } } }, getTickLimit: noop, handleDirectionalChanges: noop, buildTicks: function() { var me = this; var opts = me.options; var tickOpts = opts.ticks; // Figure out what the max number of ticks we can support it is based on the size of // the axis area. For now, we say that the minimum tick spacing in pixels must be 50 // We also limit the maximum number of ticks to 11 which gives a nice 10 squares on // the graph. Make sure we always have at least 2 ticks var maxTicks = me.getTickLimit(); maxTicks = Math.max(2, maxTicks); var numericGeneratorOptions = { maxTicks: maxTicks, min: tickOpts.min, max: tickOpts.max, stepSize: helpers.valueOrDefault(tickOpts.fixedStepSize, tickOpts.stepSize) }; var ticks = me.ticks = Ticks.generators.linear(numericGeneratorOptions, me); me.handleDirectionalChanges(); // At this point, we need to update our max and min given the tick values since we have expanded the // range of the scale me.max = helpers.max(ticks); me.min = helpers.min(ticks); if (tickOpts.reverse) { ticks.reverse(); me.start = me.max; me.end = me.min; } else { me.start = me.min; me.end = me.max; } }, convertTicksToLabels: function() { var me = this; me.ticksAsNumbers = me.ticks.slice(); me.zeroLineIndex = me.ticks.indexOf(0); Chart.Scale.prototype.convertTicksToLabels.call(me); } }); }; },{"34":34,"45":45}],55:[function(require,module,exports){ 'use strict'; var helpers = require(45); var Ticks = require(34); module.exports = function(Chart) { var defaultConfig = { position: 'left', // label settings ticks: { callback: Ticks.formatters.logarithmic } }; var LogarithmicScale = Chart.Scale.extend({ determineDataLimits: function() { var me = this; var opts = me.options; var tickOpts = opts.ticks; var chart = me.chart; var data = chart.data; var datasets = data.datasets; var valueOrDefault = helpers.valueOrDefault; var isHorizontal = me.isHorizontal(); function IDMatches(meta) { return isHorizontal ? meta.xAxisID === me.id : meta.yAxisID === me.id; } // Calculate Range me.min = null; me.max = null; me.minNotZero = null; var hasStacks = opts.stacked; if (hasStacks === undefined) { helpers.each(datasets, function(dataset, datasetIndex) { if (hasStacks) { return; } var meta = chart.getDatasetMeta(datasetIndex); if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta) && meta.stack !== undefined) { hasStacks = true; } }); } if (opts.stacked || hasStacks) { var valuesPerStack = {}; helpers.each(datasets, function(dataset, datasetIndex) { var meta = chart.getDatasetMeta(datasetIndex); var key = [ meta.type, // we have a separate stack for stack=undefined datasets when the opts.stacked is undefined ((opts.stacked === undefined && meta.stack === undefined) ? datasetIndex : ''), meta.stack ].join('.'); if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) { if (valuesPerStack[key] === undefined) { valuesPerStack[key] = []; } helpers.each(dataset.data, function(rawValue, index) { var values = valuesPerStack[key]; var value = +me.getRightValue(rawValue); if (isNaN(value) || meta.data[index].hidden) { return; } values[index] = values[index] || 0; if (opts.relativePoints) { values[index] = 100; } else { // Don't need to split positive and negative since the log scale can't handle a 0 crossing values[index] += value; } }); } }); helpers.each(valuesPerStack, function(valuesForType) { var minVal = helpers.min(valuesForType); var maxVal = helpers.max(valuesForType); me.min = me.min === null ? minVal : Math.min(me.min, minVal); me.max = me.max === null ? maxVal : Math.max(me.max, maxVal); }); } else { helpers.each(datasets, function(dataset, datasetIndex) { var meta = chart.getDatasetMeta(datasetIndex); if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) { helpers.each(dataset.data, function(rawValue, index) { var value = +me.getRightValue(rawValue); if (isNaN(value) || meta.data[index].hidden) { return; } if (me.min === null) { me.min = value; } else if (value < me.min) { me.min = value; } if (me.max === null) { me.max = value; } else if (value > me.max) { me.max = value; } if (value !== 0 && (me.minNotZero === null || value < me.minNotZero)) { me.minNotZero = value; } }); } }); } me.min = valueOrDefault(tickOpts.min, me.min); me.max = valueOrDefault(tickOpts.max, me.max); if (me.min === me.max) { if (me.min !== 0 && me.min !== null) { me.min = Math.pow(10, Math.floor(helpers.log10(me.min)) - 1); me.max = Math.pow(10, Math.floor(helpers.log10(me.max)) + 1); } else { me.min = 1; me.max = 10; } } }, buildTicks: function() { var me = this; var opts = me.options; var tickOpts = opts.ticks; var generationOptions = { min: tickOpts.min, max: tickOpts.max }; var ticks = me.ticks = Ticks.generators.logarithmic(generationOptions, me); if (!me.isHorizontal()) { // We are in a vertical orientation. The top value is the highest. So reverse the array ticks.reverse(); } // At this point, we need to update our max and min given the tick values since we have expanded the // range of the scale me.max = helpers.max(ticks); me.min = helpers.min(ticks); if (tickOpts.reverse) { ticks.reverse(); me.start = me.max; me.end = me.min; } else { me.start = me.min; me.end = me.max; } }, convertTicksToLabels: function() { this.tickValues = this.ticks.slice(); Chart.Scale.prototype.convertTicksToLabels.call(this); }, // Get the correct tooltip label getLabelForIndex: function(index, datasetIndex) { return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]); }, getPixelForTick: function(index) { return this.getPixelForValue(this.tickValues[index]); }, getPixelForValue: function(value) { var me = this; var start = me.start; var newVal = +me.getRightValue(value); var opts = me.options; var tickOpts = opts.ticks; var innerDimension, pixel, range; if (me.isHorizontal()) { range = helpers.log10(me.end) - helpers.log10(start); // todo: if start === 0 if (newVal === 0) { pixel = me.left; } else { innerDimension = me.width; pixel = me.left + (innerDimension / range * (helpers.log10(newVal) - helpers.log10(start))); } } else { // Bottom - top since pixels increase downward on a screen innerDimension = me.height; if (start === 0 && !tickOpts.reverse) { range = helpers.log10(me.end) - helpers.log10(me.minNotZero); if (newVal === start) { pixel = me.bottom; } else if (newVal === me.minNotZero) { pixel = me.bottom - innerDimension * 0.02; } else { pixel = me.bottom - innerDimension * 0.02 - (innerDimension * 0.98 / range * (helpers.log10(newVal) - helpers.log10(me.minNotZero))); } } else if (me.end === 0 && tickOpts.reverse) { range = helpers.log10(me.start) - helpers.log10(me.minNotZero); if (newVal === me.end) { pixel = me.top; } else if (newVal === me.minNotZero) { pixel = me.top + innerDimension * 0.02; } else { pixel = me.top + innerDimension * 0.02 + (innerDimension * 0.98 / range * (helpers.log10(newVal) - helpers.log10(me.minNotZero))); } } else if (newVal === 0) { pixel = tickOpts.reverse ? me.top : me.bottom; } else { range = helpers.log10(me.end) - helpers.log10(start); innerDimension = me.height; pixel = me.bottom - (innerDimension / range * (helpers.log10(newVal) - helpers.log10(start))); } } return pixel; }, getValueForPixel: function(pixel) { var me = this; var range = helpers.log10(me.end) - helpers.log10(me.start); var value, innerDimension; if (me.isHorizontal()) { innerDimension = me.width; value = me.start * Math.pow(10, (pixel - me.left) * range / innerDimension); } else { // todo: if start === 0 innerDimension = me.height; value = Math.pow(10, (me.bottom - pixel) * range / innerDimension) / me.start; } return value; } }); Chart.scaleService.registerScaleType('logarithmic', LogarithmicScale, defaultConfig); }; },{"34":34,"45":45}],56:[function(require,module,exports){ 'use strict'; var defaults = require(25); var helpers = require(45); var Ticks = require(34); module.exports = function(Chart) { var globalDefaults = defaults.global; var defaultConfig = { display: true, // Boolean - Whether to animate scaling the chart from the centre animate: true, position: 'chartArea', angleLines: { display: true, color: 'rgba(0, 0, 0, 0.1)', lineWidth: 1 }, gridLines: { circular: false }, // label settings ticks: { // Boolean - Show a backdrop to the scale label showLabelBackdrop: true, // String - The colour of the label backdrop backdropColor: 'rgba(255,255,255,0.75)', // Number - The backdrop padding above & below the label in pixels backdropPaddingY: 2, // Number - The backdrop padding to the side of the label in pixels backdropPaddingX: 2, callback: Ticks.formatters.linear }, pointLabels: { // Boolean - if true, show point labels display: true, // Number - Point label font size in pixels fontSize: 10, // Function - Used to convert point labels callback: function(label) { return label; } } }; function getValueCount(scale) { var opts = scale.options; return opts.angleLines.display || opts.pointLabels.display ? scale.chart.data.labels.length : 0; } function getPointLabelFontOptions(scale) { var pointLabelOptions = scale.options.pointLabels; var fontSize = helpers.valueOrDefault(pointLabelOptions.fontSize, globalDefaults.defaultFontSize); var fontStyle = helpers.valueOrDefault(pointLabelOptions.fontStyle, globalDefaults.defaultFontStyle); var fontFamily = helpers.valueOrDefault(pointLabelOptions.fontFamily, globalDefaults.defaultFontFamily); var font = helpers.fontString(fontSize, fontStyle, fontFamily); return { size: fontSize, style: fontStyle, family: fontFamily, font: font }; } function measureLabelSize(ctx, fontSize, label) { if (helpers.isArray(label)) { return { w: helpers.longestText(ctx, ctx.font, label), h: (label.length * fontSize) + ((label.length - 1) * 1.5 * fontSize) }; } return { w: ctx.measureText(label).width, h: fontSize }; } function determineLimits(angle, pos, size, min, max) { if (angle === min || angle === max) { return { start: pos - (size / 2), end: pos + (size / 2) }; } else if (angle < min || angle > max) { return { start: pos - size - 5, end: pos }; } return { start: pos, end: pos + size + 5 }; } /** * Helper function to fit a radial linear scale with point labels */ function fitWithPointLabels(scale) { /* * Right, this is really confusing and there is a lot of maths going on here * The gist of the problem is here: https://gist.github.com/nnnick/696cc9c55f4b0beb8fe9 * * Reaction: https://dl.dropboxusercontent.com/u/34601363/toomuchscience.gif * * Solution: * * We assume the radius of the polygon is half the size of the canvas at first * at each index we check if the text overlaps. * * Where it does, we store that angle and that index. * * After finding the largest index and angle we calculate how much we need to remove * from the shape radius to move the point inwards by that x. * * We average the left and right distances to get the maximum shape radius that can fit in the box * along with labels. * * Once we have that, we can find the centre point for the chart, by taking the x text protrusion * on each side, removing that from the size, halving it and adding the left x protrusion width. * * This will mean we have a shape fitted to the canvas, as large as it can be with the labels * and position it in the most space efficient manner * * https://dl.dropboxusercontent.com/u/34601363/yeahscience.gif */ var plFont = getPointLabelFontOptions(scale); // Get maximum radius of the polygon. Either half the height (minus the text width) or half the width. // Use this to calculate the offset + change. - Make sure L/R protrusion is at least 0 to stop issues with centre points var largestPossibleRadius = Math.min(scale.height / 2, scale.width / 2); var furthestLimits = { r: scale.width, l: 0, t: scale.height, b: 0 }; var furthestAngles = {}; var i, textSize, pointPosition; scale.ctx.font = plFont.font; scale._pointLabelSizes = []; var valueCount = getValueCount(scale); for (i = 0; i < valueCount; i++) { pointPosition = scale.getPointPosition(i, largestPossibleRadius); textSize = measureLabelSize(scale.ctx, plFont.size, scale.pointLabels[i] || ''); scale._pointLabelSizes[i] = textSize; // Add quarter circle to make degree 0 mean top of circle var angleRadians = scale.getIndexAngle(i); var angle = helpers.toDegrees(angleRadians) % 360; var hLimits = determineLimits(angle, pointPosition.x, textSize.w, 0, 180); var vLimits = determineLimits(angle, pointPosition.y, textSize.h, 90, 270); if (hLimits.start < furthestLimits.l) { furthestLimits.l = hLimits.start; furthestAngles.l = angleRadians; } if (hLimits.end > furthestLimits.r) { furthestLimits.r = hLimits.end; furthestAngles.r = angleRadians; } if (vLimits.start < furthestLimits.t) { furthestLimits.t = vLimits.start; furthestAngles.t = angleRadians; } if (vLimits.end > furthestLimits.b) { furthestLimits.b = vLimits.end; furthestAngles.b = angleRadians; } } scale.setReductions(largestPossibleRadius, furthestLimits, furthestAngles); } /** * Helper function to fit a radial linear scale with no point labels */ function fit(scale) { var largestPossibleRadius = Math.min(scale.height / 2, scale.width / 2); scale.drawingArea = Math.round(largestPossibleRadius); scale.setCenterPoint(0, 0, 0, 0); } function getTextAlignForAngle(angle) { if (angle === 0 || angle === 180) { return 'center'; } else if (angle < 180) { return 'left'; } return 'right'; } function fillText(ctx, text, position, fontSize) { if (helpers.isArray(text)) { var y = position.y; var spacing = 1.5 * fontSize; for (var i = 0; i < text.length; ++i) { ctx.fillText(text[i], position.x, y); y += spacing; } } else { ctx.fillText(text, position.x, position.y); } } function adjustPointPositionForLabelHeight(angle, textSize, position) { if (angle === 90 || angle === 270) { position.y -= (textSize.h / 2); } else if (angle > 270 || angle < 90) { position.y -= textSize.h; } } function drawPointLabels(scale) { var ctx = scale.ctx; var valueOrDefault = helpers.valueOrDefault; var opts = scale.options; var angleLineOpts = opts.angleLines; var pointLabelOpts = opts.pointLabels; ctx.lineWidth = angleLineOpts.lineWidth; ctx.strokeStyle = angleLineOpts.color; var outerDistance = scale.getDistanceFromCenterForValue(opts.ticks.reverse ? scale.min : scale.max); // Point Label Font var plFont = getPointLabelFontOptions(scale); ctx.textBaseline = 'top'; for (var i = getValueCount(scale) - 1; i >= 0; i--) { if (angleLineOpts.display) { var outerPosition = scale.getPointPosition(i, outerDistance); ctx.beginPath(); ctx.moveTo(scale.xCenter, scale.yCenter); ctx.lineTo(outerPosition.x, outerPosition.y); ctx.stroke(); ctx.closePath(); } if (pointLabelOpts.display) { // Extra 3px out for some label spacing var pointLabelPosition = scale.getPointPosition(i, outerDistance + 5); // Keep this in loop since we may support array properties here var pointLabelFontColor = valueOrDefault(pointLabelOpts.fontColor, globalDefaults.defaultFontColor); ctx.font = plFont.font; ctx.fillStyle = pointLabelFontColor; var angleRadians = scale.getIndexAngle(i); var angle = helpers.toDegrees(angleRadians); ctx.textAlign = getTextAlignForAngle(angle); adjustPointPositionForLabelHeight(angle, scale._pointLabelSizes[i], pointLabelPosition); fillText(ctx, scale.pointLabels[i] || '', pointLabelPosition, plFont.size); } } } function drawRadiusLine(scale, gridLineOpts, radius, index) { var ctx = scale.ctx; ctx.strokeStyle = helpers.valueAtIndexOrDefault(gridLineOpts.color, index - 1); ctx.lineWidth = helpers.valueAtIndexOrDefault(gridLineOpts.lineWidth, index - 1); if (scale.options.gridLines.circular) { // Draw circular arcs between the points ctx.beginPath(); ctx.arc(scale.xCenter, scale.yCenter, radius, 0, Math.PI * 2); ctx.closePath(); ctx.stroke(); } else { // Draw straight lines connecting each index var valueCount = getValueCount(scale); if (valueCount === 0) { return; } ctx.beginPath(); var pointPosition = scale.getPointPosition(0, radius); ctx.moveTo(pointPosition.x, pointPosition.y); for (var i = 1; i < valueCount; i++) { pointPosition = scale.getPointPosition(i, radius); ctx.lineTo(pointPosition.x, pointPosition.y); } ctx.closePath(); ctx.stroke(); } } function numberOrZero(param) { return helpers.isNumber(param) ? param : 0; } var LinearRadialScale = Chart.LinearScaleBase.extend({ setDimensions: function() { var me = this; var opts = me.options; var tickOpts = opts.ticks; // Set the unconstrained dimension before label rotation me.width = me.maxWidth; me.height = me.maxHeight; me.xCenter = Math.round(me.width / 2); me.yCenter = Math.round(me.height / 2); var minSize = helpers.min([me.height, me.width]); var tickFontSize = helpers.valueOrDefault(tickOpts.fontSize, globalDefaults.defaultFontSize); me.drawingArea = opts.display ? (minSize / 2) - (tickFontSize / 2 + tickOpts.backdropPaddingY) : (minSize / 2); }, determineDataLimits: function() { var me = this; var chart = me.chart; var min = Number.POSITIVE_INFINITY; var max = Number.NEGATIVE_INFINITY; helpers.each(chart.data.datasets, function(dataset, datasetIndex) { if (chart.isDatasetVisible(datasetIndex)) { var meta = chart.getDatasetMeta(datasetIndex); helpers.each(dataset.data, function(rawValue, index) { var value = +me.getRightValue(rawValue); if (isNaN(value) || meta.data[index].hidden) { return; } min = Math.min(value, min); max = Math.max(value, max); }); } }); me.min = (min === Number.POSITIVE_INFINITY ? 0 : min); me.max = (max === Number.NEGATIVE_INFINITY ? 0 : max); // Common base implementation to handle ticks.min, ticks.max, ticks.beginAtZero me.handleTickRangeOptions(); }, getTickLimit: function() { var tickOpts = this.options.ticks; var tickFontSize = helpers.valueOrDefault(tickOpts.fontSize, globalDefaults.defaultFontSize); return Math.min(tickOpts.maxTicksLimit ? tickOpts.maxTicksLimit : 11, Math.ceil(this.drawingArea / (1.5 * tickFontSize))); }, convertTicksToLabels: function() { var me = this; Chart.LinearScaleBase.prototype.convertTicksToLabels.call(me); // Point labels me.pointLabels = me.chart.data.labels.map(me.options.pointLabels.callback, me); }, getLabelForIndex: function(index, datasetIndex) { return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]); }, fit: function() { if (this.options.pointLabels.display) { fitWithPointLabels(this); } else { fit(this); } }, /** * Set radius reductions and determine new radius and center point * @private */ setReductions: function(largestPossibleRadius, furthestLimits, furthestAngles) { var me = this; var radiusReductionLeft = furthestLimits.l / Math.sin(furthestAngles.l); var radiusReductionRight = Math.max(furthestLimits.r - me.width, 0) / Math.sin(furthestAngles.r); var radiusReductionTop = -furthestLimits.t / Math.cos(furthestAngles.t); var radiusReductionBottom = -Math.max(furthestLimits.b - me.height, 0) / Math.cos(furthestAngles.b); radiusReductionLeft = numberOrZero(radiusReductionLeft); radiusReductionRight = numberOrZero(radiusReductionRight); radiusReductionTop = numberOrZero(radiusReductionTop); radiusReductionBottom = numberOrZero(radiusReductionBottom); me.drawingArea = Math.min( Math.round(largestPossibleRadius - (radiusReductionLeft + radiusReductionRight) / 2), Math.round(largestPossibleRadius - (radiusReductionTop + radiusReductionBottom) / 2)); me.setCenterPoint(radiusReductionLeft, radiusReductionRight, radiusReductionTop, radiusReductionBottom); }, setCenterPoint: function(leftMovement, rightMovement, topMovement, bottomMovement) { var me = this; var maxRight = me.width - rightMovement - me.drawingArea; var maxLeft = leftMovement + me.drawingArea; var maxTop = topMovement + me.drawingArea; var maxBottom = me.height - bottomMovement - me.drawingArea; me.xCenter = Math.round(((maxLeft + maxRight) / 2) + me.left); me.yCenter = Math.round(((maxTop + maxBottom) / 2) + me.top); }, getIndexAngle: function(index) { var angleMultiplier = (Math.PI * 2) / getValueCount(this); var startAngle = this.chart.options && this.chart.options.startAngle ? this.chart.options.startAngle : 0; var startAngleRadians = startAngle * Math.PI * 2 / 360; // Start from the top instead of right, so remove a quarter of the circle return index * angleMultiplier + startAngleRadians; }, getDistanceFromCenterForValue: function(value) { var me = this; if (value === null) { return 0; // null always in center } // Take into account half font size + the yPadding of the top value var scalingFactor = me.drawingArea / (me.max - me.min); if (me.options.ticks.reverse) { return (me.max - value) * scalingFactor; } return (value - me.min) * scalingFactor; }, getPointPosition: function(index, distanceFromCenter) { var me = this; var thisAngle = me.getIndexAngle(index) - (Math.PI / 2); return { x: Math.round(Math.cos(thisAngle) * distanceFromCenter) + me.xCenter, y: Math.round(Math.sin(thisAngle) * distanceFromCenter) + me.yCenter }; }, getPointPositionForValue: function(index, value) { return this.getPointPosition(index, this.getDistanceFromCenterForValue(value)); }, getBasePosition: function() { var me = this; var min = me.min; var max = me.max; return me.getPointPositionForValue(0, me.beginAtZero ? 0 : min < 0 && max < 0 ? max : min > 0 && max > 0 ? min : 0); }, draw: function() { var me = this; var opts = me.options; var gridLineOpts = opts.gridLines; var tickOpts = opts.ticks; var valueOrDefault = helpers.valueOrDefault; if (opts.display) { var ctx = me.ctx; var startAngle = this.getIndexAngle(0); // Tick Font var tickFontSize = valueOrDefault(tickOpts.fontSize, globalDefaults.defaultFontSize); var tickFontStyle = valueOrDefault(tickOpts.fontStyle, globalDefaults.defaultFontStyle); var tickFontFamily = valueOrDefault(tickOpts.fontFamily, globalDefaults.defaultFontFamily); var tickLabelFont = helpers.fontString(tickFontSize, tickFontStyle, tickFontFamily); helpers.each(me.ticks, function(label, index) { // Don't draw a centre value (if it is minimum) if (index > 0 || tickOpts.reverse) { var yCenterOffset = me.getDistanceFromCenterForValue(me.ticksAsNumbers[index]); // Draw circular lines around the scale if (gridLineOpts.display && index !== 0) { drawRadiusLine(me, gridLineOpts, yCenterOffset, index); } if (tickOpts.display) { var tickFontColor = valueOrDefault(tickOpts.fontColor, globalDefaults.defaultFontColor); ctx.font = tickLabelFont; ctx.save(); ctx.translate(me.xCenter, me.yCenter); ctx.rotate(startAngle); if (tickOpts.showLabelBackdrop) { var labelWidth = ctx.measureText(label).width; ctx.fillStyle = tickOpts.backdropColor; ctx.fillRect( -labelWidth / 2 - tickOpts.backdropPaddingX, -yCenterOffset - tickFontSize / 2 - tickOpts.backdropPaddingY, labelWidth + tickOpts.backdropPaddingX * 2, tickFontSize + tickOpts.backdropPaddingY * 2 ); } ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; ctx.fillStyle = tickFontColor; ctx.fillText(label, 0, -yCenterOffset); ctx.restore(); } } }); if (opts.angleLines.display || opts.pointLabels.display) { drawPointLabels(me); } } } }); Chart.scaleService.registerScaleType('radialLinear', LinearRadialScale, defaultConfig); }; },{"25":25,"34":34,"45":45}],57:[function(require,module,exports){ /* global window: false */ 'use strict'; var moment = require(1); moment = typeof moment === 'function' ? moment : window.moment; var defaults = require(25); var helpers = require(45); // Integer constants are from the ES6 spec. var MIN_INTEGER = Number.MIN_SAFE_INTEGER || -9007199254740991; var MAX_INTEGER = Number.MAX_SAFE_INTEGER || 9007199254740991; var INTERVALS = { millisecond: { common: true, size: 1, steps: [1, 2, 5, 10, 20, 50, 100, 250, 500] }, second: { common: true, size: 1000, steps: [1, 2, 5, 10, 30] }, minute: { common: true, size: 60000, steps: [1, 2, 5, 10, 30] }, hour: { common: true, size: 3600000, steps: [1, 2, 3, 6, 12] }, day: { common: true, size: 86400000, steps: [1, 2, 5] }, week: { common: false, size: 604800000, steps: [1, 2, 3, 4] }, month: { common: true, size: 2.628e9, steps: [1, 2, 3] }, quarter: { common: false, size: 7.884e9, steps: [1, 2, 3, 4] }, year: { common: true, size: 3.154e10 } }; var UNITS = Object.keys(INTERVALS); function sorter(a, b) { return a - b; } function arrayUnique(items) { var hash = {}; var out = []; var i, ilen, item; for (i = 0, ilen = items.length; i < ilen; ++i) { item = items[i]; if (!hash[item]) { hash[item] = true; out.push(item); } } return out; } /** * Returns an array of {time, pos} objects used to interpolate a specific `time` or position * (`pos`) on the scale, by searching entries before and after the requested value. `pos` is * a decimal between 0 and 1: 0 being the start of the scale (left or top) and 1 the other * extremity (left + width or top + height). Note that it would be more optimized to directly * store pre-computed pixels, but the scale dimensions are not guaranteed at the time we need * to create the lookup table. The table ALWAYS contains at least two items: min and max. * * @param {Number[]} timestamps - timestamps sorted from lowest to highest. * @param {String} distribution - If 'linear', timestamps will be spread linearly along the min * and max range, so basically, the table will contains only two items: {min, 0} and {max, 1}. * If 'series', timestamps will be positioned at the same distance from each other. In this * case, only timestamps that break the time linearity are registered, meaning that in the * best case, all timestamps are linear, the table contains only min and max. */ function buildLookupTable(timestamps, min, max, distribution) { if (distribution === 'linear' || !timestamps.length) { return [ {time: min, pos: 0}, {time: max, pos: 1} ]; } var table = []; var items = [min]; var i, ilen, prev, curr, next; for (i = 0, ilen = timestamps.length; i < ilen; ++i) { curr = timestamps[i]; if (curr > min && curr < max) { items.push(curr); } } items.push(max); for (i = 0, ilen = items.length; i < ilen; ++i) { next = items[i + 1]; prev = items[i - 1]; curr = items[i]; // only add points that breaks the scale linearity if (prev === undefined || next === undefined || Math.round((next + prev) / 2) !== curr) { table.push({time: curr, pos: i / (ilen - 1)}); } } return table; } // @see adapted from http://www.anujgakhar.com/2014/03/01/binary-search-in-javascript/ function lookup(table, key, value) { var lo = 0; var hi = table.length - 1; var mid, i0, i1; while (lo >= 0 && lo <= hi) { mid = (lo + hi) >> 1; i0 = table[mid - 1] || null; i1 = table[mid]; if (!i0) { // given value is outside table (before first item) return {lo: null, hi: i1}; } else if (i1[key] < value) { lo = mid + 1; } else if (i0[key] > value) { hi = mid - 1; } else { return {lo: i0, hi: i1}; } } // given value is outside table (after last item) return {lo: i1, hi: null}; } /** * Linearly interpolates the given source `value` using the table items `skey` values and * returns the associated `tkey` value. For example, interpolate(table, 'time', 42, 'pos') * returns the position for a timestamp equal to 42. If value is out of bounds, values at * index [0, 1] or [n - 1, n] are used for the interpolation. */ function interpolate(table, skey, sval, tkey) { var range = lookup(table, skey, sval); // Note: the lookup table ALWAYS contains at least 2 items (min and max) var prev = !range.lo ? table[0] : !range.hi ? table[table.length - 2] : range.lo; var next = !range.lo ? table[1] : !range.hi ? table[table.length - 1] : range.hi; var span = next[skey] - prev[skey]; var ratio = span ? (sval - prev[skey]) / span : 0; var offset = (next[tkey] - prev[tkey]) * ratio; return prev[tkey] + offset; } /** * Convert the given value to a moment object using the given time options. * @see http://momentjs.com/docs/#/parsing/ */ function momentify(value, options) { var parser = options.parser; var format = options.parser || options.format; if (typeof parser === 'function') { return parser(value); } if (typeof value === 'string' && typeof format === 'string') { return moment(value, format); } if (!(value instanceof moment)) { value = moment(value); } if (value.isValid()) { return value; } // Labels are in an incompatible moment format and no `parser` has been provided. // The user might still use the deprecated `format` option to convert his inputs. if (typeof format === 'function') { return format(value); } return value; } function parse(input, scale) { if (helpers.isNullOrUndef(input)) { return null; } var options = scale.options.time; var value = momentify(scale.getRightValue(input), options); if (!value.isValid()) { return null; } if (options.round) { value.startOf(options.round); } return value.valueOf(); } /** * Returns the number of unit to skip to be able to display up to `capacity` number of ticks * in `unit` for the given `min` / `max` range and respecting the interval steps constraints. */ function determineStepSize(min, max, unit, capacity) { var range = max - min; var interval = INTERVALS[unit]; var milliseconds = interval.size; var steps = interval.steps; var i, ilen, factor; if (!steps) { return Math.ceil(range / ((capacity || 1) * milliseconds)); } for (i = 0, ilen = steps.length; i < ilen; ++i) { factor = steps[i]; if (Math.ceil(range / (milliseconds * factor)) <= capacity) { break; } } return factor; } /** * Figures out what unit results in an appropriate number of auto-generated ticks */ function determineUnitForAutoTicks(minUnit, min, max, capacity) { var ilen = UNITS.length; var i, interval, factor; for (i = UNITS.indexOf(minUnit); i < ilen - 1; ++i) { interval = INTERVALS[UNITS[i]]; factor = interval.steps ? interval.steps[interval.steps.length - 1] : MAX_INTEGER; if (interval.common && Math.ceil((max - min) / (factor * interval.size)) <= capacity) { return UNITS[i]; } } return UNITS[ilen - 1]; } /** * Figures out what unit to format a set of ticks with */ function determineUnitForFormatting(ticks, minUnit, min, max) { var duration = moment.duration(moment(max).diff(moment(min))); var ilen = UNITS.length; var i, unit; for (i = ilen - 1; i >= UNITS.indexOf(minUnit); i--) { unit = UNITS[i]; if (INTERVALS[unit].common && duration.as(unit) >= ticks.length) { return unit; } } return UNITS[minUnit ? UNITS.indexOf(minUnit) : 0]; } function determineMajorUnit(unit) { for (var i = UNITS.indexOf(unit) + 1, ilen = UNITS.length; i < ilen; ++i) { if (INTERVALS[UNITS[i]].common) { return UNITS[i]; } } } /** * Generates a maximum of `capacity` timestamps between min and max, rounded to the * `minor` unit, aligned on the `major` unit and using the given scale time `options`. * Important: this method can return ticks outside the min and max range, it's the * responsibility of the calling code to clamp values if needed. */ function generate(min, max, capacity, options) { var timeOpts = options.time; var minor = timeOpts.unit || determineUnitForAutoTicks(timeOpts.minUnit, min, max, capacity); var major = determineMajorUnit(minor); var stepSize = helpers.valueOrDefault(timeOpts.stepSize, timeOpts.unitStepSize); var weekday = minor === 'week' ? timeOpts.isoWeekday : false; var majorTicksEnabled = options.ticks.major.enabled; var interval = INTERVALS[minor]; var first = moment(min); var last = moment(max); var ticks = []; var time; if (!stepSize) { stepSize = determineStepSize(min, max, minor, capacity); } // For 'week' unit, handle the first day of week option if (weekday) { first = first.isoWeekday(weekday); last = last.isoWeekday(weekday); } // Align first/last ticks on unit first = first.startOf(weekday ? 'day' : minor); last = last.startOf(weekday ? 'day' : minor); // Make sure that the last tick include max if (last < max) { last.add(1, minor); } time = moment(first); if (majorTicksEnabled && major && !weekday && !timeOpts.round) { // Align the first tick on the previous `minor` unit aligned on the `major` unit: // we first aligned time on the previous `major` unit then add the number of full // stepSize there is between first and the previous major time. time.startOf(major); time.add(~~((first - time) / (interval.size * stepSize)) * stepSize, minor); } for (; time < last; time.add(stepSize, minor)) { ticks.push(+time); } ticks.push(+time); return ticks; } /** * Returns the right and left offsets from edges in the form of {left, right}. * Offsets are added when the `offset` option is true. */ function computeOffsets(table, ticks, min, max, options) { var left = 0; var right = 0; var upper, lower; if (options.offset && ticks.length) { if (!options.time.min) { upper = ticks.length > 1 ? ticks[1] : max; lower = ticks[0]; left = ( interpolate(table, 'time', upper, 'pos') - interpolate(table, 'time', lower, 'pos') ) / 2; } if (!options.time.max) { upper = ticks[ticks.length - 1]; lower = ticks.length > 1 ? ticks[ticks.length - 2] : min; right = ( interpolate(table, 'time', upper, 'pos') - interpolate(table, 'time', lower, 'pos') ) / 2; } } return {left: left, right: right}; } function ticksFromTimestamps(values, majorUnit) { var ticks = []; var i, ilen, value, major; for (i = 0, ilen = values.length; i < ilen; ++i) { value = values[i]; major = majorUnit ? value === +moment(value).startOf(majorUnit) : false; ticks.push({ value: value, major: major }); } return ticks; } module.exports = function(Chart) { var defaultConfig = { position: 'bottom', /** * Data distribution along the scale: * - 'linear': data are spread according to their time (distances can vary), * - 'series': data are spread at the same distance from each other. * @see https://github.com/chartjs/Chart.js/pull/4507 * @since 2.7.0 */ distribution: 'linear', /** * Scale boundary strategy (bypassed by min/max time options) * - `data`: make sure data are fully visible, ticks outside are removed * - `ticks`: make sure ticks are fully visible, data outside are truncated * @see https://github.com/chartjs/Chart.js/pull/4556 * @since 2.7.0 */ bounds: 'data', time: { parser: false, // false == a pattern string from http://momentjs.com/docs/#/parsing/string-format/ or a custom callback that converts its argument to a moment format: false, // DEPRECATED false == date objects, moment object, callback or a pattern string from http://momentjs.com/docs/#/parsing/string-format/ unit: false, // false == automatic or override with week, month, year, etc. round: false, // none, or override with week, month, year, etc. displayFormat: false, // DEPRECATED isoWeekday: false, // override week start day - see http://momentjs.com/docs/#/get-set/iso-weekday/ minUnit: 'millisecond', // defaults to unit's corresponding unitFormat below or override using pattern string from http://momentjs.com/docs/#/displaying/format/ displayFormats: { millisecond: 'h:mm:ss.SSS a', // 11:20:01.123 AM, second: 'h:mm:ss a', // 11:20:01 AM minute: 'h:mm a', // 11:20 AM hour: 'hA', // 5PM day: 'MMM D', // Sep 4 week: 'll', // Week 46, or maybe "[W]WW - YYYY" ? month: 'MMM YYYY', // Sept 2015 quarter: '[Q]Q - YYYY', // Q3 year: 'YYYY' // 2015 }, }, ticks: { autoSkip: false, /** * Ticks generation input values: * - 'auto': generates "optimal" ticks based on scale size and time options. * - 'data': generates ticks from data (including labels from data {t|x|y} objects). * - 'labels': generates ticks from user given `data.labels` values ONLY. * @see https://github.com/chartjs/Chart.js/pull/4507 * @since 2.7.0 */ source: 'auto', major: { enabled: false } } }; var TimeScale = Chart.Scale.extend({ initialize: function() { if (!moment) { throw new Error('Chart.js - Moment.js could not be found! You must include it before Chart.js to use the time scale. Download at https://momentjs.com'); } this.mergeTicksOptions(); Chart.Scale.prototype.initialize.call(this); }, update: function() { var me = this; var options = me.options; // DEPRECATIONS: output a message only one time per update if (options.time && options.time.format) { console.warn('options.time.format is deprecated and replaced by options.time.parser.'); } return Chart.Scale.prototype.update.apply(me, arguments); }, /** * Allows data to be referenced via 't' attribute */ getRightValue: function(rawValue) { if (rawValue && rawValue.t !== undefined) { rawValue = rawValue.t; } return Chart.Scale.prototype.getRightValue.call(this, rawValue); }, determineDataLimits: function() { var me = this; var chart = me.chart; var timeOpts = me.options.time; var min = MAX_INTEGER; var max = MIN_INTEGER; var timestamps = []; var datasets = []; var labels = []; var i, j, ilen, jlen, data, timestamp; // Convert labels to timestamps for (i = 0, ilen = chart.data.labels.length; i < ilen; ++i) { labels.push(parse(chart.data.labels[i], me)); } // Convert data to timestamps for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) { if (chart.isDatasetVisible(i)) { data = chart.data.datasets[i].data; // Let's consider that all data have the same format. if (helpers.isObject(data[0])) { datasets[i] = []; for (j = 0, jlen = data.length; j < jlen; ++j) { timestamp = parse(data[j], me); timestamps.push(timestamp); datasets[i][j] = timestamp; } } else { timestamps.push.apply(timestamps, labels); datasets[i] = labels.slice(0); } } else { datasets[i] = []; } } if (labels.length) { // Sort labels **after** data have been converted labels = arrayUnique(labels).sort(sorter); min = Math.min(min, labels[0]); max = Math.max(max, labels[labels.length - 1]); } if (timestamps.length) { timestamps = arrayUnique(timestamps).sort(sorter); min = Math.min(min, timestamps[0]); max = Math.max(max, timestamps[timestamps.length - 1]); } min = parse(timeOpts.min, me) || min; max = parse(timeOpts.max, me) || max; // In case there is no valid min/max, let's use today limits min = min === MAX_INTEGER ? +moment().startOf('day') : min; max = max === MIN_INTEGER ? +moment().endOf('day') + 1 : max; // Make sure that max is strictly higher than min (required by the lookup table) me.min = Math.min(min, max); me.max = Math.max(min + 1, max); // PRIVATE me._horizontal = me.isHorizontal(); me._table = []; me._timestamps = { data: timestamps, datasets: datasets, labels: labels }; }, buildTicks: function() { var me = this; var min = me.min; var max = me.max; var options = me.options; var timeOpts = options.time; var timestamps = []; var ticks = []; var i, ilen, timestamp; switch (options.ticks.source) { case 'data': timestamps = me._timestamps.data; break; case 'labels': timestamps = me._timestamps.labels; break; case 'auto': default: timestamps = generate(min, max, me.getLabelCapacity(min), options); } if (options.bounds === 'ticks' && timestamps.length) { min = timestamps[0]; max = timestamps[timestamps.length - 1]; } // Enforce limits with user min/max options min = parse(timeOpts.min, me) || min; max = parse(timeOpts.max, me) || max; // Remove ticks outside the min/max range for (i = 0, ilen = timestamps.length; i < ilen; ++i) { timestamp = timestamps[i]; if (timestamp >= min && timestamp <= max) { ticks.push(timestamp); } } me.min = min; me.max = max; // PRIVATE me._unit = timeOpts.unit || determineUnitForFormatting(ticks, timeOpts.minUnit, me.min, me.max); me._majorUnit = determineMajorUnit(me._unit); me._table = buildLookupTable(me._timestamps.data, min, max, options.distribution); me._offsets = computeOffsets(me._table, ticks, min, max, options); return ticksFromTimestamps(ticks, me._majorUnit); }, getLabelForIndex: function(index, datasetIndex) { var me = this; var data = me.chart.data; var timeOpts = me.options.time; var label = data.labels && index < data.labels.length ? data.labels[index] : ''; var value = data.datasets[datasetIndex].data[index]; if (helpers.isObject(value)) { label = me.getRightValue(value); } if (timeOpts.tooltipFormat) { label = momentify(label, timeOpts).format(timeOpts.tooltipFormat); } return label; }, /** * Function to format an individual tick mark * @private */ tickFormatFunction: function(tick, index, ticks, formatOverride) { var me = this; var options = me.options; var time = tick.valueOf(); var formats = options.time.displayFormats; var minorFormat = formats[me._unit]; var majorUnit = me._majorUnit; var majorFormat = formats[majorUnit]; var majorTime = tick.clone().startOf(majorUnit).valueOf(); var majorTickOpts = options.ticks.major; var major = majorTickOpts.enabled && majorUnit && majorFormat && time === majorTime; var label = tick.format(formatOverride ? formatOverride : major ? majorFormat : minorFormat); var tickOpts = major ? majorTickOpts : options.ticks.minor; var formatter = helpers.valueOrDefault(tickOpts.callback, tickOpts.userCallback); return formatter ? formatter(label, index, ticks) : label; }, convertTicksToLabels: function(ticks) { var labels = []; var i, ilen; for (i = 0, ilen = ticks.length; i < ilen; ++i) { labels.push(this.tickFormatFunction(moment(ticks[i].value), i, ticks)); } return labels; }, /** * @private */ getPixelForOffset: function(time) { var me = this; var size = me._horizontal ? me.width : me.height; var start = me._horizontal ? me.left : me.top; var pos = interpolate(me._table, 'time', time, 'pos'); return start + size * (me._offsets.left + pos) / (me._offsets.left + 1 + me._offsets.right); }, getPixelForValue: function(value, index, datasetIndex) { var me = this; var time = null; if (index !== undefined && datasetIndex !== undefined) { time = me._timestamps.datasets[datasetIndex][index]; } if (time === null) { time = parse(value, me); } if (time !== null) { return me.getPixelForOffset(time); } }, getPixelForTick: function(index) { var ticks = this.getTicks(); return index >= 0 && index < ticks.length ? this.getPixelForOffset(ticks[index].value) : null; }, getValueForPixel: function(pixel) { var me = this; var size = me._horizontal ? me.width : me.height; var start = me._horizontal ? me.left : me.top; var pos = (size ? (pixel - start) / size : 0) * (me._offsets.left + 1 + me._offsets.left) - me._offsets.right; var time = interpolate(me._table, 'pos', pos, 'time'); return moment(time); }, /** * Crude approximation of what the label width might be * @private */ getLabelWidth: function(label) { var me = this; var ticksOpts = me.options.ticks; var tickLabelWidth = me.ctx.measureText(label).width; var angle = helpers.toRadians(ticksOpts.maxRotation); var cosRotation = Math.cos(angle); var sinRotation = Math.sin(angle); var tickFontSize = helpers.valueOrDefault(ticksOpts.fontSize, defaults.global.defaultFontSize); return (tickLabelWidth * cosRotation) + (tickFontSize * sinRotation); }, /** * @private */ getLabelCapacity: function(exampleTime) { var me = this; var formatOverride = me.options.time.displayFormats.millisecond; // Pick the longest format for guestimation var exampleLabel = me.tickFormatFunction(moment(exampleTime), 0, [], formatOverride); var tickLabelWidth = me.getLabelWidth(exampleLabel); var innerWidth = me.isHorizontal() ? me.width : me.height; return Math.floor(innerWidth / tickLabelWidth); } }); Chart.scaleService.registerScaleType('time', TimeScale, defaultConfig); }; },{"1":1,"25":25,"45":45}]},{},[7])(7) }); ================================================ FILE: src/main/resources/static/admin/dist/js/plugins/chartjs2/docs/axes/cartesian/category.html ================================================ Category · GitBook

Category Cartesian Axis

If global configuration is used, labels are drawn from one of the label arrays included in the chart data. If only data.labels is defined, this will be used. If data.xLabels is defined and the axis is horizontal, this will be used. Similarly, if data.yLabels is defined and the axis is vertical, this property will be used. Using both xLabels and yLabels together can create a chart that uses strings for both the X and Y axes.

Specifying any of the settings above defines the x axis as type: category if not defined otherwise. For more fine-grained control of category labels it is also possible to add labels as part of the category axis definition. Doing so does not apply the global defaults.

Category Axis Definition

Globally:

let chart = new Chart(ctx, {
    type: ...
    data: {
        labels: ['January', 'February', 'March', 'April', 'May', 'June'],
        datasets: ...
    },
});

As part of axis definition:

let chart = new Chart(ctx, {
    type: ...
    data: ...
    options: {
        scales: {
            xAxes: [{
                type: 'category',
                labels: ['January', 'February', 'March', 'April', 'May', 'June'],
            }]
        }
    }
});

Tick Configuration Options

The category scale provides the following options for configuring tick marks. They are nested in the ticks sub object. These options extend the common tick configuration.

Name Type Default Description
labels Array[String] - An array of labels to display.
min String The minimum item to display. more...
max String The maximum item to display. more...

Min Max Configuration

For both the min and max properties, the value must be in the labels array. In the example below, the x axis would only display "March" through "June".

let chart = new Chart(ctx, {
    type: 'line',
    data: {
        datasets: [{
            data: [10, 20, 30, 40, 50, 60]
        }],
        labels: ['January', 'February', 'March', 'April', 'May', 'June'],
    },
    options: {
        scales: {
            xAxes: [{
                ticks: {
                    min: 'March'
                }
            }]
        }
    }
});

results matching ""

    No results matching ""

    ================================================ FILE: src/main/resources/static/admin/dist/js/plugins/chartjs2/docs/axes/cartesian/index.html ================================================ Cartesian · GitBook

    Cartesian Axes

    Axes that follow a cartesian grid are known as 'Cartesian Axes'. Cartesian axes are used for line, bar, and bubble charts. Four cartesian axes are included in Chart.js by default.

    Common Configuration

    All of the included cartesian axes support a number of common options.

    Name Type Default Description
    type String Type of scale being employed. Custom scales can be created and registered with a string key. This allows changing the type of an axis for a chart.
    position String Position of the axis in the chart. Possible values are: 'top', 'left', 'bottom', 'right'
    offset Boolean false If true, extra space is added to the both edges and the axis is scaled to fit into the chart area. This is set to true in the bar chart by default.
    id String The ID is used to link datasets and scale axes together. more...
    gridLines Object Grid line configuration. more...
    scaleLabel Object Scale title configuration. more...
    ticks Object Tick configuration. more...

    Tick Configuration

    The following options are common to all cartesian axes but do not apply to other axes.

    Name Type Default Description
    autoSkip Boolean true If true, automatically calculates how many labels that can be shown and hides labels accordingly. Turn it off to show all labels no matter what
    autoSkipPadding Number 0 Padding between the ticks on the horizontal axis when autoSkip is enabled. Note: Only applicable to horizontal scales.
    labelOffset Number 0 Distance in pixels to offset the label from the centre point of the tick (in the y direction for the x axis, and the x direction for the y axis). Note: this can cause labels at the edges to be cropped by the edge of the canvas
    maxRotation Number 90 Maximum rotation for tick labels when rotating to condense labels. Note: Rotation doesn't occur until necessary. Note: Only applicable to horizontal scales.
    minRotation Number 0 Minimum rotation for tick labels. Note: Only applicable to horizontal scales.
    mirror Boolean false Flips tick labels around axis, displaying the labels inside the chart instead of outside. Note: Only applicable to vertical scales.
    padding Number 10 Padding between the tick label and the axis. When set on a vertical axis, this applies in the horizontal (X) direction. When set on a horizontal axis, this applies in the vertical (Y) direction.

    Axis ID

    The properties dataset.xAxisID or dataset.yAxisID have to match the scale properties scales.xAxes.id or scales.yAxes.id. This is especially needed if multi-axes charts are used.

    var myChart = new Chart(ctx, {
        type: 'line',
        data: {
            datasets: [{
                // This dataset appears on the first axis
                yAxisID: 'first-y-axis'
            }, {
                // This dataset appears on the second axis
                yAxisID: 'second-y-axis'
            }]
        },
        options: {
            scales: {
                yAxes: [{
                    id: 'first-y-axis',
                    type: 'linear'
                }, {
                    id: 'second-y-axis',
                    type: 'linear'
                }]
            }
        }
    });
    

    Creating Multiple Axes

    With cartesian axes, it is possible to create multiple X and Y axes. To do so, you can add multiple configuration objects to the xAxes and yAxes properties. When adding new axes, it is important to ensure that you specify the type of the new axes as default types are not used in this case.

    In the example below, we are creating two Y axes. We then use the yAxisID property to map the datasets to their correct axes.

    var myChart = new Chart(ctx, {
        type: 'line',
        data: {
            datasets: [{
                data: [20, 50, 100, 75, 25, 0],
                label: 'Left dataset'
    
                // This binds the dataset to the left y axis
                yAxisID: 'left-y-axis'
            }, {
                data: [0.1, 0.5, 1.0, 2.0, 1.5, 0]
                label: 'Right dataset'
    
                // This binds the dataset to the right y axis
                yAxisID: 'right-y-axis',
            }],
            labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun']
        },
        options: {
            scales: {
                yAxes: [{
                    id: 'left-y-axis',
                    type: 'linear',
                    position: 'left'
                }, {
                    id: 'right-y-axis',
                    type: 'linear',
                    position: 'right'
                }]
            }
        }
    });
    

    results matching ""

      No results matching ""

      ================================================ FILE: src/main/resources/static/admin/dist/js/plugins/chartjs2/docs/axes/cartesian/linear.html ================================================ Linear · GitBook

      Linear Cartesian Axis

      The linear scale is use to chart numerical data. It can be placed on either the x or y axis. The scatter chart type automatically configures a line chart to use one of these scales for the x axis. As the name suggests, linear interpolation is used to determine where a value lies on the axis.

      Tick Configuration Options

      The following options are provided by the linear scale. They are all located in the ticks sub options. These options extend the common tick configuration.

      Name Type Default Description
      beginAtZero Boolean if true, scale will include 0 if it is not already included.
      min Number User defined minimum number for the scale, overrides minimum value from data. more...
      max Number User defined maximum number for the scale, overrides maximum value from data. more...
      maxTicksLimit Number 11 Maximum number of ticks and gridlines to show.
      stepSize Number User defined fixed step size for the scale. more...
      suggestedMax Number Adjustment used when calculating the maximum data value. more...
      suggestedMin Number Adjustment used when calculating the minimum data value. more...

      Axis Range Settings

      Given the number of axis range settings, it is important to understand how they all interact with each other.

      The suggestedMax and suggestedMin settings only change the data values that are used to scale the axis. These are useful for extending the range of the axis while maintaing the auto fit behaviour.

      let minDataValue = Math.min(mostNegativeValue, options.ticks.suggestedMin);
      let maxDataValue = Math.max(mostPositiveValue, options.ticks.suggestedMax);
      

      In this example, the largest positive value is 50, but the data maximum is expanded out to 100. However, because the lowest data value is below the suggestedMin setting, it is ignored.

      let chart = new Chart(ctx, {
          type: 'line',
          data: {
              datasets: [{
                  label: 'First dataset',
                  data: [0, 20, 40, 50]
              }],
              labels: ['January', 'February', 'March', 'April']
          },
          options: {
              scales: {
                  yAxes: [{
                      ticks: {
                          suggestedMin: 50
                          suggestedMax: 100
                      }
                  }]
              }
          }
      });
      

      In contrast to the suggested* settings, the min and max settings set explicit ends to the axes. When these are set, some data points may not be visible.

      Step Size

      If set, the scale ticks will be enumerated by multiple of stepSize, having one tick per increment. If not set, the ticks are labeled automatically using the nice numbers algorithm.

      This example sets up a chart with a y axis that creates ticks at 0, 0.5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5.

      let options = {
          scales: {
              yAxes: [{
                  ticks: {
                      max: 5,
                      min: 0,
                      stepSize: 0.5
                  }
              }]
          }
      };
      

      results matching ""

        No results matching ""

        ================================================ FILE: src/main/resources/static/admin/dist/js/plugins/chartjs2/docs/axes/cartesian/logarithmic.html ================================================ Logarithmic · GitBook

        Logarithmic Cartesian Axis

        The logarithmic scale is use to chart numerical data. It can be placed on either the x or y axis. As the name suggests, logarithmic interpolation is used to determine where a value lies on the axis.

        Tick Configuration Options

        The following options are provided by the logarithmic scale. They are all located in the ticks sub options. These options extend the common tick configuration.

        Name Type Default Description
        min Number User defined minimum number for the scale, overrides minimum value from data.
        max Number User defined maximum number for the scale, overrides maximum value from data.

        results matching ""

          No results matching ""

          ================================================ FILE: src/main/resources/static/admin/dist/js/plugins/chartjs2/docs/axes/cartesian/time.html ================================================ Time · GitBook

          Time Cartesian Axis

          The time scale is used to display times and dates. When building its ticks, it will automatically calculate the most comfortable unit base on the size of the scale.

          Data Sets

          Input Data

          The x-axis data points may additionally be specified via the t attribute when using the time scale.

          data: [{
              x: new Date(),
              y: 1
          }, {
              t: new Date(),
              y: 10
          }]
          

          Date Formats

          When providing data for the time scale, Chart.js supports all of the formats that Moment.js accepts. See Moment.js docs for details.

          Configuration Options

          The following options are provided by the time scale. You may also set options provided by the common tick configuration.

          Name Type Default Description
          distribution String linear How data is plotted. more...
          bounds String data Determines the scale bounds. more...
          ticks.source String auto How ticks are generated. more...
          time.displayFormats Object Sets how different time units are displayed. more...
          time.isoWeekday Boolean false If true and the unit is set to 'week', then the first day of the week will be Monday. Otherwise, it will be Sunday.
          time.max Time If defined, this will override the data maximum
          time.min Time If defined, this will override the data minimum
          time.parser String/Function Custom parser for dates. more...
          time.round String false If defined, dates will be rounded to the start of this unit. See Time Units below for the allowed units.
          time.tooltipFormat String The moment js format string to use for the tooltip.
          time.unit String false If defined, will force the unit to be a certain type. See Time Units section below for details.
          time.stepSize Number 1 The number of units between grid lines.
          time.minUnit String 'millisecond' The minimum display format to be used for a time unit.

          Time Units

          The following time measurements are supported. The names can be passed as strings to the time.unit config option to force a certain unit.

          • millisecond
          • second
          • minute
          • hour
          • day
          • week
          • month
          • quarter
          • year

          For example, to create a chart with a time scale that always displayed units per month, the following config could be used.

          var chart = new Chart(ctx, {
              type: 'line',
              data: data,
              options: {
                  scales: {
                      xAxes: [{
                          time: {
                              unit: 'month'
                          }
                      }]
                  }
              }
          })
          

          Display Formats

          The following display formats are used to configure how different time units are formed into strings for the axis tick marks. See moment.js for the allowable format strings.

          Name Default Example
          millisecond 'h:mm:ss.SSS a' 11:20:01.123 AM
          second 'h:mm:ss a' 11:20:01 AM
          minute 'h:mm a' 11:20 AM
          hour 'hA' 11AM
          day 'MMM D' Sep 4
          week 'll' Sep 4 2015
          month 'MMM YYYY' Sep 2015
          quarter '[Q]Q - YYYY' Q3 - 2015
          year 'YYYY' 2015

          For example, to set the display format for the 'quarter' unit to show the month and year, the following config would be passed to the chart constructor.

          var chart = new Chart(ctx, {
              type: 'line',
              data: data,
              options: {
                  scales: {
                      xAxes: [{
                          type: 'time',
                          time: {
                              displayFormats: {
                                  quarter: 'MMM YYYY'
                              }
                          }
                      }]
                  }
              }
          })
          

          Scale Distribution

          The distribution property controls the data distribution along the scale:

          • 'linear': data are spread according to their time (distances can vary)
          • 'series': data are spread at the same distance from each other
          var chart = new Chart(ctx, {
              type: 'line',
              data: data,
              options: {
                  scales: {
                      xAxes: [{
                          type: 'time',
                          distribution: 'series'
                      }]
                  }
              }
          })
          

          Scale Bounds

          The bounds property controls the scale boundary strategy (bypassed by min/max time options)

          • 'data': make sure data are fully visible, labels outside are removed
          • 'ticks': make sure ticks are fully visible, data outside are truncated

          Ticks Source

          The ticks.source property controls the ticks generation

          • 'auto': generates "optimal" ticks based on scale size and time options.
          • 'data': generates ticks from data (including labels from data {t|x|y} objects)
          • 'labels': generates ticks from user given data.labels values ONLY

          Parser

          If this property is defined as a string, it is interpreted as a custom format to be used by moment to parse the date.

          If this is a function, it must return a moment.js object given the appropriate data value.

          results matching ""

            No results matching ""

            ================================================ FILE: src/main/resources/static/admin/dist/js/plugins/chartjs2/docs/axes/index.html ================================================ Axes · GitBook

            Axes

            Axes are an integral part of a chart. They are used to determine how data maps to a pixel value on the chart. In a cartesian chart, there is 1 or more X axis and 1 or more Y axis to map points onto the 2 dimensional canvas. These axes are know as 'cartesian axes'.

            In a radial chart, such as a radar chart or a polar area chart, there is a single axis that maps points in the angular and radial directions. These are known as 'radial axes'.

            Scales in Chart.js >V2.0 are significantly more powerful, but also different than those of v1.0.

            • Multiple X & Y axes are supported.
            • A built-in label auto-skip feature detects would-be overlapping ticks and labels and removes every nth label to keep things displaying normally.
            • Scale titles are supported
            • New scale types can be extended without writing an entirely new chart type

            Common Configuration

            The following properties are common to all axes provided by Chart.js

            Name Type Default Description
            display Boolean true If set to false the axis is hidden from view. Overrides gridLines.display, scaleLabel.display, and ticks.display.
            callbacks Object Callback functions to hook into the axis lifecycle. more...
            weight Number 0 The weight used to sort the axis. Higher weights are further away from the chart area.

            Callbacks

            There are a number of config callbacks that can be used to change parameters in the scale at different points in the update process.

            Name Arguments Description
            beforeUpdate axis Callback called before the update process starts.
            beforeSetDimensions axis Callback that runs before dimensions are set.
            afterSetDimensions axis Callback that runs after dimensions are set.
            beforeDataLimits axis Callback that runs before data limits are determined.
            afterDataLimits axis Callback that runs after data limits are determined.
            beforeBuildTicks axis Callback that runs before ticks are created.
            afterBuildTicks axis Callback that runs after ticks are created. Useful for filtering ticks.
            beforeTickToLabelConversion axis Callback that runs before ticks are converted into strings.
            afterTickToLabelConversion axis Callback that runs after ticks are converted into strings.
            beforeCalculateTickRotation axis Callback that runs before tick rotation is determined.
            afterCalculateTickRotation axis Callback that runs after tick rotation is determined.
            beforeFit axis Callback that runs before the scale fits to the canvas.
            afterFit axis Callback that runs after the scale fits to the canvas.
            afterUpdate axis Callback that runs at the end of the update process.

            Updating Axis Defaults

            The default configuration for a scale can be easily changed using the scale service. All you need to do is to pass in a partial configuration that will be merged with the current scale default configuration to form the new default.

            For example, to set the minimum value of 0 for all linear scales, you would do the following. Any linear scales created after this time would now have a minimum of 0.

            Chart.scaleService.updateScaleDefaults('linear', {
                ticks: {
                    min: 0
                }
            });
            

            Creating New Axes

            To create a new axis, see the developer docs.

            results matching ""

              No results matching ""

              ================================================ FILE: src/main/resources/static/admin/dist/js/plugins/chartjs2/docs/axes/labelling.html ================================================ Labelling · GitBook

              Labeling Axes

              When creating a chart, you want to tell the viewer what data they are viewing. To do this, you need to label the axis.

              Scale Title Configuration

              The scale label configuration is nested under the scale configuration in the scaleLabel key. It defines options for the scale title. Note that this only applies to cartesian axes.

              Name Type Default Description
              display Boolean false If true, display the axis title.
              labelString String '' The text for the title. (i.e. "# of People" or "Response Choices").
              lineHeight Number/String 1.2 Height of an individual line of text (see MDN)
              fontColor Color '#666' Font color for scale title.
              fontFamily String "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif" Font family for the scale title, follows CSS font-family options.
              fontSize Number 12 Font size for scale title.
              fontStyle String 'normal' Font style for the scale title, follows CSS font-style options (i.e. normal, italic, oblique, initial, inherit).
              padding Number/Object 4 Padding to apply around scale labels. Only top and bottom are implemented.

              Creating Custom Tick Formats

              It is also common to want to change the tick marks to include information about the data type. For example, adding a dollar sign ('$'). To do this, you need to override the ticks.callback method in the axis configuration. In the following example, every label of the Y axis would be displayed with a dollar sign at the front..

              If the callback returns null or undefined the associated grid line will be hidden.

              var chart = new Chart(ctx, {
                  type: 'line',
                  data: data,
                  options: {
                      scales: {
                          yAxes: [{
                              ticks: {
                                  // Include a dollar sign in the ticks
                                  callback: function(value, index, values) {
                                      return '$' + value;
                                  }
                              }
                          }]
                      }
                  }
              });
              

              results matching ""

                No results matching ""

                ================================================ FILE: src/main/resources/static/admin/dist/js/plugins/chartjs2/docs/axes/radial/index.html ================================================ Radial · GitBook

                Radial Axes

                Radial axes are used specifically for the radar and polar area chart types. These axes overlay the chart area, rather than being positioned on one of the edges. One radial axis is included by default in Chart.js.

                results matching ""

                  No results matching ""

                  ================================================ FILE: src/main/resources/static/admin/dist/js/plugins/chartjs2/docs/axes/radial/linear.html ================================================ Linear · GitBook

                  Linear Radial Axis

                  The linear scale is use to chart numerical data. As the name suggests, linear interpolation is used to determine where a value lies in relation the center of the axis.

                  The following additional configuration options are provided by the radial linear scale.

                  Configuration Options

                  The axis has configuration properties for ticks, angle lines (line that appear in a radar chart outward from the center), pointLabels (labels around the edge in a radar chart). The following sections define each of the properties in those sections.

                  Name Type Description
                  angleLines Object Angle line configuration. more...
                  gridLines Object Grid line configuration. more...
                  pointLabels Object Point label configuration. more...
                  ticks Object Tick configuration. more...

                  Tick Options

                  The following options are provided by the linear scale. They are all located in the ticks sub options. The common tick configuration options are supported by this axis.

                  Name Type Default Description
                  backdropColor Color 'rgba(255, 255, 255, 0.75)' Color of label backdrops
                  backdropPaddingX Number 2 Horizontal padding of label backdrop.
                  backdropPaddingY Number 2 Vertical padding of label backdrop.
                  beginAtZero Boolean false if true, scale will include 0 if it is not already included.
                  min Number User defined minimum number for the scale, overrides minimum value from data. more...
                  max Number User defined maximum number for the scale, overrides maximum value from data. more...
                  maxTicksLimit Number 11 Maximum number of ticks and gridlines to show.
                  stepSize Number User defined fixed step size for the scale. more...
                  suggestedMax Number Adjustment used when calculating the maximum data value. more...
                  suggestedMin Number Adjustment used when calculating the minimum data value. more...
                  showLabelBackdrop Boolean true If true, draw a background behind the tick labels

                  Axis Range Settings

                  Given the number of axis range settings, it is important to understand how they all interact with each other.

                  The suggestedMax and suggestedMin settings only change the data values that are used to scale the axis. These are useful for extending the range of the axis while maintaing the auto fit behaviour.

                  let minDataValue = Math.min(mostNegativeValue, options.ticks.suggestedMin);
                  let maxDataValue = Math.max(mostPositiveValue, options.ticks.suggestedMax);
                  

                  In this example, the largest positive value is 50, but the data maximum is expanded out to 100. However, because the lowest data value is below the suggestedMin setting, it is ignored.

                  let chart = new Chart(ctx, {
                      type: 'radar',
                      data: {
                          datasets: [{
                              label: 'First dataset',
                              data: [0, 20, 40, 50]
                          }],
                          labels: ['January', 'February', 'March', 'April']
                      },
                      options: {
                          scale: {
                              ticks: {
                                  suggestedMin: 50
                                  suggestedMax: 100
                              }
                          }
                      }
                  });
                  

                  In contrast to the suggested* settings, the min and max settings set explicit ends to the axes. When these are set, some data points may not be visible.

                  Step Size

                  If set, the scale ticks will be enumerated by multiple of stepSize, having one tick per increment. If not set, the ticks are labeled automatically using the nice numbers algorithm.

                  This example sets up a chart with a y axis that creates ticks at 0, 0.5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5.

                  let options = {
                      scales: {
                          yAxes: [{
                              ticks: {
                                  max: 5,
                                  min: 0,
                                  stepSize: 0.5
                              }
                          }]
                      }
                  };
                  

                  Angle Line Options

                  The following options are used to configure angled lines that radiate from the center of the chart to the point labels. They can be found in the angleLines sub options. Note that these options only apply if angleLines.display is true.

                  Name Type Default Description
                  display Boolean true if true, angle lines are shown
                  color Color rgba(0, 0, 0, 0.1) Color of angled lines
                  lineWidth Number 1 Width of angled lines

                  Point Label Options

                  The following options are used to configure the point labels that are shown on the perimeter of the scale. They can be found in the pointLabels sub options. Note that these options only apply if pointLabels.display is true.

                  Name Type Default Description
                  callback Function Callback function to transform data labels to point labels. The default implementation simply returns the current string.
                  fontColor Color '#666' Font color for point labels.
                  fontFamily String "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif" Font family to use when rendering labels.
                  fontSize Number 10 font size in pixels
                  fontStyle String 'normal' Font style to use when rendering point labels.

                  results matching ""

                    No results matching ""

                    ================================================ FILE: src/main/resources/static/admin/dist/js/plugins/chartjs2/docs/axes/styling.html ================================================ Styling · GitBook

                    Styling

                    There are a number of options to allow styling an axis. There are settings to control grid lines and ticks.

                    Grid Line Configuration

                    The grid line configuration is nested under the scale configuration in the gridLines key. It defines options for the grid lines that run perpendicular to the axis.

                    Name Type Default Description
                    display Boolean true If false, do not display grid lines for this axis.
                    color Color/Color[] 'rgba(0, 0, 0, 0.1)' The color of the grid lines. If specified as an array, the first color applies to the first grid line, the second to the second grid line and so on.
                    borderDash Number[] [] Length and spacing of dashes on grid lines. See MDN
                    borderDashOffset Number 0 Offset for line dashes. See MDN
                    lineWidth Number/Number[] 1 Stroke width of grid lines.
                    drawBorder Boolean true If true, draw border at the edge between the axis and the chart area.
                    drawOnChartArea Boolean true If true, draw lines on the chart area inside the axis lines. This is useful when there are multiple axes and you need to control which grid lines are drawn.
                    drawTicks Boolean true If true, draw lines beside the ticks in the axis area beside the chart.
                    tickMarkLength Number 10 Length in pixels that the grid lines will draw into the axis area.
                    zeroLineWidth Number 1 Stroke width of the grid line for the first index (index 0).
                    zeroLineColor Color 'rgba(0, 0, 0, 0.25)' Stroke color of the grid line for the first index (index 0).
                    zeroLineBorderDash Number[] [] Length and spacing of dashes of the grid line for the first index (index 0). See MDN
                    zeroLineBorderDashOffset Number 0 Offset for line dashes of the grid line for the first index (index 0). See MDN
                    offsetGridLines Boolean false If true, grid lines will be shifted to be between labels. This is set to true in the bar chart by default.

                    Tick Configuration

                    The tick configuration is nested under the scale configuration in the ticks key. It defines options for the tick marks that are generated by the axis.

                    Name Type Default Description
                    callback Function Returns the string representation of the tick value as it should be displayed on the chart. See callback.
                    display Boolean true If true, show tick marks
                    fontColor Color '#666' Font color for tick labels.
                    fontFamily String "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif" Font family for the tick labels, follows CSS font-family options.
                    fontSize Number 12 Font size for the tick labels.
                    fontStyle String 'normal' Font style for the tick labels, follows CSS font-style options (i.e. normal, italic, oblique, initial, inherit).
                    reverse Boolean false Reverses order of tick labels.
                    minor object {} Minor ticks configuration. Ommited options are inherited from options above.
                    major object {} Major ticks configuration. Ommited options are inherited from options above.

                    Minor Tick Configuration

                    The minorTick configuration is nested under the ticks configuration in the minor key. It defines options for the minor tick marks that are generated by the axis. Omitted options are inherited from ticks configuration.

                    Name Type Default Description
                    callback Function Returns the string representation of the tick value as it should be displayed on the chart. See callback.
                    fontColor Color '#666' Font color for tick labels.
                    fontFamily String "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif" Font family for the tick labels, follows CSS font-family options.
                    fontSize Number 12 Font size for the tick labels.
                    fontStyle String 'normal' Font style for the tick labels, follows CSS font-style options (i.e. normal, italic, oblique, initial, inherit).

                    Major Tick Configuration

                    The majorTick configuration is nested under the ticks configuration in the major key. It defines options for the major tick marks that are generated by the axis. Omitted options are inherited from ticks configuration.

                    Name Type Default Description
                    callback Function Returns the string representation of the tick value as it should be displayed on the chart. See callback.
                    fontColor Color '#666' Font color for tick labels.
                    fontFamily String "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif" Font family for the tick labels, follows CSS font-family options.
                    fontSize Number 12 Font size for the tick labels.
                    fontStyle String 'normal' Font style for the tick labels, follows CSS font-style options (i.e. normal, italic, oblique, initial, inherit).

                    results matching ""

                      No results matching ""

                      ================================================ FILE: src/main/resources/static/admin/dist/js/plugins/chartjs2/docs/charts/area.html ================================================ Area · GitBook

                      Area Charts

                      Both line and radar charts support a fill option on the dataset object which can be used to create area between two datasets or a dataset and a boundary, i.e. the scale origin, start or end (see filling modes).

                      Note: this feature is implemented by the filler plugin.

                      Filling modes

                      Mode Type Values
                      Absolute dataset index 1 Number 1, 2, 3, ...
                      Relative dataset index 1 String '-1', '-2', '+1', ...
                      Boundary 2 String 'start', 'end', 'origin'
                      Disabled 3 Boolean false

                      1 dataset filling modes have been introduced in version 2.6.0
                      2 prior version 2.6.0, boundary values was 'zero', 'top', 'bottom' (deprecated)
                      3 for backward compatibility, fill: true (default) is equivalent to fill: 'origin'

                      Example

                      new Chart(ctx, {
                          data: {
                              datasets: [
                                  {fill: 'origin'},      // 0: fill to 'origin'
                                  {fill: '+2'},          // 1: fill to dataset 3
                                  {fill: 1},             // 2: fill to dataset 1
                                  {fill: false},         // 3: no fill
                                  {fill: '-2'}           // 4: fill to dataset 2
                              ]
                          }
                      })
                      

                      Configuration

                      Option Type Default Description
                      plugins.filler.propagate Boolean true Fill propagation when target is hidden

                      propagate

                      Boolean (default: true)

                      If true, the fill area will be recursively extended to the visible target defined by the fill value of hidden dataset targets:

                      Example

                      new Chart(ctx, {
                          data: {
                              datasets: [
                                  {fill: 'origin'},   // 0: fill to 'origin'
                                  {fill: '-1'},       // 1: fill to dataset 0
                                  {fill: 1},          // 2: fill to dataset 1
                                  {fill: false},      // 3: no fill
                                  {fill: '-2'}        // 4: fill to dataset 2
                              ]
                          },
                          options: {
                              plugins: {
                                  filler: {
                                      propagate: true
                                  }
                              }
                          }
                      })
                      

                      propagate: true:

                      • if dataset 2 is hidden, dataset 4 will fill to dataset 1
                      • if dataset 2 and 1 are hidden, dataset 4 will fill to 'origin'

                      propagate: false:

                      • if dataset 2 and/or 4 are hidden, dataset 4 will not be filled

                      results matching ""

                        No results matching ""

                        ================================================ FILE: src/main/resources/static/admin/dist/js/plugins/chartjs2/docs/charts/bar.html ================================================ Bar · GitBook

                        Bar

                        A bar chart provides a way of showing data values represented as vertical bars. It is sometimes used to show trend data, and the comparison of multiple data sets side by side.

                        Example Usage

                        var myBarChart = new Chart(ctx, {
                            type: 'bar',
                            data: data,
                            options: options
                        });
                        

                        Dataset Properties

                        The bar chart allows a number of properties to be specified for each dataset. These are used to set display properties for a specific dataset. For example, the colour of the bars is generally set this way.

                        Some properties can be specified as an array. If these are set to an array value, the first value applies to the first bar, the second value to the second bar, and so on.

                        Name Type Description
                        label String The label for the dataset which appears in the legend and tooltips.
                        xAxisID String The ID of the x axis to plot this dataset on. If not specified, this defaults to the ID of the first found x axis
                        yAxisID String The ID of the y axis to plot this dataset on. If not specified, this defaults to the ID of the first found y axis.
                        backgroundColor Color/Color[] The fill color of the bar. See Colors
                        borderColor Color/Color[] The color of the bar border. See Colors
                        borderWidth Number/Number[] The stroke width of the bar in pixels.
                        borderSkipped String Which edge to skip drawing the border for. more...
                        hoverBackgroundColor Color/Color[] The fill colour of the bars when hovered.
                        hoverBorderColor Color/Color[] The stroke colour of the bars when hovered.
                        hoverBorderWidth Number/Number[] The stroke width of the bars when hovered.

                        borderSkipped

                        This setting is used to avoid drawing the bar stroke at the base of the fill. In general, this does not need to be changed except when creating chart types that derive from a bar chart.

                        Options are:

                        • 'bottom'
                        • 'left'
                        • 'top'
                        • 'right'

                        Configuration Options

                        The bar chart defines the following configuration options. These options are merged with the global chart configuration options, Chart.defaults.global, to form the options passed to the chart.

                        Name Type Default Description
                        barPercentage Number 0.9 Percent (0-1) of the available width each bar should be within the category width. 1.0 will take the whole category width and put the bars right next to each other. more...
                        categoryPercentage Number 0.8 Percent (0-1) of the available width each category should be within the sample width. more...
                        barThickness Number Manually set width of each bar in pixels. If not set, the base sample widths are calculated automatically so that they take the full available widths without overlap. Then, the bars are sized using barPercentage and categoryPercentage.
                        maxBarThickness Number Set this to ensure that bars are not sized thicker than this.
                        gridLines.offsetGridLines Boolean true If true, the bars for a particular data point fall between the grid lines. The grid line will move to the left by one half of the tick interval. If false, the grid line will go right down the middle of the bars. more...

                        offsetGridLines

                        If true, the bars for a particular data point fall between the grid lines. The grid line will move to the left by one half of the tick interval, which is the space between the grid lines. If false, the grid line will go right down the middle of the bars. This is set to true for a bar chart while false for other charts by default.

                        This setting applies to the axis configuration. If axes are added to the chart, this setting will need to be set for each new axis.

                        options = {
                            scales: {
                                xAxes: [{
                                    gridLines: {
                                        offsetGridLines: true
                                    }
                                }]
                            }
                        }
                        

                        Default Options

                        It is common to want to apply a configuration setting to all created bar charts. The global bar chart settings are stored in Chart.defaults.bar. Changing the global options only affects charts created after the change. Existing charts are not changed.

                        barPercentage vs categoryPercentage

                        The following shows the relationship between the bar percentage option and the category percentage option.

                        // categoryPercentage: 1.0
                        // barPercentage: 1.0
                        Bar:        | 1.0 | 1.0 |
                        Category:   |    1.0    |
                        Sample:     |===========|
                        
                        // categoryPercentage: 1.0
                        // barPercentage: 0.5
                        Bar:          |.5|  |.5|
                        Category:  |      1.0     |
                        Sample:    |==============|
                        
                        // categoryPercentage: 0.5
                        // barPercentage: 1.0
                        Bar:            |1.||1.|
                        Category:       |  .5  |
                        Sample:     |==============|
                        

                        Data Structure

                        The data property of a dataset for a bar chart is specified as a an array of numbers. Each point in the data array corresponds to the label at the same index on the x axis.

                        data: [20, 10]
                        

                        You can also specify the dataset as x/y coordinates when using the time scale.

                        data: [{x:'2016-12-25', y:20}, {x:'2016-12-26', y:10}]
                        

                        Stacked Bar Chart

                        Bar charts can be configured into stacked bar charts by changing the settings on the X and Y axes to enable stacking. Stacked bar charts can be used to show how one data series is made up of a number of smaller pieces.

                        var stackedBar = new Chart(ctx, {
                            type: 'bar',
                            data: data,
                            options: {
                                scales: {
                                    xAxes: [{
                                        stacked: true
                                    }],
                                    yAxes: [{
                                        stacked: true
                                    }]
                                }
                            }
                        });
                        

                        Dataset Properties

                        The following dataset properties are specific to stacked bar charts.

                        Name Type Description
                        stack String The ID of the group to which this dataset belongs to (when stacked, each group will be a separate stack)

                        Horizontal Bar Chart

                        A horizontal bar chart is a variation on a vertical bar chart. It is sometimes used to show trend data, and the comparison of multiple data sets side by side.

                        Example

                        var myBarChart = new Chart(ctx, {
                            type: 'horizontalBar',
                            data: data,
                            options: options
                        });
                        

                        Config Options

                        The configuration options for the horizontal bar chart are the same as for the bar chart. However, any options specified on the x axis in a bar chart, are applied to the y axis in a horizontal bar chart.

                        The default horizontal bar configuration is specified in Chart.defaults.horizontalBar.

                        results matching ""

                          No results matching ""

                          ================================================ FILE: src/main/resources/static/admin/dist/js/plugins/chartjs2/docs/charts/bubble.html ================================================ Bubble · GitBook

                          Bubble Chart

                          A bubble chart is used to display three dimensions of data at the same time. The location of the bubble is determined by the first two dimensions and the corresponding horizontal and vertical axes. The third dimension is represented by the size of the individual bubbles.

                          Example Usage

                          // For a bubble chart
                          var myBubbleChart = new Chart(ctx,{
                              type: 'bubble',
                              data: data,
                              options: options
                          });
                          

                          Dataset Properties

                          The bubble chart allows a number of properties to be specified for each dataset. These are used to set display properties for a specific dataset. For example, the colour of the bubbles is generally set this way.

                          Name Type Scriptable Indexable Default
                          backgroundColor Color Yes Yes 'rgba(0,0,0,0.1)'
                          borderColor Color Yes Yes 'rgba(0,0,0,0.1)'
                          borderWidth Number Yes Yes 3
                          data Object[] - - required
                          hoverBackgroundColor Color Yes Yes undefined
                          hoverBorderColor Color Yes Yes undefined
                          hoverBorderWidth Number Yes Yes 1
                          hoverRadius Number Yes Yes 4
                          hitRadius Number Yes Yes 1
                          label String - - undefined
                          pointStyle String Yes Yes circle
                          radius Number Yes Yes 3

                          Labeling

                          label defines the text associated to the dataset and which appears in the legend and tooltips.

                          Styling

                          The style of each bubble can be controlled with the following properties:

                          Name Description
                          backgroundColor bubble background color
                          borderColor bubble border color
                          borderWidth bubble border width (in pixels)
                          pointStyle bubble shape style
                          radius bubble radius (in pixels)

                          All these values, if undefined, fallback to the associated elements.point.* options.

                          Interactions

                          The interaction with each bubble can be controlled with the following properties:

                          Name Description
                          hoverBackgroundColor bubble background color when hovered
                          hoverBorderColor bubble border color hovered
                          hoverBorderWidth bubble border width when hovered (in pixels)
                          hoverRadius bubble additional radius when hovered (in pixels)
                          hitRadius bubble additional radius for hit detection (in pixels)

                          All these values, if undefined, fallback to the associated elements.point.* options.

                          Default Options

                          We can also change the default values for the Bubble chart type. Doing so will give all bubble charts created after this point the new defaults. The default configuration for the bubble chart can be accessed at Chart.defaults.bubble.

                          Data Structure

                          Bubble chart datasets need to contain a data array of points, each points represented by an object containing the following properties:

                          {
                              // X Value
                              x: <Number>,
                          
                              // Y Value
                              y: <Number>,
                          
                              // Bubble radius in pixels (not scaled).
                              r: <Number>
                          }
                          

                          Important: the radius property, r is not scaled by the chart, it is the raw radius in pixels of the bubble that is drawn on the canvas.

                          results matching ""

                            No results matching ""

                            ================================================ FILE: src/main/resources/static/admin/dist/js/plugins/chartjs2/docs/charts/doughnut.html ================================================ Doughnut & Pie · GitBook

                            Doughnut and Pie

                            Pie and doughnut charts are probably the most commonly used charts. They are divided into segments, the arc of each segment shows the proportional value of each piece of data.

                            They are excellent at showing the relational proportions between data.

                            Pie and doughnut charts are effectively the same class in Chart.js, but have one different default value - their cutoutPercentage. This equates what percentage of the inner should be cut out. This defaults to 0 for pie charts, and 50 for doughnuts.

                            They are also registered under two aliases in the Chart core. Other than their different default value, and different alias, they are exactly the same.

                            Example Usage

                            // For a pie chart
                            var myPieChart = new Chart(ctx,{
                                type: 'pie',
                                data: data,
                                options: options
                            });
                            
                            // And for a doughnut chart
                            var myDoughnutChart = new Chart(ctx, {
                                type: 'doughnut',
                                data: data,
                                options: options
                            });
                            

                            Dataset Properties

                            The doughnut/pie chart allows a number of properties to be specified for each dataset. These are used to set display properties for a specific dataset. For example, the colour of a the dataset's arc are generally set this way.

                            Name Type Description
                            label String The label for the dataset which appears in the legend and tooltips.
                            backgroundColor Color[] The fill color of the arcs in the dataset. See Colors
                            borderColor Color[] The border color of the arcs in the dataset. See Colors
                            borderWidth Number[] The border width of the arcs in the dataset.
                            hoverBackgroundColor Color[] The fill colour of the arcs when hovered.
                            hoverBorderColor Color[] The stroke colour of the arcs when hovered.
                            hoverBorderWidth Number[] The stroke width of the arcs when hovered.

                            Config Options

                            These are the customisation options specific to Pie & Doughnut charts. These options are merged with the global chart configuration options, and form the options of the chart.

                            Name Type Default Description
                            cutoutPercentage Number 50 - for doughnut, 0 - for pie The percentage of the chart that is cut out of the middle.
                            rotation Number -0.5 * Math.PI Starting angle to draw arcs from.
                            circumference Number 2 * Math.PI Sweep to allow arcs to cover
                            animation.animateRotate Boolean true If true, the chart will animate in with a rotation animation. This property is in the options.animation object.
                            animation.animateScale Boolean false If true, will animate scaling the chart from the center outwards.

                            Default Options

                            We can also change these default values for each Doughnut type that is created, this object is available at Chart.defaults.doughnut. Pie charts also have a clone of these defaults available to change at Chart.defaults.pie, with the only difference being cutoutPercentage being set to 0.

                            Data Structure

                            For a pie chart, datasets need to contain an array of data points. The data points should be a number, Chart.js will total all of the numbers and calculate the relative proportion of each.

                            You also need to specify an array of labels so that tooltips appear correctly

                            data = {
                                datasets: [{
                                    data: [10, 20, 30]
                                }],
                            
                                // These labels appear in the legend and in the tooltips when hovering different arcs
                                labels: [
                                    'Red',
                                    'Yellow',
                                    'Blue'
                                ]
                            };
                            

                            results matching ""

                              No results matching ""

                              ================================================ FILE: src/main/resources/static/admin/dist/js/plugins/chartjs2/docs/charts/index.html ================================================ Charts · GitBook

                              Charts

                              Chart.js comes with built-in chart types:

                              Area charts can be built from a line or radar chart using the dataset fill option.

                              To create a new chart type, see the developer notes

                              results matching ""

                                No results matching ""

                                ================================================ FILE: src/main/resources/static/admin/dist/js/plugins/chartjs2/docs/charts/line.html ================================================ Line · GitBook

                                Line

                                A line chart is a way of plotting data points on a line. Often, it is used to show trend data, or the comparison of two data sets.

                                Example Usage

                                var myLineChart = new Chart(ctx, {
                                    type: 'line',
                                    data: data,
                                    options: options
                                });
                                

                                Dataset Properties

                                The line chart allows a number of properties to be specified for each dataset. These are used to set display properties for a specific dataset. For example, the colour of a line is generally set this way.

                                All point* properties can be specified as an array. If these are set to an array value, the first value applies to the first point, the second value to the second point, and so on.

                                Name Type Description
                                label String The label for the dataset which appears in the legend and tooltips.
                                xAxisID String The ID of the x axis to plot this dataset on. If not specified, this defaults to the ID of the first found x axis
                                yAxisID String The ID of the y axis to plot this dataset on. If not specified, this defaults to the ID of the first found y axis.
                                backgroundColor Color The fill color under the line. See Colors
                                borderColor Color The color of the line. See Colors
                                borderWidth Number The width of the line in pixels.
                                borderDash Number[] Length and spacing of dashes. See MDN
                                borderDashOffset Number Offset for line dashes. See MDN
                                borderCapStyle String Cap style of the line. See MDN
                                borderJoinStyle String Line joint style. See MDN
                                cubicInterpolationMode String Algorithm used to interpolate a smooth curve from the discrete data points. more...
                                fill Boolean/String How to fill the area under the line. See area charts
                                lineTension Number Bezier curve tension of the line. Set to 0 to draw straightlines. This option is ignored if monotone cubic interpolation is used.
                                pointBackgroundColor Color/Color[] The fill color for points.
                                pointBorderColor Color/Color[] The border color for points.
                                pointBorderWidth Number/Number[] The width of the point border in pixels.
                                pointRadius Number/Number[] The radius of the point shape. If set to 0, the point is not rendered.
                                pointStyle String/String[]/Image/Image[] Style of the point. more...
                                pointHitRadius Number/Number[] The pixel size of the non-displayed point that reacts to mouse events.
                                pointHoverBackgroundColor Color/Color[] Point background color when hovered.
                                pointHoverBorderColor Color/Color[] Point border color when hovered.
                                pointHoverBorderWidth Number/Number[] Border width of point when hovered.
                                pointHoverRadius Number/Number[] The radius of the point when hovered.
                                showLine Boolean If false, the line is not drawn for this dataset.
                                spanGaps Boolean If true, lines will be drawn between points with no or null data. If false, points with NaN data will create a break in the line
                                steppedLine Boolean/String If the line is shown as a stepped line. more...

                                cubicInterpolationMode

                                The following interpolation modes are supported:

                                • 'default'
                                • 'monotone'.

                                The 'default' algorithm uses a custom weighted cubic interpolation, which produces pleasant curves for all types of datasets.

                                The 'monotone' algorithm is more suited to y = f(x) datasets : it preserves monotonicity (or piecewise monotonicity) of the dataset being interpolated, and ensures local extremums (if any) stay at input data points.

                                If left untouched (undefined), the global options.elements.line.cubicInterpolationMode property is used.

                                Stepped Line

                                The following values are supported for steppedLine:

                                • false: No Step Interpolation (default)
                                • true: Step-before Interpolation (eq. 'before')
                                • 'before': Step-before Interpolation
                                • 'after': Step-after Interpolation

                                If the steppedLine value is set to anything other than false, lineTension will be ignored.

                                Configuration Options

                                The line chart defines the following configuration options. These options are merged with the global chart configuration options, Chart.defaults.global, to form the options passed to the chart.

                                Name Type Default Description
                                showLines Boolean true If false, the lines between points are not drawn.
                                spanGaps Boolean false If false, NaN data causes a break in the line.

                                Default Options

                                It is common to want to apply a configuration setting to all created line charts. The global line chart settings are stored in Chart.defaults.line. Changing the global options only affects charts created after the change. Existing charts are not changed.

                                For example, to configure all line charts with spanGaps = true you would do:

                                Chart.defaults.line.spanGaps = true;
                                

                                Data Structure

                                The data property of a dataset for a line chart can be passed in two formats.

                                Number[]

                                data: [20, 10]
                                

                                When the data array is an array of numbers, the x axis is generally a category. The points are placed onto the axis using their position in the array. When a line chart is created with a category axis, the labels property of the data object must be specified.

                                Point[]

                                data: [{
                                        x: 10,
                                        y: 20
                                    }, {
                                        x: 15,
                                        y: 10
                                    }]
                                

                                This alternate is used for sparse datasets, such as those in scatter charts. Each data point is specified using an object containing x and y properties.

                                Stacked Area Chart

                                Line charts can be configured into stacked area charts by changing the settings on the y axis to enable stacking. Stacked area charts can be used to show how one data trend is made up of a number of smaller pieces.

                                var stackedLine = new Chart(ctx, {
                                    type: 'line',
                                    data: data,
                                    options: {
                                        scales: {
                                            yAxes: [{
                                                stacked: true
                                            }]
                                        }
                                    }
                                });
                                

                                High Performance Line Charts

                                When charting a lot of data, the chart render time may start to get quite large. In that case, the following strategies can be used to improve performance.

                                Data Decimation

                                Decimating your data will achieve the best results. When there is a lot of data to display on the graph, it doesn't make sense to show tens of thousands of data points on a graph that is only a few hundred pixels wide.

                                There are many approaches to data decimation and selection of an algorithm will depend on your data and the results you want to achieve. For instance, min/max decimation will preserve peaks in your data but could require up to 4 points for each pixel. This type of decimation would work well for a very noisy signal where you need to see data peaks.

                                Disable Bezier Curves

                                If you are drawing lines on your chart, disabling bezier curves will improve render times since drawing a straight line is more performant than a bezier curve.

                                To disable bezier curves for an entire chart:

                                new Chart(ctx, {
                                    type: 'line',
                                    data: data,
                                    options: {
                                        elements: {
                                            line: {
                                                tension: 0, // disables bezier curves
                                            }
                                        }
                                    }
                                });
                                

                                Disable Line Drawing

                                If you have a lot of data points, it can be more performant to disable rendering of the line for a dataset and only draw points. Doing this means that there is less to draw on the canvas which will improve render performance.

                                To disable lines:

                                new Chart(ctx, {
                                    type: 'line',
                                    data: {
                                        datasets: [{
                                            showLine: false, // disable for a single dataset
                                        }]
                                    },
                                    options: {
                                        showLines: false, // disable for all datasets
                                    }
                                });
                                

                                Disable Animations

                                If your charts have long render times, it is a good idea to disable animations. Doing so will mean that the chart needs to only be rendered once during an update instead of multiple times. This will have the effect of reducing CPU usage and improving general page performance.

                                To disable animations

                                new Chart(ctx, {
                                    type: 'line',
                                    data: data,
                                    options: {
                                        animation: {
                                            duration: 0, // general animation time
                                        },
                                        hover: {
                                            animationDuration: 0, // duration of animations when hovering an item
                                        },
                                        responsiveAnimationDuration: 0, // animation duration after a resize
                                    }
                                });
                                

                                results matching ""

                                  No results matching ""

                                  ================================================ FILE: src/main/resources/static/admin/dist/js/plugins/chartjs2/docs/charts/mixed.html ================================================ Mixed · GitBook

                                  Mixed Chart Types

                                  With Chart.js, it is possible to create mixed charts that are a combination of two or more different chart types. A common example is a bar chart that also includes a line dataset.

                                  Creating a mixed chart starts with the initialization of a basic chart.

                                  var myChart = new Chart(ctx, {
                                    type: 'bar',
                                    data: data,
                                    options: options
                                  });
                                  

                                  At this point we have a standard bar chart. Now we need to convert one of the datasets to a line dataset.

                                  var mixedChart = new Chart(ctx, {
                                    type: 'bar',
                                    data: {
                                      datasets: [{
                                            label: 'Bar Dataset',
                                            data: [10, 20, 30, 40]
                                          }, {
                                            label: 'Line Dataset',
                                            data: [50, 50, 50, 50],
                                  
                                            // Changes this dataset to become a line
                                            type: 'line'
                                          }],
                                      labels: ['January', 'February', 'March', 'April']
                                    },
                                    options: options
                                  });
                                  

                                  At this point we have a chart rendering how we'd like. It's important to note that the default options for a line chart are not merged in this case. Only the options for the default type are merged in. In this case, that means that the default options for a bar chart are merged because that is the type specified by the type field.

                                  results matching ""

                                    No results matching ""

                                    ================================================ FILE: src/main/resources/static/admin/dist/js/plugins/chartjs2/docs/charts/polar.html ================================================ Polar Area · GitBook

                                    Polar Area

                                    Polar area charts are similar to pie charts, but each segment has the same angle - the radius of the segment differs depending on the value.

                                    This type of chart is often useful when we want to show a comparison data similar to a pie chart, but also show a scale of values for context.

                                    Example Usage

                                    new Chart(ctx, {
                                        data: data,
                                        type: 'polarArea',
                                        options: options
                                    });
                                    

                                    Dataset Properties

                                    The following options can be included in a polar area chart dataset to configure options for that specific dataset.

                                    Name Type Description
                                    label String The label for the dataset which appears in the legend and tooltips.
                                    backgroundColor Color[] The fill color of the arcs in the dataset. See Colors
                                    borderColor Color[] The border color of the arcs in the dataset. See Colors
                                    borderWidth Number[] The border width of the arcs in the dataset.
                                    hoverBackgroundColor Color[] The fill colour of the arcs when hovered.
                                    hoverBorderColor Color[] The stroke colour of the arcs when hovered.
                                    hoverBorderWidth Number[] The stroke width of the arcs when hovered.

                                    Config Options

                                    These are the customisation options specific to Polar Area charts. These options are merged with the global chart default options, and form the options of the chart.

                                    Name Type Default Description
                                    startAngle Number -0.5 * Math.PI Starting angle to draw arcs for the first item in a dataset.
                                    animation.animateRotate Boolean true If true, the chart will animate in with a rotation animation. This property is in the options.animation object.
                                    animation.animateScale Boolean true If true, will animate scaling the chart from the center outwards.

                                    Default Options

                                    We can also change these defaults values for each PolarArea type that is created, this object is available at Chart.defaults.polarArea. Changing the global options only affects charts created after the change. Existing charts are not changed.

                                    For example, to configure all new polar area charts with animateScale = false you would do:

                                    Chart.defaults.polarArea.animation.animateScale = false;
                                    

                                    Data Structure

                                    For a polar area chart, datasets need to contain an array of data points. The data points should be a number, Chart.js will total all of the numbers and calculate the relative proportion of each.

                                    You also need to specify an array of labels so that tooltips appear correctly for each slice.

                                    data = {
                                        datasets: [{
                                            data: [10, 20, 30]
                                        }],
                                    
                                        // These labels appear in the legend and in the tooltips when hovering different arcs
                                        labels: [
                                            'Red',
                                            'Yellow',
                                            'Blue'
                                        ]
                                    };
                                    

                                    results matching ""

                                      No results matching ""

                                      ================================================ FILE: src/main/resources/static/admin/dist/js/plugins/chartjs2/docs/charts/radar.html ================================================ Radar · GitBook

                                      Radar

                                      A radar chart is a way of showing multiple data points and the variation between them.

                                      They are often useful for comparing the points of two or more different data sets.

                                      Example Usage

                                      var myRadarChart = new Chart(ctx, {
                                          type: 'radar',
                                          data: data,
                                          options: options
                                      });
                                      

                                      Dataset Properties

                                      The radar chart allows a number of properties to be specified for each dataset. These are used to set display properties for a specific dataset. For example, the colour of a line is generally set this way.

                                      All point* properties can be specified as an array. If these are set to an array value, the first value applies to the first point, the second value to the second point, and so on.

                                      Name Type Description
                                      label String The label for the dataset which appears in the legend and tooltips.
                                      backgroundColor Color The fill color under the line. See Colors
                                      borderColor Color The color of the line. See Colors
                                      borderWidth Number The width of the line in pixels.
                                      borderDash Number[] Length and spacing of dashes. See MDN
                                      borderDashOffset Number Offset for line dashes. See MDN
                                      borderCapStyle String Cap style of the line. See MDN
                                      borderJoinStyle String Line joint style. See MDN
                                      fill Boolean/String How to fill the area under the line. See area charts
                                      lineTension Number Bezier curve tension of the line. Set to 0 to draw straightlines.
                                      pointBackgroundColor Color/Color[] The fill color for points.
                                      pointBorderColor Color/Color[] The border color for points.
                                      pointBorderWidth Number/Number[] The width of the point border in pixels.
                                      pointRadius Number/Number[] The radius of the point shape. If set to 0, the point is not rendered.
                                      pointStyle String/String[]/Image/Image[] Style of the point. more...
                                      pointHitRadius Number/Number[] The pixel size of the non-displayed point that reacts to mouse events.
                                      pointHoverBackgroundColor Color/Color[] Point background color when hovered.
                                      pointHoverBorderColor Color/Color[] Point border color when hovered.
                                      pointHoverBorderWidth Number/Number[] Border width of point when hovered.
                                      pointHoverRadius Number/Number[] The radius of the point when hovered.

                                      pointStyle

                                      The style of point. Options are:

                                      • 'circle'
                                      • 'cross'
                                      • 'crossRot'
                                      • 'dash'.
                                      • 'line'
                                      • 'rect'
                                      • 'rectRounded'
                                      • 'rectRot'
                                      • 'star'
                                      • 'triangle'

                                      If the option is an image, that image is drawn on the canvas using drawImage.

                                      Configuration Options

                                      Unlike other charts, the radar chart has no chart specific options.

                                      Scale Options

                                      The radar chart supports only a single scale. The options for this scale are defined in the scale property.

                                      options = {
                                          scale: {
                                              // Hides the scale
                                              display: false
                                          }
                                      };
                                      

                                      Default Options

                                      It is common to want to apply a configuration setting to all created radar charts. The global radar chart settings are stored in Chart.defaults.radar. Changing the global options only affects charts created after the change. Existing charts are not changed.

                                      Data Structure

                                      The data property of a dataset for a radar chart is specified as a an array of numbers. Each point in the data array corresponds to the label at the same index on the x axis.

                                      data: [20, 10]
                                      

                                      For a radar chart, to provide context of what each point means, we include an array of strings that show around each point in the chart.

                                      data: {
                                          labels: ['Running', 'Swimming', 'Eating', 'Cycling'],
                                          datasets: [{
                                              data: [20, 10, 4, 2]
                                          }]
                                      }
                                      

                                      results matching ""

                                        No results matching ""

                                        ================================================ FILE: src/main/resources/static/admin/dist/js/plugins/chartjs2/docs/charts/scatter.html ================================================ Scatter · GitBook

                                        Scatter Chart

                                        Scatter charts are based on basic line charts with the x axis changed to a linear axis. To use a scatter chart, data must be passed as objects containing X and Y properties. The example below creates a scatter chart with 3 points.

                                        var scatterChart = new Chart(ctx, {
                                            type: 'scatter',
                                            data: {
                                                datasets: [{
                                                    label: 'Scatter Dataset',
                                                    data: [{
                                                        x: -10,
                                                        y: 0
                                                    }, {
                                                        x: 0,
                                                        y: 10
                                                    }, {
                                                        x: 10,
                                                        y: 5
                                                    }]
                                                }]
                                            },
                                            options: {
                                                scales: {
                                                    xAxes: [{
                                                        type: 'linear',
                                                        position: 'bottom'
                                                    }]
                                                }
                                            }
                                        });
                                        

                                        Dataset Properties

                                        The scatter chart supports all of the same properties as the line chart.

                                        Data Structure

                                        Unlike the line chart where data can be supplied in two different formats, the scatter chart only accepts data in a point format.

                                        data: [{
                                                x: 10,
                                                y: 20
                                            }, {
                                                x: 15,
                                                y: 10
                                            }]
                                        

                                        results matching ""

                                          No results matching ""

                                          ================================================ FILE: src/main/resources/static/admin/dist/js/plugins/chartjs2/docs/configuration/animations.html ================================================ Animations · GitBook

                                          Animations

                                          Chart.js animates charts out of the box. A number of options are provided to configure how the animation looks and how long it takes

                                          Animation Configuration

                                          The following animation options are available. The global options for are defined in Chart.defaults.global.animation.

                                          Name Type Default Description
                                          duration Number 1000 The number of milliseconds an animation takes.
                                          easing String 'easeOutQuart' Easing function to use. more...
                                          onProgress Function null Callback called on each step of an animation. more...
                                          onComplete Function null Callback called at the end of an animation. more...

                                          Easing

                                          Available options are:

                                          • 'linear'
                                          • 'easeInQuad'
                                          • 'easeOutQuad'
                                          • 'easeInOutQuad'
                                          • 'easeInCubic'
                                          • 'easeOutCubic'
                                          • 'easeInOutCubic'
                                          • 'easeInQuart'
                                          • 'easeOutQuart'
                                          • 'easeInOutQuart'
                                          • 'easeInQuint'
                                          • 'easeOutQuint'
                                          • 'easeInOutQuint'
                                          • 'easeInSine'
                                          • 'easeOutSine'
                                          • 'easeInOutSine'
                                          • 'easeInExpo'
                                          • 'easeOutExpo'
                                          • 'easeInOutExpo'
                                          • 'easeInCirc'
                                          • 'easeOutCirc'
                                          • 'easeInOutCirc'
                                          • 'easeInElastic'
                                          • 'easeOutElastic'
                                          • 'easeInOutElastic'
                                          • 'easeInBack'
                                          • 'easeOutBack'
                                          • 'easeInOutBack'
                                          • 'easeInBounce'
                                          • 'easeOutBounce'
                                          • 'easeInOutBounce'

                                          See Robert Penner's easing equations.

                                          Animation Callbacks

                                          The onProgress and onComplete callbacks are useful for synchronizing an external draw to the chart animation. The callback is passed a Chart.Animation instance:

                                          {
                                              // Chart object
                                              chart: Chart,
                                          
                                              // Current Animation frame number
                                              currentStep: Number,
                                          
                                              // Number of animation frames
                                              numSteps: Number,
                                          
                                              // Animation easing to use
                                              easing: String,
                                          
                                              // Function that renders the chart
                                              render: Function,
                                          
                                              // User callback
                                              onAnimationProgress: Function,
                                          
                                              // User callback
                                              onAnimationComplete: Function
                                          }
                                          

                                          The following example fills a progress bar during the chart animation.

                                          var chart = new Chart(ctx, {
                                              type: 'line',
                                              data: data,
                                              options: {
                                                  animation: {
                                                      onProgress: function(animation) {
                                                          progress.value = animation.animationObject.currentStep / animation.animationObject.numSteps;
                                                      }
                                                  }
                                              }
                                          });
                                          

                                          Another example usage of these callbacks can be found on Github: this sample displays a progress bar showing how far along the animation is.

                                          results matching ""

                                            No results matching ""

                                            ================================================ FILE: src/main/resources/static/admin/dist/js/plugins/chartjs2/docs/configuration/elements.html ================================================ Elements · GitBook

                                            Elements

                                            While chart types provide settings to configure the styling of each dataset, you sometimes want to style all datasets the same way. A common example would be to stroke all of the bars in a bar chart with the same colour but change the fill per dataset. Options can be configured for four different types of elements: arc, lines, points, and rectangles. When set, these options apply to all objects of that type unless specifically overridden by the configuration attached to a dataset.

                                            Global Configuration

                                            The element options can be specified per chart or globally. The global options for elements are defined in Chart.defaults.global.elements. For example, to set the border width of all bar charts globally you would do:

                                            Chart.defaults.global.elements.rectangle.borderWidth = 2;
                                            

                                            Point Configuration

                                            Point elements are used to represent the points in a line chart or a bubble chart.

                                            Global point options: Chart.defaults.global.elements.point

                                            Name Type Default Description
                                            radius Number 3 Point radius.
                                            pointStyle String circle Point style.
                                            backgroundColor Color 'rgba(0,0,0,0.1)' Point fill color.
                                            borderWidth Number 1 Point stroke width.
                                            borderColor Color 'rgba(0,0,0,0.1)' Point stroke color.
                                            hitRadius Number 1 Extra radius added to point radius for hit detection.
                                            hoverRadius Number 4 Point radius when hovered.
                                            hoverBorderWidth Number 1 Stroke width when hovered.

                                            Point Styles

                                            The following values are supported:

                                            • 'circle'
                                            • 'cross'
                                            • 'crossRot'
                                            • 'dash'
                                            • 'line'
                                            • 'rect'
                                            • 'rectRounded'
                                            • 'rectRot'
                                            • 'star'
                                            • 'triangle'

                                            If the value is an image, that image is drawn on the canvas using drawImage.

                                            Line Configuration

                                            Line elements are used to represent the line in a line chart.

                                            Global line options: Chart.defaults.global.elements.line

                                            Name Type Default Description
                                            tension Number 0.4 Bézier curve tension (0 for no Bézier curves).
                                            backgroundColor Color 'rgba(0,0,0,0.1)' Line fill color.
                                            borderWidth Number 3 Line stroke width.
                                            borderColor Color 'rgba(0,0,0,0.1)' Line stroke color.
                                            borderCapStyle String 'butt' Line cap style (see MDN).
                                            borderDash Array [] Line dash (see MDN).
                                            borderDashOffset Number 0 Line dash offset (see MDN).
                                            borderJoinStyle String 'miter Line join style (see MDN).
                                            capBezierPoints Boolean true true to keep Bézier control inside the chart, false for no restriction.
                                            fill Boolean/String true Fill location: 'zero', 'top', 'bottom', true (eq. 'zero') or false (no fill).
                                            stepped Boolean false true to show the line as a stepped line (tension will be ignored).

                                            Rectangle Configuration

                                            Rectangle elements are used to represent the bars in a bar chart.

                                            Global rectangle options: Chart.defaults.global.elements.rectangle

                                            Name Type Default Description
                                            backgroundColor Color 'rgba(0,0,0,0.1)' Bar fill color.
                                            borderWidth Number 0 Bar stroke width.
                                            borderColor Color 'rgba(0,0,0,0.1)' Bar stroke color.
                                            borderSkipped String 'bottom' Skipped (excluded) border: 'bottom', 'left', 'top' or 'right'.

                                            Arc Configuration

                                            Arcs are used in the polar area, doughnut and pie charts.

                                            Global arc options: Chart.defaults.global.elements.arc.

                                            Name Type Default Description
                                            backgroundColor Color 'rgba(0,0,0,0.1)' Arc fill color.
                                            borderColor Color '#fff' Arc stroke color.
                                            borderWidth Number 2 Arc stroke width.

                                            results matching ""

                                              No results matching ""

                                              ================================================ FILE: src/main/resources/static/admin/dist/js/plugins/chartjs2/docs/configuration/index.html ================================================ Configuration · GitBook

                                              Configuration

                                              The configuration is used to change how the chart behaves. There are properties to control styling, fonts, the legend, etc.

                                              Global Configuration

                                              This concept was introduced in Chart.js 1.0 to keep configuration DRY, and allow for changing options globally across chart types, avoiding the need to specify options for each instance, or the default for a particular chart type.

                                              Chart.js merges the options object passed to the chart with the global configuration using chart type defaults and scales defaults appropriately. This way you can be as specific as you would like in your individual chart configuration, while still changing the defaults for all chart types where applicable. The global general options are defined in Chart.defaults.global. The defaults for each chart type are discussed in the documentation for that chart type.

                                              The following example would set the hover mode to 'nearest' for all charts where this was not overridden by the chart type defaults or the options passed to the constructor on creation.

                                              Chart.defaults.global.hover.mode = 'nearest';
                                              
                                              // Hover mode is set to nearest because it was not overridden here
                                              var chartHoverModeNearest  = new Chart(ctx, {
                                                  type: 'line',
                                                  data: data,
                                              });
                                              
                                              // This chart would have the hover mode that was passed in
                                              var chartDifferentHoverMode = new Chart(ctx, {
                                                  type: 'line',
                                                  data: data,
                                                  options: {
                                                      hover: {
                                                          // Overrides the global setting
                                                          mode: 'index'
                                                      }
                                                  }
                                              })
                                              

                                              results matching ""

                                                No results matching ""

                                                ================================================ FILE: src/main/resources/static/admin/dist/js/plugins/chartjs2/docs/configuration/layout.html ================================================ Layout · GitBook

                                                Layout Configuration

                                                The layout configuration is passed into the options.layout namespace. The global options for the chart layout is defined in Chart.defaults.global.layout.

                                                Name Type Default Description
                                                padding Number or Object 0 The padding to add inside the chart. more...

                                                Padding

                                                If this value is a number, it is applied to all sides of the chart (left, top, right, bottom). If this value is an object, the left property defines the left padding. Similarly the right, top, and bottom properties can also be specified.

                                                Lets say you wanted to add 50px of padding to the left side of the chart canvas, you would do:

                                                let chart = new Chart(ctx, {
                                                    type: 'line',
                                                    data: data,
                                                    options: {
                                                        layout: {
                                                            padding: {
                                                                left: 50,
                                                                right: 0,
                                                                top: 0,
                                                                bottom: 0
                                                            }
                                                        }
                                                    }
                                                });
                                                

                                                results matching ""

                                                  No results matching ""

                                                  ================================================ FILE: src/main/resources/static/admin/dist/js/plugins/chartjs2/docs/configuration/legend.html ================================================ Legend · GitBook

                                                  Legend Configuration

                                                  The chart legend displays data about the datasets that area appearing on the chart.

                                                  Configuration options

                                                  The legend configuration is passed into the options.legend namespace. The global options for the chart legend is defined in Chart.defaults.global.legend.

                                                  Name Type Default Description
                                                  display Boolean true is the legend shown
                                                  position String 'top' Position of the legend. more...
                                                  fullWidth Boolean true Marks that this box should take the full width of the canvas (pushing down other boxes). This is unlikely to need to be changed in day-to-day use.
                                                  onClick Function A callback that is called when a click event is registered on a label item
                                                  onHover Function A callback that is called when a 'mousemove' event is registered on top of a label item
                                                  reverse Boolean false Legend will show datasets in reverse order.
                                                  labels Object See the Legend Label Configuration section below.

                                                  Position

                                                  Position of the legend. Options are:

                                                  • 'top'
                                                  • 'left'
                                                  • 'bottom'
                                                  • 'right'

                                                  Legend Label Configuration

                                                  The legend label configuration is nested below the legend configuration using the labels key.

                                                  Name Type Default Description
                                                  boxWidth Number 40 width of coloured box
                                                  fontSize Number 12 font size of text
                                                  fontStyle String 'normal' font style of text
                                                  fontColor Color '#666' Color of text
                                                  fontFamily String "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif" Font family of legend text.
                                                  padding Number 10 Padding between rows of colored boxes.
                                                  generateLabels Function Generates legend items for each thing in the legend. Default implementation returns the text + styling for the color box. See Legend Item for details.
                                                  filter Function null Filters legend items out of the legend. Receives 2 parameters, a Legend Item and the chart data.
                                                  usePointStyle Boolean false Label style will match corresponding point style (size is based on fontSize, boxWidth is not used in this case).

                                                  Legend Item Interface

                                                  Items passed to the legend onClick function are the ones returned from labels.generateLabels. These items must implement the following interface.

                                                  {
                                                      // Label that will be displayed
                                                      text: String,
                                                  
                                                      // Fill style of the legend box
                                                      fillStyle: Color,
                                                  
                                                      // If true, this item represents a hidden dataset. Label will be rendered with a strike-through effect
                                                      hidden: Boolean,
                                                  
                                                      // For box border. See https://developer.mozilla.org/en/docs/Web/API/CanvasRenderingContext2D/lineCap
                                                      lineCap: String,
                                                  
                                                      // For box border. See https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/setLineDash
                                                      lineDash: Array[Number],
                                                  
                                                      // For box border. See https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/lineDashOffset
                                                      lineDashOffset: Number,
                                                  
                                                      // For box border. See https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/lineJoin
                                                      lineJoin: String,
                                                  
                                                      // Width of box border
                                                      lineWidth: Number,
                                                  
                                                      // Stroke style of the legend box
                                                      strokeStyle: Color
                                                  
                                                      // Point style of the legend box (only used if usePointStyle is true)
                                                      pointStyle: String
                                                  }
                                                  

                                                  Example

                                                  The following example will create a chart with the legend enabled and turn all of the text red in color.

                                                  var chart = new Chart(ctx, {
                                                      type: 'bar',
                                                      data: data,
                                                      options: {
                                                          legend: {
                                                              display: true,
                                                              labels: {
                                                                  fontColor: 'rgb(255, 99, 132)'
                                                              }
                                                          }
                                                  }
                                                  });
                                                  

                                                  Custom On Click Actions

                                                  It can be common to want to trigger different behaviour when clicking an item in the legend. This can be easily achieved using a callback in the config object.

                                                  The default legend click handler is:

                                                  function(e, legendItem) {
                                                      var index = legendItem.datasetIndex;
                                                      var ci = this.chart;
                                                      var meta = ci.getDatasetMeta(index);
                                                  
                                                      // See controller.isDatasetVisible comment
                                                      meta.hidden = meta.hidden === null? !ci.data.datasets[index].hidden : null;
                                                  
                                                      // We hid a dataset ... rerender the chart
                                                      ci.update();
                                                  }
                                                  

                                                  Lets say we wanted instead to link the display of the first two datasets. We could change the click handler accordingly.

                                                  var defaultLegendClickHandler = Chart.defaults.global.legend.onClick;
                                                  var newLegendClickHandler = function (e, legendItem) {
                                                      var index = legendItem.datasetIndex;
                                                  
                                                      if (index > 1) {
                                                          // Do the original logic
                                                          defaultLegendClickHandler(e, legendItem);
                                                      } else {
                                                          let ci = this.chart;
                                                          [ci.getDatasetMeta(0),
                                                           ci.getDatasetMeta(1)].forEach(function(meta) {
                                                              meta.hidden = meta.hidden === null? !ci.data.datasets[index].hidden : null;
                                                          });
                                                          ci.update();
                                                      }
                                                  };
                                                  
                                                  var chart = new Chart(ctx, {
                                                      type: 'line',
                                                      data: data,
                                                      options: {
                                                          legend: {
                                                  
                                                          }
                                                      }
                                                  });
                                                  

                                                  Now when you click the legend in this chart, the visibility of the first two datasets will be linked together.

                                                  HTML Legends

                                                  Sometimes you need a very complex legend. In these cases, it makes sense to generate an HTML legend. Charts provide a generateLegend() method on their prototype that returns an HTML string for the legend.

                                                  To configure how this legend is generated, you can change the legendCallback config property.

                                                  var chart = new Chart(ctx, {
                                                      type: 'line',
                                                      data: data,
                                                      options: {
                                                          legendCallback: function(chart) {
                                                              // Return the HTML string here.
                                                          }
                                                      }
                                                  });
                                                  

                                                  results matching ""

                                                    No results matching ""

                                                    ================================================ FILE: src/main/resources/static/admin/dist/js/plugins/chartjs2/docs/configuration/title.html ================================================ Title · GitBook

                                                    Title

                                                    The chart title defines text to draw at the top of the chart.

                                                    Title Configuration

                                                    The title configuration is passed into the options.title namespace. The global options for the chart title is defined in Chart.defaults.global.title.

                                                    Name Type Default Description
                                                    display Boolean false is the title shown
                                                    position String 'top' Position of title. more...
                                                    fontSize Number 12 Font size
                                                    fontFamily String "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif" Font family for the title text.
                                                    fontColor Color '#666' Font color
                                                    fontStyle String 'bold' Font style
                                                    padding Number 10 Number of pixels to add above and below the title text.
                                                    lineHeight Number/String 1.2 Height of an individual line of text (see MDN)
                                                    text String/String[] '' Title text to display. If specified as an array, text is rendered on multiple lines.

                                                    Position

                                                    Possible title position values are:

                                                    • 'top'
                                                    • 'left'
                                                    • 'bottom'
                                                    • 'right'

                                                    Example Usage

                                                    The example below would enable a title of 'Custom Chart Title' on the chart that is created.

                                                    var chart = new Chart(ctx, {
                                                        type: 'line',
                                                        data: data,
                                                        options: {
                                                            title: {
                                                                display: true,
                                                                text: 'Custom Chart Title'
                                                            }
                                                        }
                                                    })
                                                    

                                                    results matching ""

                                                      No results matching ""

                                                      ================================================ FILE: src/main/resources/static/admin/dist/js/plugins/chartjs2/docs/configuration/tooltip.html ================================================ Tooltip · GitBook

                                                      Tooltips

                                                      Tooltip Configuration

                                                      The tooltip configuration is passed into the options.tooltips namespace. The global options for the chart tooltips is defined in Chart.defaults.global.tooltips.

                                                      Name Type Default Description
                                                      enabled Boolean true Are tooltips enabled
                                                      custom Function null See custom tooltip section.
                                                      mode String 'nearest' Sets which elements appear in the tooltip. more....
                                                      intersect Boolean true if true, the tooltip mode applies only when the mouse position intersects with an element. If false, the mode will be applied at all times.
                                                      position String 'average' The mode for positioning the tooltip. more...
                                                      callbacks Object See the callbacks section
                                                      itemSort Function Sort tooltip items. more...
                                                      filter Function Filter tooltip items. more...
                                                      backgroundColor Color 'rgba(0,0,0,0.8)' Background color of the tooltip.
                                                      titleFontFamily String "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif" title font
                                                      titleFontSize Number 12 Title font size
                                                      titleFontStyle String 'bold' Title font style
                                                      titleFontColor Color '#fff' Title font color
                                                      titleSpacing Number 2 Spacing to add to top and bottom of each title line.
                                                      titleMarginBottom Number 6 Margin to add on bottom of title section.
                                                      bodyFontFamily String "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif" body line font
                                                      bodyFontSize Number 12 Body font size
                                                      bodyFontStyle String 'normal' Body font style
                                                      bodyFontColor Color '#fff' Body font color
                                                      bodySpacing Number 2 Spacing to add to top and bottom of each tooltip item.
                                                      footerFontFamily String "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif" footer font
                                                      footerFontSize Number 12 Footer font size
                                                      footerFontStyle String 'bold' Footer font style
                                                      footerFontColor Color '#fff' Footer font color
                                                      footerSpacing Number 2 Spacing to add to top and bottom of each fotter line.
                                                      footerMarginTop Number 6 Margin to add before drawing the footer.
                                                      xPadding Number 6 Padding to add on left and right of tooltip.
                                                      yPadding Number 6 Padding to add on top and bottom of tooltip.
                                                      caretPadding Number 2 Extra distance to move the end of the tooltip arrow away from the tooltip point.
                                                      caretSize Number 5 Size, in px, of the tooltip arrow.
                                                      cornerRadius Number 6 Radius of tooltip corner curves.
                                                      multiKeyBackground Color '#fff' Color to draw behind the colored boxes when multiple items are in the tooltip
                                                      displayColors Boolean true if true, color boxes are shown in the tooltip
                                                      borderColor Color 'rgba(0,0,0,0)' Color of the border
                                                      borderWidth Number 0 Size of the border

                                                      Position Modes

                                                      Possible modes are:

                                                      • 'average'
                                                      • 'nearest'

                                                      'average' mode will place the tooltip at the average position of the items displayed in the tooltip. 'nearest' will place the tooltip at the position of the element closest to the event position.

                                                      New modes can be defined by adding functions to the Chart.Tooltip.positioners map.

                                                      Example:

                                                      /**
                                                       * Custom positioner
                                                       * @function Chart.Tooltip.positioners.custom
                                                       * @param elements {Chart.Element[]} the tooltip elements
                                                       * @param eventPosition {Point} the position of the event in canvas coordinates
                                                       * @returns {Point} the tooltip position
                                                       */
                                                      Chart.Tooltip.positioners.custom = function(elements, eventPosition) {
                                                          /** @type {Chart.Tooltip} */
                                                          var tooltip = this;
                                                      
                                                          /* ... */
                                                      
                                                          return {
                                                              x: 0,
                                                              y: 0
                                                          };
                                                      }
                                                      

                                                      Sort Callback

                                                      Allows sorting of tooltip items. Must implement at minimum a function that can be passed to Array.prototype.sort. This function can also accept a third parameter that is the data object passed to the chart.

                                                      Filter Callback

                                                      Allows filtering of tooltip items. Must implement at minimum a function that can be passed to Array.prototype.filter. This function can also accept a second parameter that is the data object passed to the chart.

                                                      Tooltip Callbacks

                                                      The tooltip label configuration is nested below the tooltip configuration using the callbacks key. The tooltip has the following callbacks for providing text. For all functions, 'this' will be the tooltip object created from the Chart.Tooltip constructor.

                                                      All functions are called with the same arguments: a tooltip item and the data object passed to the chart. All functions must return either a string or an array of strings. Arrays of strings are treated as multiple lines of text.

                                                      Name Arguments Description
                                                      beforeTitle Array[tooltipItem], data Returns the text to render before the title.
                                                      title Array[tooltipItem], data Returns text to render as the title of the tooltip.
                                                      afterTitle Array[tooltipItem], data Returns text to render after the title.
                                                      beforeBody Array[tooltipItem], data Returns text to render before the body section.
                                                      beforeLabel tooltipItem, data Returns text to render before an individual label. This will be called for each item in the tooltip.
                                                      label tooltipItem, data Returns text to render for an individual item in the tooltip.
                                                      labelColor tooltipItem, chart Returns the colors to render for the tooltip item. more...
                                                      labelTextColor tooltipItem, chart Returns the colors for the text of the label for the tooltip item.
                                                      afterLabel tooltipItem, data Returns text to render after an individual label.
                                                      afterBody Array[tooltipItem], data Returns text to render after the body section
                                                      beforeFooter Array[tooltipItem], data Returns text to render before the footer section.
                                                      footer Array[tooltipItem], data Returns text to render as the footer of the tooltip.
                                                      afterFooter Array[tooltipItem], data Text to render after the footer section

                                                      Label Color Callback

                                                      For example, to return a red box for each item in the tooltip you could do:

                                                      var chart = new Chart(ctx, {
                                                          type: 'line',
                                                          data: data,
                                                          options: {
                                                              tooltips: {
                                                                  callbacks: {
                                                                      labelColor: function(tooltipItem, chart) {
                                                                          return {
                                                                              borderColor: 'rgb(255, 0, 0)',
                                                                              backgroundColor: 'rgb(255, 0, 0)'
                                                                          }
                                                                      },
                                                                      labelTextColor:function(tooltipItem, chart){
                                                                          return '#543453';
                                                                      }
                                                                  }
                                                              }
                                                          }
                                                      });
                                                      

                                                      Tooltip Item Interface

                                                      The tooltip items passed to the tooltip callbacks implement the following interface.

                                                      {
                                                          // X Value of the tooltip as a string
                                                          xLabel: String,
                                                      
                                                          // Y value of the tooltip as a string
                                                          yLabel: String,
                                                      
                                                          // Index of the dataset the item comes from
                                                          datasetIndex: Number,
                                                      
                                                          // Index of this data item in the dataset
                                                          index: Number,
                                                      
                                                          // X position of matching point
                                                          x: Number,
                                                      
                                                          // Y position of matching point
                                                          y: Number,
                                                      }
                                                      

                                                      External (Custom) Tooltips

                                                      Custom tooltips allow you to hook into the tooltip rendering process so that you can render the tooltip in your own custom way. Generally this is used to create an HTML tooltip instead of an oncanvas one. You can enable custom tooltips in the global or chart configuration like so:

                                                      var myPieChart = new Chart(ctx, {
                                                          type: 'pie',
                                                          data: data,
                                                          options: {
                                                              tooltips: {
                                                                  custom: function(tooltipModel) {
                                                                      // Tooltip Element
                                                                      var tooltipEl = document.getElementById('chartjs-tooltip');
                                                      
                                                                      // Create element on first render
                                                                      if (!tooltipEl) {
                                                                          tooltipEl = document.createElement('div');
                                                                          tooltipEl.id = 'chartjs-tooltip';
                                                                          tooltipEl.innerHTML = "<table></table>"
                                                                          document.body.appendChild(tooltipEl);
                                                                      }
                                                      
                                                                      // Hide if no tooltip
                                                                      if (tooltipModel.opacity === 0) {
                                                                          tooltipEl.style.opacity = 0;
                                                                          return;
                                                                      }
                                                      
                                                                      // Set caret Position
                                                                      tooltipEl.classList.remove('above', 'below', 'no-transform');
                                                                      if (tooltipModel.yAlign) {
                                                                          tooltipEl.classList.add(tooltipModel.yAlign);
                                                                      } else {
                                                                          tooltipEl.classList.add('no-transform');
                                                                      }
                                                      
                                                                      function getBody(bodyItem) {
                                                                          return bodyItem.lines;
                                                                      }
                                                      
                                                                      // Set Text
                                                                      if (tooltipModel.body) {
                                                                          var titleLines = tooltipModel.title || [];
                                                                          var bodyLines = tooltipModel.body.map(getBody);
                                                      
                                                                          var innerHtml = '<thead>';
                                                      
                                                                          titleLines.forEach(function(title) {
                                                                              innerHtml += '<tr><th>' + title + '</th></tr>';
                                                                          });
                                                                          innerHtml += '</thead><tbody>';
                                                      
                                                                          bodyLines.forEach(function(body, i) {
                                                                              var colors = tooltipModel.labelColors[i];
                                                                              var style = 'background:' + colors.backgroundColor;
                                                                              style += '; border-color:' + colors.borderColor;
                                                                              style += '; border-width: 2px';
                                                                              var span = '<span class="chartjs-tooltip-key" style="' + style + '"></span>';
                                                                              innerHtml += '<tr><td>' + span + body + '</td></tr>';
                                                                          });
                                                                          innerHtml += '</tbody>';
                                                      
                                                                          var tableRoot = tooltipEl.querySelector('table');
                                                                          tableRoot.innerHTML = innerHtml;
                                                                      }
                                                      
                                                                      // `this` will be the overall tooltip
                                                                      var position = this._chart.canvas.getBoundingClientRect();
                                                      
                                                                      // Display, position, and set styles for font
                                                                      tooltipEl.style.opacity = 1;
                                                                      tooltipEl.style.left = position.left + tooltipModel.caretX + 'px';
                                                                      tooltipEl.style.top = position.top + tooltipModel.caretY + 'px';
                                                                      tooltipEl.style.fontFamily = tooltipModel._fontFamily;
                                                                      tooltipEl.style.fontSize = tooltipModel.fontSize;
                                                                      tooltipEl.style.fontStyle = tooltipModel._fontStyle;
                                                                      tooltipEl.style.padding = tooltipModel.yPadding + 'px ' + tooltipModel.xPadding + 'px';
                                                                  }
                                                              }
                                                          }
                                                      });
                                                      

                                                      See samples/tooltips/line-customTooltips.html for examples on how to get started.

                                                      Tooltip Model

                                                      The tooltip model contains parameters that can be used to render the tooltip.

                                                      {
                                                          // The items that we are rendering in the tooltip. See Tooltip Item Interface section
                                                          dataPoints: TooltipItem[],
                                                      
                                                          // Positioning
                                                          xPadding: Number,
                                                          yPadding: Number,
                                                          xAlign: String,
                                                          yAlign: String,
                                                      
                                                          // X and Y properties are the top left of the tooltip
                                                          x: Number,
                                                          y: Number,
                                                          width: Number,
                                                          height: Number,
                                                          // Where the tooltip points to
                                                          caretX: Number,
                                                          caretY: Number,
                                                      
                                                          // Body
                                                          // The body lines that need to be rendered
                                                          // Each object contains 3 parameters
                                                          // before: String[] // lines of text before the line with the color square
                                                          // lines: String[], // lines of text to render as the main item with color square
                                                          // after: String[], // lines of text to render after the main lines
                                                          body: Object[],
                                                          // lines of text that appear after the title but before the body
                                                          beforeBody: String[],
                                                          // line of text that appear after the body and before the footer
                                                          afterBody: String[],
                                                          bodyFontColor: Color,
                                                          _bodyFontFamily: String,
                                                          _bodyFontStyle: String,
                                                          _bodyAlign: String,
                                                          bodyFontSize: Number,
                                                          bodySpacing: Number,
                                                      
                                                          // Title
                                                          // lines of text that form the title
                                                          title: String[],
                                                          titleFontColor: Color,
                                                          _titleFontFamily: String,
                                                          _titleFontStyle: String,
                                                          titleFontSize: Number,
                                                          _titleAlign: String,
                                                          titleSpacing: Number,
                                                          titleMarginBottom: Number,
                                                      
                                                          // Footer
                                                          // lines of text that form the footer
                                                          footer: String[],
                                                          footerFontColor: Color,
                                                          _footerFontFamily: String,
                                                          _footerFontStyle: String,
                                                          footerFontSize: Number,
                                                          _footerAlign: String,
                                                          footerSpacing: Number,
                                                          footerMarginTop: Number,
                                                      
                                                          // Appearance
                                                          caretSize: Number,
                                                          cornerRadius: Number,
                                                          backgroundColor: Color,
                                                      
                                                          // colors to render for each item in body[]. This is the color of the squares in the tooltip
                                                          labelColors: Color[],
                                                      
                                                          // 0 opacity is a hidden tooltip
                                                          opacity: Number,
                                                          legendColorBackground: Color,
                                                          displayColors: Boolean,
                                                      }
                                                      

                                                      results matching ""

                                                        No results matching ""

                                                        ================================================ FILE: src/main/resources/static/admin/dist/js/plugins/chartjs2/docs/developers/api.html ================================================ Chart.js API · GitBook

                                                        Chart Prototype Methods

                                                        For each chart, there are a set of global prototype methods on the shared ChartType which you may find useful. These are available on all charts created with Chart.js, but for the examples, let's use a line chart we've made.

                                                        // For example:
                                                        var myLineChart = new Chart(ctx, config);
                                                        

                                                        .destroy()

                                                        Use this to destroy any chart instances that are created. This will clean up any references stored to the chart object within Chart.js, along with any associated event listeners attached by Chart.js. This must be called before the canvas is reused for a new chart.

                                                        // Destroys a specific chart instance
                                                        myLineChart.destroy();
                                                        

                                                        .update(config)

                                                        Triggers an update of the chart. This can be safely called after updating the data object. This will update all scales, legends, and then re-render the chart.

                                                        // duration is the time for the animation of the redraw in milliseconds
                                                        // lazy is a boolean. if true, the animation can be interrupted by other animations
                                                        myLineChart.data.datasets[0].data[2] = 50; // Would update the first dataset's value of 'March' to be 50
                                                        myLineChart.update(); // Calling update now animates the position of March from 90 to 50.
                                                        

                                                        Note: replacing the data reference (e.g. myLineChart.data = {datasets: [...]} only works starting version 2.6. Prior that, replacing the entire data object could be achieved with the following workaround: myLineChart.config.data = {datasets: [...]}.

                                                        A config object can be provided with additional configuration for the update process. This is useful when update is manually called inside an event handler and some different animation is desired.

                                                        The following properties are supported:

                                                        • duration (number): Time for the animation of the redraw in milliseconds
                                                        • lazy (boolean): If true, the animation can be interrupted by other animations
                                                        • easing (string): The animation easing function. See Animation Easing for possible values.

                                                        Example:

                                                        myChart.update({
                                                            duration: 800,
                                                            easing: 'easeOutBounce'
                                                        })
                                                        

                                                        See Updating Charts for more details.

                                                        .reset()

                                                        Reset the chart to it's state before the initial animation. A new animation can then be triggered using update.

                                                        myLineChart.reset();
                                                        

                                                        .render(config)

                                                        Triggers a redraw of all chart elements. Note, this does not update elements for new data. Use .update() in that case.

                                                        See .update(config) for more details on the config object.

                                                        // duration is the time for the animation of the redraw in milliseconds
                                                        // lazy is a boolean. if true, the animation can be interrupted by other animations
                                                        myLineChart.render({
                                                            duration: 800,
                                                            lazy: false,
                                                            easing: 'easeOutBounce'
                                                        });
                                                        

                                                        .stop()

                                                        Use this to stop any current animation loop. This will pause the chart during any current animation frame. Call .render() to re-animate.

                                                        // Stops the charts animation loop at its current frame
                                                        myLineChart.stop();
                                                        // => returns 'this' for chainability
                                                        

                                                        .resize()

                                                        Use this to manually resize the canvas element. This is run each time the canvas container is resized, but you can call this method manually if you change the size of the canvas nodes container element.

                                                        // Resizes & redraws to fill its container element
                                                        myLineChart.resize();
                                                        // => returns 'this' for chainability
                                                        

                                                        .clear()

                                                        Will clear the chart canvas. Used extensively internally between animation frames, but you might find it useful.

                                                        // Will clear the canvas that myLineChart is drawn on
                                                        myLineChart.clear();
                                                        // => returns 'this' for chainability
                                                        

                                                        .toBase64Image()

                                                        This returns a base 64 encoded string of the chart in it's current state.

                                                        myLineChart.toBase64Image();
                                                        // => returns png data url of the image on the canvas
                                                        

                                                        .generateLegend()

                                                        Returns an HTML string of a legend for that chart. The legend is generated from the legendCallback in the options.

                                                        myLineChart.generateLegend();
                                                        // => returns HTML string of a legend for this chart
                                                        

                                                        .getElementAtEvent(e)

                                                        Calling getElementAtEvent(event) on your Chart instance passing an argument of an event, or jQuery event, will return the single element at the event position. If there are multiple items within range, only the first is returned. The value returned from this method is an array with a single parameter. An array is used to keep a consistent API between the get*AtEvent methods.

                                                        myLineChart.getElementAtEvent(e);
                                                        // => returns the first element at the event point.
                                                        

                                                        To get an item that was clicked on, getElementAtEvent can be used.

                                                        function clickHandler(evt) {
                                                            var item = myChart.getElementAtEvent(evt)[0];
                                                        
                                                            if (item) {
                                                                var label = myChart.data.labels[firstPoint._index];
                                                                var value = myChart.data.datasets[firstPoint._datasetIndex].data[firstPoint._index];
                                                            }
                                                        }
                                                        

                                                        .getElementsAtEvent(e)

                                                        Looks for the element under the event point, then returns all elements at the same data index. This is used internally for 'label' mode highlighting.

                                                        Calling getElementsAtEvent(event) on your Chart instance passing an argument of an event, or jQuery event, will return the point elements that are at that the same position of that event.

                                                        canvas.onclick = function(evt){
                                                            var activePoints = myLineChart.getElementsAtEvent(evt);
                                                            // => activePoints is an array of points on the canvas that are at the same position as the click event.
                                                        };
                                                        

                                                        This functionality may be useful for implementing DOM based tooltips, or triggering custom behaviour in your application.

                                                        .getDatasetAtEvent(e)

                                                        Looks for the element under the event point, then returns all elements from that dataset. This is used internally for 'dataset' mode highlighting

                                                        myLineChart.getDatasetAtEvent(e);
                                                        // => returns an array of elements
                                                        

                                                        .getDatasetMeta(index)

                                                        Looks for the dataset that matches the current index and returns that metadata. This returned data has all of the metadata that is used to construct the chart.

                                                        The data property of the metadata will contain information about each point, rectangle, etc. depending on the chart type.

                                                        Extensive examples of usage are available in the Chart.js tests.

                                                        var meta = myChart.getDatasetMeta(0);
                                                        var x = meta.data[0]._model.x
                                                        

                                                        results matching ""

                                                          No results matching ""

                                                          ================================================ FILE: src/main/resources/static/admin/dist/js/plugins/chartjs2/docs/developers/axes.html ================================================ New Axes · GitBook

                                                          New Axes

                                                          Axes in Chart.js can be individually extended. Axes should always derive from Chart.Scale but this is not a mandatory requirement.

                                                          let MyScale = Chart.Scale.extend({
                                                              /* extensions ... */
                                                          });
                                                          
                                                          // MyScale is now derived from Chart.Scale
                                                          

                                                          Once you have created your scale class, you need to register it with the global chart object so that it can be used. A default config for the scale may be provided when registering the constructor. The first parameter to the register function is a string key that is used later to identify which scale type to use for a chart.

                                                          Chart.scaleService.registerScaleType('myScale', MyScale, defaultConfigObject);
                                                          

                                                          To use the new scale, simply pass in the string key to the config when creating a chart.

                                                          var lineChart = new Chart(ctx, {
                                                              data: data,
                                                              type: 'line',
                                                              options: {
                                                                  scales: {
                                                                      yAxes: [{
                                                                          type: 'myScale' // this is the same key that was passed to the registerScaleType function
                                                                      }]
                                                                  }
                                                              }
                                                          })
                                                          

                                                          Scale Properties

                                                          Scale instances are given the following properties during the fitting process.

                                                          {
                                                              left: Number, // left edge of the scale bounding box
                                                              right: Number, // right edge of the bounding box'
                                                              top: Number,
                                                              bottom: Number,
                                                              width: Number, // the same as right - left
                                                              height: Number, // the same as bottom - top
                                                          
                                                              // Margin on each side. Like css, this is outside the bounding box.
                                                              margins: {
                                                                  left: Number,
                                                                  right: Number,
                                                                  top: Number,
                                                                  bottom: Number,
                                                              },
                                                          
                                                              // Amount of padding on the inside of the bounding box (like CSS)
                                                              paddingLeft: Number,
                                                              paddingRight: Number,
                                                              paddingTop: Number,
                                                              paddingBottom: Number,
                                                          }
                                                          

                                                          Scale Interface

                                                          To work with Chart.js, custom scale types must implement the following interface.

                                                          {
                                                              // Determines the data limits. Should set this.min and this.max to be the data max/min
                                                              determineDataLimits: function() {},
                                                          
                                                              // Generate tick marks. this.chart is the chart instance. The data object can be accessed as this.chart.data
                                                              // buildTicks() should create a ticks array on the axis instance, if you intend to use any of the implementations from the base class
                                                              buildTicks: function() {},
                                                          
                                                              // Get the value to show for the data at the given index of the the given dataset, ie this.chart.data.datasets[datasetIndex].data[index]
                                                              getLabelForIndex: function(index, datasetIndex) {},
                                                          
                                                              // Get the pixel (x coordinate for horizontal axis, y coordinate for vertical axis) for a given value
                                                              // @param index: index into the ticks array
                                                              // @param includeOffset: if true, get the pixel halway between the given tick and the next
                                                              getPixelForTick: function(index, includeOffset) {},
                                                          
                                                              // Get the pixel (x coordinate for horizontal axis, y coordinate for vertical axis) for a given value
                                                              // @param value : the value to get the pixel for
                                                              // @param index : index into the data array of the value
                                                              // @param datasetIndex : index of the dataset the value comes from
                                                              // @param includeOffset : if true, get the pixel halway between the given tick and the next
                                                              getPixelForValue: function(value, index, datasetIndex, includeOffset) {}
                                                          
                                                              // Get the value for a given pixel (x coordinate for horizontal axis, y coordinate for vertical axis)
                                                              // @param pixel : pixel value
                                                              getValueForPixel: function(pixel) {}
                                                          }
                                                          

                                                          Optionally, the following methods may also be overwritten, but an implementation is already provided by the Chart.Scale base class.

                                                              // Transform the ticks array of the scale instance into strings. The default implementation simply calls this.options.ticks.callback(numericalTick, index, ticks);
                                                              convertTicksToLabels: function() {},
                                                          
                                                              // Determine how much the labels will rotate by. The default implementation will only rotate labels if the scale is horizontal.
                                                              calculateTickRotation: function() {},
                                                          
                                                              // Fits the scale into the canvas.
                                                              // this.maxWidth and this.maxHeight will tell you the maximum dimensions the scale instance can be. Scales should endeavour to be as efficient as possible with canvas space.
                                                              // this.margins is the amount of space you have on either side of your scale that you may expand in to. This is used already for calculating the best label rotation
                                                              // You must set this.minSize to be the size of your scale. It must be an object containing 2 properties: width and height.
                                                              // You must set this.width to be the width and this.height to be the height of the scale
                                                              fit: function() {},
                                                          
                                                              // Draws the scale onto the canvas. this.(left|right|top|bottom) will have been populated to tell you the area on the canvas to draw in
                                                              // @param chartArea : an object containing four properties: left, right, top, bottom. This is the rectangle that lines, bars, etc will be drawn in. It may be used, for example, to draw grid lines.
                                                              draw: function(chartArea) {},
                                                          

                                                          The Core.Scale base class also has some utility functions that you may find useful.

                                                          {
                                                              // Returns true if the scale instance is horizontal
                                                              isHorizontal: function() {},
                                                          
                                                              // Get the correct value from the value from this.chart.data.datasets[x].data[]
                                                              // If dataValue is an object, returns .x or .y depending on the return of isHorizontal()
                                                              // If the value is undefined, returns NaN
                                                              // Otherwise returns the value.
                                                              // Note that in all cases, the returned value is not guaranteed to be a Number
                                                              getRightValue: function(dataValue) {},
                                                          }
                                                          

                                                          results matching ""

                                                            No results matching ""

                                                            ================================================ FILE: src/main/resources/static/admin/dist/js/plugins/chartjs2/docs/developers/charts.html ================================================ New Charts · GitBook

                                                            New Charts

                                                            Chart.js 2.0 introduces the concept of controllers for each dataset. Like scales, new controllers can be written as needed.

                                                            Chart.controllers.MyType = Chart.DatasetController.extend({
                                                            
                                                            });
                                                            
                                                            
                                                            // Now we can create a new instance of our chart, using the Chart.js API
                                                            new Chart(ctx, {
                                                                // this is the string the constructor was registered at, ie Chart.controllers.MyType
                                                                type: 'MyType',
                                                                data: data,
                                                                options: options
                                                            });
                                                            

                                                            Dataset Controller Interface

                                                            Dataset controllers must implement the following interface.

                                                            {
                                                                // Create elements for each piece of data in the dataset. Store elements in an array on the dataset as dataset.metaData
                                                                addElements: function() {},
                                                            
                                                                // Create a single element for the data at the given index and reset its state
                                                                addElementAndReset: function(index) {},
                                                            
                                                                // Draw the representation of the dataset
                                                                // @param ease : if specified, this number represents how far to transition elements. See the implementation of draw() in any of the provided controllers to see how this should be used
                                                                draw: function(ease) {},
                                                            
                                                                // Remove hover styling from the given element
                                                                removeHoverStyle: function(element) {},
                                                            
                                                                // Add hover styling to the given element
                                                                setHoverStyle: function(element) {},
                                                            
                                                                // Update the elements in response to new data
                                                                // @param reset : if true, put the elements into a reset state so they can animate to their final values
                                                                update: function(reset) {},
                                                            }
                                                            

                                                            The following methods may optionally be overridden by derived dataset controllers

                                                            {
                                                                // Initializes the controller
                                                                initialize: function(chart, datasetIndex) {},
                                                            
                                                                // Ensures that the dataset represented by this controller is linked to a scale. Overridden to helpers.noop in the polar area and doughnut controllers as these
                                                                // chart types using a single scale
                                                                linkScales: function() {},
                                                            
                                                                // Called by the main chart controller when an update is triggered. The default implementation handles the number of data points changing and creating elements appropriately.
                                                                buildOrUpdateElements: function() {}
                                                            }
                                                            

                                                            Extending Existing Chart Types

                                                            Extending or replacing an existing controller type is easy. Simply replace the constructor for one of the built in types with your own.

                                                            The built in controller types are:

                                                            • Chart.controllers.line
                                                            • Chart.controllers.bar
                                                            • Chart.controllers.radar
                                                            • Chart.controllers.doughnut
                                                            • Chart.controllers.polarArea
                                                            • Chart.controllers.bubble

                                                            For example, to derive a new chart type that extends from a bubble chart, you would do the following.

                                                            // Sets the default config for 'derivedBubble' to be the same as the bubble defaults. 
                                                            // We look for the defaults by doing Chart.defaults[chartType]
                                                            // It looks like a bug exists when the defaults don't exist
                                                            Chart.defaults.derivedBubble = Chart.defaults.bubble;
                                                            
                                                            // I think the recommend using Chart.controllers.bubble.extend({ extensions here });
                                                            var custom = Chart.controllers.bubble.extend({
                                                                draw: function(ease) {
                                                                    // Call super method first
                                                                    Chart.controllers.bubble.prototype.draw.call(this, ease);
                                                            
                                                                    // Now we can do some custom drawing for this dataset. Here we'll draw a red box around the first point in each dataset
                                                                    var meta = this.getMeta();
                                                                    var pt0 = meta.data[0];
                                                                    var radius = pt0._view.radius;
                                                            
                                                                    var ctx = this.chart.chart.ctx;
                                                                    ctx.save();
                                                                    ctx.strokeStyle = 'red';
                                                                    ctx.lineWidth = 1;
                                                                    ctx.strokeRect(pt0._view.x - radius, pt0._view.y - radius, 2 * radius, 2 * radius);
                                                                    ctx.restore();
                                                                }
                                                            });
                                                            
                                                            // Stores the controller so that the chart initialization routine can look it up with
                                                            // Chart.controllers[type]
                                                            Chart.controllers.derivedBubble = custom;
                                                            
                                                            // Now we can create and use our new chart type
                                                            new Chart(ctx, {
                                                                type: 'derivedBubble',
                                                                data: data,
                                                                options: options,
                                                            });
                                                            

                                                            Bar Controller

                                                            The bar controller has a special property that you should be aware of. To correctly calculate the width of a bar, the controller must determine the number of datasets that map to bars. To do this, the bar controller attaches a property bar to the dataset during initialization. If you are creating a replacement or updated bar controller, you should do the same. This will ensure that charts with regular bars and your new derived bars will work seamlessly.

                                                            results matching ""

                                                              No results matching ""

                                                              ================================================ FILE: src/main/resources/static/admin/dist/js/plugins/chartjs2/docs/developers/contributing.html ================================================ Contributing · GitBook

                                                              Contributing

                                                              New contributions to the library are welcome, but we ask that you please follow these guidelines:

                                                              • Use tabs for indentation, not spaces.
                                                              • Only change the individual files in /src.
                                                              • Check that your code will pass eslint code standards, gulp lint will run this for you.
                                                              • Check that your code will pass tests, gulp test will run tests for you.
                                                              • Keep pull requests concise, and document new functionality in the relevant .md file.
                                                              • Consider whether your changes are useful for all users, or if creating a Chart.js plugin would be more appropriate.
                                                              • Avoid breaking changes unless there is an upcoming major release, which are infrequent. We encourage people to write plugins for most new advanced features, so care a lot about backwards compatibility.

                                                              Joining the project

                                                              Active committers and contributors are invited to introduce yourself and request commit access to this project. We have a very active Slack community that you can join here. If you think you can help, we'd love to have you!

                                                              Building and Testing

                                                              Chart.js uses gulp to build the library into a single JavaScript file.

                                                              Firstly, we need to ensure development dependencies are installed. With node and npm installed, after cloning the Chart.js repo to a local directory, and navigating to that directory in the command line, we can run the following:

                                                              > npm install
                                                              > npm install -g gulp
                                                              

                                                              This will install the local development dependencies for Chart.js, along with a CLI for the JavaScript task runner gulp.

                                                              The following commands are now available from the repository root:

                                                              > gulp build                // build Chart.js in ./dist
                                                              > gulp unittest             // run tests from ./test/specs
                                                              > gulp unittest --watch     // run tests and watch for source changes
                                                              > gulp unittest --coverage  // run tests and generate coverage reports in ./coverage
                                                              > gulp lint                 // perform code linting (ESLint)
                                                              > gulp test                 // perform code linting and run unit tests
                                                              > gulp docs                 // build the documentation in ./dist/docs
                                                              

                                                              More information can be found in gulpfile.js.

                                                              Bugs and Issues

                                                              Please report these on the GitHub page - at github.com/chartjs/Chart.js. Please do not use issues for support requests. For help using Chart.js, please take a look at the chartjs tag on Stack Overflow.

                                                              Well structured, detailed bug reports are hugely valuable for the project.

                                                              Guidelines for reporting bugs:

                                                              • Check the issue search to see if it has already been reported
                                                              • Isolate the problem to a simple test case
                                                              • Please include a demonstration of the bug on a website such as JS Bin, JS Fiddle, or Codepen. (Template)

                                                              Please provide any additional details associated with the bug, if it's browser or screen density specific, or only happens with a certain configuration or data.

                                                              results matching ""

                                                                No results matching ""

                                                                ================================================ FILE: src/main/resources/static/admin/dist/js/plugins/chartjs2/docs/developers/index.html ================================================ Developers · GitBook

                                                                Developers

                                                                Developer features allow extending and enhancing Chart.js in many different ways.

                                                                Latest resources

                                                                Latest documentation and samples, including unreleased features, are available at:

                                                                Development releases

                                                                Latest builds are available for testing at:

                                                                Note: Development builds are currently only available via HTTP, so in order to include them in JSFiddle or CodePen, you need to access these tools via HTTP as well.

                                                                WARNING: Development builds MUST not be used for production purposes or as replacement for CDN.

                                                                Browser support

                                                                Chart.js offers support for the following browsers:

                                                                • Chrome 50+
                                                                • Firefox 45+
                                                                • Internet Explorer 11
                                                                • Edge 14+
                                                                • Safari 9+

                                                                Browser support for the canvas element is available in all modern & major mobile browsers. CanIUse

                                                                Thanks to BrowserStack for allowing our team to test on thousands of browsers.

                                                                Previous versions

                                                                Version 2 has a completely different API than earlier versions.

                                                                Most earlier version options have current equivalents or are the same.

                                                                Please use the documentation that is available on chartjs.org for the current version of Chart.js.

                                                                Please note - documentation for previous versions are available on the GitHub repo.

                                                                results matching ""

                                                                  No results matching ""

                                                                  ================================================ FILE: src/main/resources/static/admin/dist/js/plugins/chartjs2/docs/developers/plugins.html ================================================ Plugins · GitBook

                                                                  Plugins

                                                                  Plugins are the most efficient way to customize or change the default behavior of a chart. They have been introduced at version 2.1.0 (global plugins only) and extended at version 2.5.0 (per chart plugins and options).

                                                                  Using plugins

                                                                  Plugins can be shared between chart instances:

                                                                  var plugin = { /* plugin implementation */ };
                                                                  
                                                                  // chart1 and chart2 use "plugin"
                                                                  var chart1 = new Chart(ctx, {
                                                                      plugins: [plugin]
                                                                  });
                                                                  
                                                                  var chart2 = new Chart(ctx, {
                                                                      plugins: [plugin]
                                                                  });
                                                                  
                                                                  // chart3 doesn't use "plugin"
                                                                  var chart3 = new Chart(ctx, {});
                                                                  

                                                                  Plugins can also be defined directly in the chart plugins config (a.k.a. inline plugins):

                                                                  var chart = new Chart(ctx, {
                                                                      plugins: [{
                                                                          beforeInit: function(chart, options) {
                                                                              //..
                                                                          }
                                                                      }]
                                                                  });
                                                                  

                                                                  However, this approach is not ideal when the customization needs to apply to many charts.

                                                                  Global plugins

                                                                  Plugins can be registered globally to be applied on all charts (a.k.a. global plugins):

                                                                  Chart.plugins.register({
                                                                      // plugin implementation
                                                                  });
                                                                  

                                                                  Note: inline plugins can't be registered globally.

                                                                  Configuration

                                                                  Plugin ID

                                                                  Plugins must define a unique id in order to be configurable.

                                                                  This id should follow the npm package name convention:

                                                                  • can't start with a dot or an underscore
                                                                  • can't contain any non-URL-safe characters
                                                                  • can't contain uppercase letters
                                                                  • should be something short, but also reasonably descriptive

                                                                  If a plugin is intended to be released publicly, you may want to check the registry to see if there's something by that name already. Note that in this case, the package name should be prefixed by chartjs-plugin- to appear in Chart.js plugin registry.

                                                                  Plugin options

                                                                  Plugin options are located under the options.plugins config and are scoped by the plugin ID: options.plugins.{plugin-id}.

                                                                  var chart = new Chart(ctx, {
                                                                      config: {
                                                                          foo: { ... },           // chart 'foo' option
                                                                          plugins: {
                                                                              p1: {
                                                                                  foo: { ... },   // p1 plugin 'foo' option
                                                                                  bar: { ... }
                                                                              },
                                                                              p2: {
                                                                                  foo: { ... },   // p2 plugin 'foo' option
                                                                                  bla: { ... }
                                                                              }
                                                                          }
                                                                      }
                                                                  });
                                                                  

                                                                  Disable plugins

                                                                  To disable a global plugin for a specific chart instance, the plugin options must be set to false:

                                                                  Chart.plugins.register({
                                                                      id: 'p1',
                                                                      // ...
                                                                  });
                                                                  
                                                                  var chart = new Chart(ctx, {
                                                                      config: {
                                                                          plugins: {
                                                                              p1: false   // disable plugin 'p1' for this instance
                                                                          }
                                                                      }
                                                                  });
                                                                  

                                                                  Plugin Core API

                                                                  Available hooks (as of version 2.6):

                                                                  • beforeInit
                                                                  • afterInit
                                                                  • beforeUpdate (cancellable)
                                                                  • afterUpdate
                                                                  • beforeLayout (cancellable)
                                                                  • afterLayout
                                                                  • beforeDatasetsUpdate (cancellable)
                                                                  • afterDatasetsUpdate
                                                                  • beforeDatasetUpdate (cancellable)
                                                                  • afterDatasetUpdate
                                                                  • beforeRender (cancellable)
                                                                  • afterRender
                                                                  • beforeDraw (cancellable)
                                                                  • afterDraw
                                                                  • beforeDatasetsDraw (cancellable)
                                                                  • afterDatasetsDraw
                                                                  • beforeDatasetDraw (cancellable)
                                                                  • afterDatasetDraw
                                                                  • beforeEvent (cancellable)
                                                                  • afterEvent
                                                                  • resize
                                                                  • destroy

                                                                  results matching ""

                                                                    No results matching ""

                                                                    ================================================ FILE: src/main/resources/static/admin/dist/js/plugins/chartjs2/docs/developers/updates.html ================================================ Updating Charts · GitBook

                                                                    Updating Charts

                                                                    It's pretty common to want to update charts after they've been created. When the chart data is changed, Chart.js will animate to the new data values.

                                                                    Adding or Removing Data

                                                                    Adding and removing data is supported by changing the data array. To add data, just add data into the data array as seen in this example.

                                                                    function addData(chart, label, data) {
                                                                        chart.data.labels.push(label);
                                                                        chart.data.datasets.forEach((dataset) => {
                                                                            dataset.data.push(data);
                                                                        });
                                                                        chart.update();
                                                                    }
                                                                    
                                                                    function removeData(chart) {
                                                                        chart.data.labels.pop();
                                                                        chart.data.datasets.forEach((dataset) => {
                                                                            dataset.data.pop();
                                                                        });
                                                                        chart.update();
                                                                    }
                                                                    

                                                                    Preventing Animations

                                                                    Sometimes when a chart updates, you may not want an animation. To achieve this you can call update with a duration of 0. This will render the chart synchronously and without an animation.

                                                                    results matching ""

                                                                      No results matching ""

                                                                      ================================================ FILE: src/main/resources/static/admin/dist/js/plugins/chartjs2/docs/general/colors.html ================================================ Colors · GitBook

                                                                      Colors

                                                                      When supplying colors to Chart options, you can use a number of formats. You can specify the color as a string in hexadecimal, RGB, or HSL notations. If a color is needed, but not specified, Chart.js will use the global default color. This color is stored at Chart.defaults.global.defaultColor. It is initially set to 'rgba(0, 0, 0, 0.1)'

                                                                      You can also pass a CanvasGradient object. You will need to create this before passing to the chart, but using it you can achieve some interesting effects.

                                                                      Patterns and Gradients

                                                                      An alternative option is to pass a CanvasPattern or CanvasGradient object instead of a string colour.

                                                                      For example, if you wanted to fill a dataset with a pattern from an image you could do the following.

                                                                      var img = new Image();
                                                                      img.src = 'https://example.com/my_image.png';
                                                                      img.onload = function() {
                                                                          var ctx = document.getElementById('canvas').getContext('2d');
                                                                          var fillPattern = ctx.createPattern(img, 'repeat');
                                                                      
                                                                          var chart = new Chart(ctx, {
                                                                              data: {
                                                                                  labels: ['Item 1', 'Item 2', 'Item 3'],
                                                                                  datasets: [{
                                                                                      data: [10, 20, 30],
                                                                                      backgroundColor: fillPattern
                                                                                  }]
                                                                              }
                                                                          })
                                                                      }
                                                                      

                                                                      Using pattern fills for data graphics can help viewers with vision deficiencies (e.g. color-blindness or partial sight) to more easily understand your data.

                                                                      Using the Patternomaly library you can generate patterns to fill datasets.

                                                                      var chartData = {
                                                                          datasets: [{
                                                                              data: [45, 25, 20, 10],
                                                                              backgroundColor: [
                                                                                  pattern.draw('square', '#ff6384'),
                                                                                  pattern.draw('circle', '#36a2eb'),
                                                                                  pattern.draw('diamond', '#cc65fe'),
                                                                                  pattern.draw('triangle', '#ffce56'),
                                                                              ]
                                                                          }],
                                                                          labels: ['Red', 'Blue', 'Purple', 'Yellow']
                                                                      };
                                                                      

                                                                      results matching ""

                                                                        No results matching ""

                                                                        ================================================ FILE: src/main/resources/static/admin/dist/js/plugins/chartjs2/docs/general/device-pixel-ratio.md ================================================ # Device Pixel Ratio By default the chart's canvas will use a 1:1 pixel ratio, unless the physical display has a higher pixel ratio (e.g. Retina displays). For applications where a chart will be converted to a bitmap, or printed to a higher DPI medium it can be desirable to render the chart at a higher resolution than the default. Setting `devicePixelRatio` to a value other than 1 will force the canvas size to be scaled by that amount, relative to the container size. There should be no visible difference on screen; the difference will only be visible when the image is zoomed or printed. ## Configuration Options | Name | Type | Default | Description | ---- | ---- | ------- | ----------- | `devicePixelRatio` | `Number` | window.devicePixelRatio | Override the window's default devicePixelRatio. ================================================ FILE: src/main/resources/static/admin/dist/js/plugins/chartjs2/docs/general/fonts.html ================================================ Fonts · GitBook

                                                                        Fonts

                                                                        There are 4 special global settings that can change all of the fonts on the chart. These options are in Chart.defaults.global. The global font settings only apply when more specific options are not included in the config.

                                                                        For example, in this chart the text will all be red except for the labels in the legend.

                                                                        Chart.defaults.global.defaultFontColor = 'red';
                                                                        let chart = new Chart(ctx, {
                                                                            type: 'line',
                                                                            data: data,
                                                                            options: {
                                                                                legend: {
                                                                                    labels: {
                                                                                        // This more specific font property overrides the global property
                                                                                        fontColor: 'black'
                                                                                    }
                                                                                }
                                                                            }
                                                                        });
                                                                        
                                                                        Name Type Default Description
                                                                        defaultFontColor Color '#666' Default font color for all text.
                                                                        defaultFontFamily String "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif" Default font family for all text.
                                                                        defaultFontSize Number 12 Default font size (in px) for text. Does not apply to radialLinear scale point labels.
                                                                        defaultFontStyle String 'normal' Default font style. Does not apply to tooltip title or footer. Does not apply to chart title.

                                                                        Non-Existant Fonts

                                                                        If a font is specified for a chart that does exist on the system, the browser will not apply the font when it is set. If you notice odd fonts appearing in your charts, check that the font you are applying exists on your system. See issue 3318 for more details.

                                                                        results matching ""

                                                                          No results matching ""

                                                                          ================================================ FILE: src/main/resources/static/admin/dist/js/plugins/chartjs2/docs/general/index.html ================================================ General · GitBook

                                                                          General Configuration

                                                                          These sections describe general configuration options that can apply elsewhere in the documentation.

                                                                          • Responsive defines responsive chart options that apply to all charts.
                                                                          • Device Pixel Ratio defines the ratio between display pixels and rendered pixels.
                                                                          • Interactions defines options that reflect how hovering chart elements works.
                                                                          • Options scriptable and indexable options syntax.
                                                                          • Colors defines acceptable color values.
                                                                          • Font defines various font options.

                                                                          results matching ""

                                                                            No results matching ""

                                                                            ================================================ FILE: src/main/resources/static/admin/dist/js/plugins/chartjs2/docs/general/interactions/events.html ================================================ Events · GitBook

                                                                            Events

                                                                            The following properties define how the chart interacts with events.

                                                                            Name Type Default Description
                                                                            events String[] ["mousemove", "mouseout", "click", "touchstart", "touchmove", "touchend"] The events option defines the browser events that the chart should listen to for tooltips and hovering. more...
                                                                            onHover Function null Called when any of the events fire. Called in the context of the chart and passed the event and an array of active elements (bars, points, etc).
                                                                            onClick Function null Called if the event is of type 'mouseup' or 'click'. Called in the context of the chart and passed the event and an array of active elements

                                                                            Event Option

                                                                            For example, to have the chart only respond to click events, you could do

                                                                            var chart = new Chart(ctx, {
                                                                                type: 'line',
                                                                                data: data,
                                                                                options: {
                                                                                    // This chart will not respond to mousemove, etc
                                                                                    events: ['click']
                                                                                }
                                                                            });
                                                                            

                                                                            results matching ""

                                                                              No results matching ""

                                                                              ================================================ FILE: src/main/resources/static/admin/dist/js/plugins/chartjs2/docs/general/interactions/index.html ================================================ Interactions · GitBook

                                                                              Interactions

                                                                              The hover configuration is passed into the options.hover namespace. The global hover configuration is at Chart.defaults.global.hover. To configure which events trigger chart interactions, see events.

                                                                              Name Type Default Description
                                                                              mode String 'nearest' Sets which elements appear in the tooltip. See Interaction Modes for details.
                                                                              intersect Boolean true if true, the hover mode only applies when the mouse position intersects an item on the chart.
                                                                              axis String 'x' Can be set to 'x', 'y', or 'xy' to define which directions are used in calculating distances. Defaults to 'x' for index mode and 'xy' in dataset and nearest modes.
                                                                              animationDuration Number 400 Duration in milliseconds it takes to animate hover style changes.

                                                                              results matching ""

                                                                                No results matching ""

                                                                                ================================================ FILE: src/main/resources/static/admin/dist/js/plugins/chartjs2/docs/general/interactions/modes.html ================================================ Modes · GitBook

                                                                                Interaction Modes

                                                                                When configuring interaction with the graph via hover or tooltips, a number of different modes are available.

                                                                                The modes are detailed below and how they behave in conjunction with the intersect setting.

                                                                                point

                                                                                Finds all of the items that intersect the point.

                                                                                var chart = new Chart(ctx, {
                                                                                    type: 'line',
                                                                                    data: data,
                                                                                    options: {
                                                                                        tooltips: {
                                                                                            mode: 'point'
                                                                                        }
                                                                                    }
                                                                                })
                                                                                

                                                                                nearest

                                                                                Gets the item that is nearest to the point. The nearest item is determined based on the distance to the center of the chart item (point, bar). If 2 or more items are at the same distance, the one with the smallest area is used. If intersect is true, this is only triggered when the mouse position intersects an item in the graph. This is very useful for combo charts where points are hidden behind bars.

                                                                                var chart = new Chart(ctx, {
                                                                                    type: 'line',
                                                                                    data: data,
                                                                                    options: {
                                                                                        tooltips: {
                                                                                            mode: 'nearest'
                                                                                        }
                                                                                    }
                                                                                })
                                                                                

                                                                                single (deprecated)

                                                                                Finds the first item that intersects the point and returns it. Behaves like 'nearest' mode with intersect = true.

                                                                                label (deprecated)

                                                                                See 'index' mode

                                                                                index

                                                                                Finds item at the same index. If the intersect setting is true, the first intersecting item is used to determine the index in the data. If intersect false the nearest item, in the x direction, is used to determine the index.

                                                                                var chart = new Chart(ctx, {
                                                                                    type: 'line',
                                                                                    data: data,
                                                                                    options: {
                                                                                        tooltips: {
                                                                                            mode: 'index'
                                                                                        }
                                                                                    }
                                                                                })
                                                                                

                                                                                To use index mode in a chart like the horizontal bar chart, where we search along the y direction, you can use the axis setting introduced in v2.7.0. By setting this value to 'y' on the y direction is used.

                                                                                var chart = new Chart(ctx, {
                                                                                    type: 'horizontalBar',
                                                                                    data: data,
                                                                                    options: {
                                                                                        tooltips: {
                                                                                            mode: 'index',
                                                                                            axis: 'y'
                                                                                        }
                                                                                    }
                                                                                })
                                                                                

                                                                                x-axis (deprecated)

                                                                                Behaves like 'index' mode with intersect = false.

                                                                                dataset

                                                                                Finds items in the same dataset. If the intersect setting is true, the first intersecting item is used to determine the index in the data. If intersect false the nearest item is used to determine the index.

                                                                                var chart = new Chart(ctx, {
                                                                                    type: 'line',
                                                                                    data: data,
                                                                                    options: {
                                                                                        tooltips: {
                                                                                            mode: 'dataset'
                                                                                        }
                                                                                    }
                                                                                })
                                                                                

                                                                                x

                                                                                Returns all items that would intersect based on the X coordinate of the position only. Would be useful for a vertical cursor implementation. Note that this only applies to cartesian charts

                                                                                var chart = new Chart(ctx, {
                                                                                    type: 'line',
                                                                                    data: data,
                                                                                    options: {
                                                                                        tooltips: {
                                                                                            mode: 'x'
                                                                                        }
                                                                                    }
                                                                                })
                                                                                

                                                                                y

                                                                                Returns all items that would intersect based on the Y coordinate of the position. This would be useful for a horizontal cursor implementation. Note that this only applies to cartesian charts.

                                                                                var chart = new Chart(ctx, {
                                                                                    type: 'line',
                                                                                    data: data,
                                                                                    options: {
                                                                                        tooltips: {
                                                                                            mode: 'y'
                                                                                        }
                                                                                    }
                                                                                })
                                                                                

                                                                                results matching ""

                                                                                  No results matching ""

                                                                                  ================================================ FILE: src/main/resources/static/admin/dist/js/plugins/chartjs2/docs/general/options.html ================================================ Options · GitBook

                                                                                  Options

                                                                                  Scriptable Options

                                                                                  Scriptable options also accept a function which is called for each data and that takes the unique argument context representing contextual information (see option context).

                                                                                  Example:

                                                                                  color: function(context) {
                                                                                      var index = context.dataIndex;
                                                                                      var value = context.dataset.data[index];
                                                                                      return value < 0 ? 'red' :  // draw negative values in red
                                                                                          index % 2 ? 'blue' :    // else, alternate values in blue and green
                                                                                          'green';
                                                                                  }
                                                                                  

                                                                                  Note: scriptable options are only supported by a few bubble chart options.

                                                                                  Indexable Options

                                                                                  Indexable options also accept an array in which each item corresponds to the element at the same index. Note that this method requires to provide as many items as data, so, in most cases, using a function is more appropriated if supported.

                                                                                  Example:

                                                                                  color: [
                                                                                      'red',    // color for data at index 0
                                                                                      'blue',   // color for data at index 1
                                                                                      'green',  // color for data at index 2
                                                                                      'black',  // color for data at index 3
                                                                                      //...
                                                                                  ]
                                                                                  

                                                                                  Option Context

                                                                                  The option context is used to give contextual information when resolving options and currently only applies to scriptable options.

                                                                                  The context object contains the following properties:

                                                                                  • chart: the associated chart
                                                                                  • dataIndex: index of the current data
                                                                                  • dataset: dataset at index datasetIndex
                                                                                  • datasetIndex: index of the current dataset

                                                                                  Important: since the context can represent different types of entities (dataset, data, etc.), some properties may be undefined so be sure to test any context property before using it.

                                                                                  results matching ""

                                                                                    No results matching ""

                                                                                    ================================================ FILE: src/main/resources/static/admin/dist/js/plugins/chartjs2/docs/general/responsive.html ================================================ Responsive · GitBook

                                                                                    Responsive Charts

                                                                                    When it comes to change the chart size based on the window size, a major limitation is that the canvas render size (canvas.width and .height) can not be expressed with relative values, contrary to the display size (canvas.style.width and .height). Furthermore, these sizes are independent from each other and thus the canvas render size does not adjust automatically based on the display size, making the rendering inaccurate.

                                                                                    The following examples do not work:

                                                                                    • <canvas height="40vh" width="80vw">: invalid values, the canvas doesn't resize (example)
                                                                                    • <canvas style="height:40vh; width:80vw">: invalid behavior, the canvas is resized but becomes blurry (example)

                                                                                    Chart.js provides a few options to enable responsiveness and control the resize behavior of charts by detecting when the canvas display size changes and update the render size accordingly.

                                                                                    Configuration Options

                                                                                    Name Type Default Description
                                                                                    responsive Boolean true Resizes the chart canvas when its container does (important note...).
                                                                                    responsiveAnimationDuration Number 0 Duration in milliseconds it takes to animate to new size after a resize event.
                                                                                    maintainAspectRatio Boolean true Maintain the original canvas aspect ratio (width / height) when resizing.
                                                                                    onResize Function null Called when a resize occurs. Gets passed two arguments: the chart instance and the new size.

                                                                                    Important Note

                                                                                    Detecting when the canvas size changes can not be done directly from the CANVAS element. Chart.js uses its parent container to update the canvas render and display sizes. However, this method requires the container to be relatively positioned and dedicated to the chart canvas only. Responsiveness can then be achieved by setting relative values for the container size (example):

                                                                                    <div class="chart-container" style="position: relative; height:40vh; width:80vw">
                                                                                        <canvas id="chart"></canvas>
                                                                                    </div>
                                                                                    

                                                                                    The chart can also be programmatically resized by modifying the container size:

                                                                                    chart.canvas.parentNode.style.height = '128px';
                                                                                    

                                                                                    results matching ""

                                                                                      No results matching ""

                                                                                      ================================================ FILE: src/main/resources/static/admin/dist/js/plugins/chartjs2/docs/getting-started/index.html ================================================ Getting Started · GitBook

                                                                                      Getting Started

                                                                                      Let's get started using Chart.js!

                                                                                      First, we need to have a canvas in our page.

                                                                                      <canvas id="myChart"></canvas>
                                                                                      

                                                                                      Now that we have a canvas we can use, we need to include Chart.js in our page.

                                                                                      <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.4.0/Chart.min.js"></script>
                                                                                      

                                                                                      Now, we can create a chart. We add a script to our page:

                                                                                      var ctx = document.getElementById('myChart').getContext('2d');
                                                                                      var chart = new Chart(ctx, {
                                                                                          // The type of chart we want to create
                                                                                          type: 'line',
                                                                                      
                                                                                          // The data for our dataset
                                                                                          data: {
                                                                                              labels: ["January", "February", "March", "April", "May", "June", "July"],
                                                                                              datasets: [{
                                                                                                  label: "My First dataset",
                                                                                                  backgroundColor: 'rgb(255, 99, 132)',
                                                                                                  borderColor: 'rgb(255, 99, 132)',
                                                                                                  data: [0, 10, 5, 2, 20, 30, 45],
                                                                                              }]
                                                                                          },
                                                                                      
                                                                                          // Configuration options go here
                                                                                          options: {}
                                                                                      });
                                                                                      

                                                                                      It's that easy to get started using Chart.js! From here you can explore the many options that can help you customise your charts with scales, tooltips, labels, colors, custom actions, and much more.

                                                                                      There are many examples of Chart.js that are available in the /samples folder of Chart.js.zip that is attatched to every release.

                                                                                      results matching ""

                                                                                        No results matching ""

                                                                                        ================================================ FILE: src/main/resources/static/admin/dist/js/plugins/chartjs2/docs/getting-started/installation.html ================================================ Installation · GitBook

                                                                                        Installation

                                                                                        Chart.js can be installed via npm or bower. It is recommended to get Chart.js this way.

                                                                                        npm

                                                                                        npm npm

                                                                                        npm install chart.js --save
                                                                                        

                                                                                        Bower

                                                                                        bower

                                                                                        bower install chart.js --save
                                                                                        

                                                                                        CDN

                                                                                        CDNJS

                                                                                        cdnjs

                                                                                        Chart.js built files are available on CDNJS:

                                                                                        https://cdnjs.com/libraries/Chart.js

                                                                                        jsDelivr

                                                                                        jsdelivr jsdelivr hits

                                                                                        Chart.js built files are also available through jsDelivr:

                                                                                        https://www.jsdelivr.com/package/npm/chart.js?path=dist

                                                                                        Github

                                                                                        github

                                                                                        You can download the latest version of Chart.js on GitHub.

                                                                                        If you download or clone the repository, you must build Chart.js to generate the dist files. Chart.js no longer comes with prebuilt release versions, so an alternative option to downloading the repo is strongly advised.

                                                                                        Selecting the Correct Build

                                                                                        Chart.js provides two different builds that are available for your use.

                                                                                        Stand-Alone Build

                                                                                        Files:

                                                                                        • dist/Chart.js
                                                                                        • dist/Chart.min.js

                                                                                        This version only includes Chart.js. If this version is used and you require the use of the time axis, Moment.js will need to be included before Chart.js.

                                                                                        Bundled Build

                                                                                        Files:

                                                                                        • dist/Chart.bundle.js
                                                                                        • dist/Chart.bundle.min.js

                                                                                        The bundled version includes Moment.js built into the same file. This version should be used if you wish to use time axes and want a single file to include. Do not use this build if your application already includes Moment.js. If you do, Moment.js will be included twice, increasing the page load time and potentially introducing version issues.

                                                                                        results matching ""

                                                                                          No results matching ""

                                                                                          ================================================ FILE: src/main/resources/static/admin/dist/js/plugins/chartjs2/docs/getting-started/integration.html ================================================ Integration · GitBook

                                                                                          Integration

                                                                                          Chart.js can be integrated with plain JavaScript or with different module loaders. The examples below show how to load Chart.js in different systems.

                                                                                          ES6 Modules

                                                                                          import Chart from 'chart.js';
                                                                                          var myChart = new Chart(ctx, {...});
                                                                                          

                                                                                          Script Tag

                                                                                          <script src="path/to/chartjs/dist/Chart.js"></script>
                                                                                          <script>
                                                                                              var myChart = new Chart(ctx, {...});
                                                                                          </script>
                                                                                          

                                                                                          Common JS

                                                                                          var Chart = require('chart.js');
                                                                                          var myChart = new Chart(ctx, {...});
                                                                                          

                                                                                          Require JS

                                                                                          require(['path/to/chartjs/dist/Chart.js'], function(Chart){
                                                                                              var myChart = new Chart(ctx, {...});
                                                                                          });
                                                                                          

                                                                                          Important: RequireJS can not load CommonJS module as is, so be sure to require one of the built UMD files instead (i.e. dist/Chart.js, dist/Chart.min.js, etc.).

                                                                                          results matching ""

                                                                                            No results matching ""

                                                                                            ================================================ FILE: src/main/resources/static/admin/dist/js/plugins/chartjs2/docs/getting-started/usage.html ================================================ Usage · GitBook

                                                                                            Usage

                                                                                            Chart.js can be used with ES6 modules, plain JavaScript and module loaders.

                                                                                            Creating a Chart

                                                                                            To create a chart, we need to instantiate the Chart class. To do this, we need to pass in the node, jQuery instance, or 2d context of the canvas of where we want to draw the chart. Here's an example.

                                                                                            <canvas id="myChart" width="400" height="400"></canvas>
                                                                                            
                                                                                            // Any of the following formats may be used
                                                                                            var ctx = document.getElementById("myChart");
                                                                                            var ctx = document.getElementById("myChart").getContext("2d");
                                                                                            var ctx = $("#myChart");
                                                                                            var ctx = "myChart";
                                                                                            

                                                                                            Once you have the element or context, you're ready to instantiate a pre-defined chart-type or create your own!

                                                                                            The following example instantiates a bar chart showing the number of votes for different colors and the y-axis starting at 0.

                                                                                            <canvas id="myChart" width="400" height="400"></canvas>
                                                                                            <script>
                                                                                            var ctx = document.getElementById("myChart");
                                                                                            var myChart = new Chart(ctx, {
                                                                                                type: 'bar',
                                                                                                data: {
                                                                                                    labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
                                                                                                    datasets: [{
                                                                                                        label: '# of Votes',
                                                                                                        data: [12, 19, 3, 5, 2, 3],
                                                                                                        backgroundColor: [
                                                                                                            'rgba(255, 99, 132, 0.2)',
                                                                                                            'rgba(54, 162, 235, 0.2)',
                                                                                                            'rgba(255, 206, 86, 0.2)',
                                                                                                            'rgba(75, 192, 192, 0.2)',
                                                                                                            'rgba(153, 102, 255, 0.2)',
                                                                                                            'rgba(255, 159, 64, 0.2)'
                                                                                                        ],
                                                                                                        borderColor: [
                                                                                                            'rgba(255,99,132,1)',
                                                                                                            'rgba(54, 162, 235, 1)',
                                                                                                            'rgba(255, 206, 86, 1)',
                                                                                                            'rgba(75, 192, 192, 1)',
                                                                                                            'rgba(153, 102, 255, 1)',
                                                                                                            'rgba(255, 159, 64, 1)'
                                                                                                        ],
                                                                                                        borderWidth: 1
                                                                                                    }]
                                                                                                },
                                                                                                options: {
                                                                                                    scales: {
                                                                                                        yAxes: [{
                                                                                                            ticks: {
                                                                                                                beginAtZero:true
                                                                                                            }
                                                                                                        }]
                                                                                                    }
                                                                                                }
                                                                                            });
                                                                                            </script>
                                                                                            

                                                                                            results matching ""

                                                                                              No results matching ""

                                                                                              ================================================ FILE: src/main/resources/static/admin/dist/js/plugins/chartjs2/docs/gitbook/gitbook-plugin-anchorjs/anchor-style.js ================================================ gitbook.events.bind('start', function(e, config) { anchors.options = config.anchorjs || {}; }); gitbook.events.bind('page.change', function() { anchors.add('h1,h2,h3,h4,h5'); }); ================================================ FILE: src/main/resources/static/admin/dist/js/plugins/chartjs2/docs/gitbook/gitbook-plugin-chartjs/Chart.bundle.js ================================================ /*! * Chart.js * http://chartjs.org/ * Version: 2.5.0 * * Copyright 2017 Nick Downie * Released under the MIT license * https://github.com/chartjs/Chart.js/blob/master/LICENSE.md */ (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Chart = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o lum2) { return (lum1 + 0.05) / (lum2 + 0.05); } return (lum2 + 0.05) / (lum1 + 0.05); }, level: function (color2) { var contrastRatio = this.contrast(color2); if (contrastRatio >= 7.1) { return 'AAA'; } return (contrastRatio >= 4.5) ? 'AA' : ''; }, dark: function () { // YIQ equation from http://24ways.org/2010/calculating-color-contrast var rgb = this.values.rgb; var yiq = (rgb[0] * 299 + rgb[1] * 587 + rgb[2] * 114) / 1000; return yiq < 128; }, light: function () { return !this.dark(); }, negate: function () { var rgb = []; for (var i = 0; i < 3; i++) { rgb[i] = 255 - this.values.rgb[i]; } this.setValues('rgb', rgb); return this; }, lighten: function (ratio) { var hsl = this.values.hsl; hsl[2] += hsl[2] * ratio; this.setValues('hsl', hsl); return this; }, darken: function (ratio) { var hsl = this.values.hsl; hsl[2] -= hsl[2] * ratio; this.setValues('hsl', hsl); return this; }, saturate: function (ratio) { var hsl = this.values.hsl; hsl[1] += hsl[1] * ratio; this.setValues('hsl', hsl); return this; }, desaturate: function (ratio) { var hsl = this.values.hsl; hsl[1] -= hsl[1] * ratio; this.setValues('hsl', hsl); return this; }, whiten: function (ratio) { var hwb = this.values.hwb; hwb[1] += hwb[1] * ratio; this.setValues('hwb', hwb); return this; }, blacken: function (ratio) { var hwb = this.values.hwb; hwb[2] += hwb[2] * ratio; this.setValues('hwb', hwb); return this; }, greyscale: function () { var rgb = this.values.rgb; // http://en.wikipedia.org/wiki/Grayscale#Converting_color_to_grayscale var val = rgb[0] * 0.3 + rgb[1] * 0.59 + rgb[2] * 0.11; this.setValues('rgb', [val, val, val]); return this; }, clearer: function (ratio) { var alpha = this.values.alpha; this.setValues('alpha', alpha - (alpha * ratio)); return this; }, opaquer: function (ratio) { var alpha = this.values.alpha; this.setValues('alpha', alpha + (alpha * ratio)); return this; }, rotate: function (degrees) { var hsl = this.values.hsl; var hue = (hsl[0] + degrees) % 360; hsl[0] = hue < 0 ? 360 + hue : hue; this.setValues('hsl', hsl); return this; }, /** * Ported from sass implementation in C * https://github.com/sass/libsass/blob/0e6b4a2850092356aa3ece07c6b249f0221caced/functions.cpp#L209 */ mix: function (mixinColor, weight) { var color1 = this; var color2 = mixinColor; var p = weight === undefined ? 0.5 : weight; var w = 2 * p - 1; var a = color1.alpha() - color2.alpha(); var w1 = (((w * a === -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0; var w2 = 1 - w1; return this .rgb( w1 * color1.red() + w2 * color2.red(), w1 * color1.green() + w2 * color2.green(), w1 * color1.blue() + w2 * color2.blue() ) .alpha(color1.alpha() * p + color2.alpha() * (1 - p)); }, toJSON: function () { return this.rgb(); }, clone: function () { // NOTE(SB): using node-clone creates a dependency to Buffer when using browserify, // making the final build way to big to embed in Chart.js. So let's do it manually, // assuming that values to clone are 1 dimension arrays containing only numbers, // except 'alpha' which is a number. var result = new Color(); var source = this.values; var target = result.values; var value, type; for (var prop in source) { if (source.hasOwnProperty(prop)) { value = source[prop]; type = ({}).toString.call(value); if (type === '[object Array]') { target[prop] = value.slice(0); } else if (type === '[object Number]') { target[prop] = value; } else { console.error('unexpected color value:', value); } } } return result; } }; Color.prototype.spaces = { rgb: ['red', 'green', 'blue'], hsl: ['hue', 'saturation', 'lightness'], hsv: ['hue', 'saturation', 'value'], hwb: ['hue', 'whiteness', 'blackness'], cmyk: ['cyan', 'magenta', 'yellow', 'black'] }; Color.prototype.maxes = { rgb: [255, 255, 255], hsl: [360, 100, 100], hsv: [360, 100, 100], hwb: [360, 100, 100], cmyk: [100, 100, 100, 100] }; Color.prototype.getValues = function (space) { var values = this.values; var vals = {}; for (var i = 0; i < space.length; i++) { vals[space.charAt(i)] = values[space][i]; } if (values.alpha !== 1) { vals.a = values.alpha; } // {r: 255, g: 255, b: 255, a: 0.4} return vals; }; Color.prototype.setValues = function (space, vals) { var values = this.values; var spaces = this.spaces; var maxes = this.maxes; var alpha = 1; var i; if (space === 'alpha') { alpha = vals; } else if (vals.length) { // [10, 10, 10] values[space] = vals.slice(0, space.length); alpha = vals[space.length]; } else if (vals[space.charAt(0)] !== undefined) { // {r: 10, g: 10, b: 10} for (i = 0; i < space.length; i++) { values[space][i] = vals[space.charAt(i)]; } alpha = vals.a; } else if (vals[spaces[space][0]] !== undefined) { // {red: 10, green: 10, blue: 10} var chans = spaces[space]; for (i = 0; i < space.length; i++) { values[space][i] = vals[chans[i]]; } alpha = vals.alpha; } values.alpha = Math.max(0, Math.min(1, (alpha === undefined ? values.alpha : alpha))); if (space === 'alpha') { return false; } var capped; // cap values of the space prior converting all values for (i = 0; i < space.length; i++) { capped = Math.max(0, Math.min(maxes[space][i], values[space][i])); values[space][i] = Math.round(capped); } // convert to all the other color spaces for (var sname in spaces) { if (sname !== space) { values[sname] = convert[space][sname](values[space]); } } return true; }; Color.prototype.setSpace = function (space, args) { var vals = args[0]; if (vals === undefined) { // color.rgb() return this.getValues(space); } // color.rgb(10, 10, 10) if (typeof vals === 'number') { vals = Array.prototype.slice.call(args); } this.setValues(space, vals); return this; }; Color.prototype.setChannel = function (space, index, val) { var svalues = this.values[space]; if (val === undefined) { // color.red() return svalues[index]; } else if (val === svalues[index]) { // color.red(color.red()) return this; } // color.red(100) svalues[index] = val; this.setValues(space, svalues); return this; }; if (typeof window !== 'undefined') { window.Color = Color; } module.exports = Color; },{"1":1,"4":4}],3:[function(require,module,exports){ /* MIT license */ module.exports = { rgb2hsl: rgb2hsl, rgb2hsv: rgb2hsv, rgb2hwb: rgb2hwb, rgb2cmyk: rgb2cmyk, rgb2keyword: rgb2keyword, rgb2xyz: rgb2xyz, rgb2lab: rgb2lab, rgb2lch: rgb2lch, hsl2rgb: hsl2rgb, hsl2hsv: hsl2hsv, hsl2hwb: hsl2hwb, hsl2cmyk: hsl2cmyk, hsl2keyword: hsl2keyword, hsv2rgb: hsv2rgb, hsv2hsl: hsv2hsl, hsv2hwb: hsv2hwb, hsv2cmyk: hsv2cmyk, hsv2keyword: hsv2keyword, hwb2rgb: hwb2rgb, hwb2hsl: hwb2hsl, hwb2hsv: hwb2hsv, hwb2cmyk: hwb2cmyk, hwb2keyword: hwb2keyword, cmyk2rgb: cmyk2rgb, cmyk2hsl: cmyk2hsl, cmyk2hsv: cmyk2hsv, cmyk2hwb: cmyk2hwb, cmyk2keyword: cmyk2keyword, keyword2rgb: keyword2rgb, keyword2hsl: keyword2hsl, keyword2hsv: keyword2hsv, keyword2hwb: keyword2hwb, keyword2cmyk: keyword2cmyk, keyword2lab: keyword2lab, keyword2xyz: keyword2xyz, xyz2rgb: xyz2rgb, xyz2lab: xyz2lab, xyz2lch: xyz2lch, lab2xyz: lab2xyz, lab2rgb: lab2rgb, lab2lch: lab2lch, lch2lab: lch2lab, lch2xyz: lch2xyz, lch2rgb: lch2rgb } function rgb2hsl(rgb) { var r = rgb[0]/255, g = rgb[1]/255, b = rgb[2]/255, min = Math.min(r, g, b), max = Math.max(r, g, b), delta = max - min, h, s, l; if (max == min) h = 0; else if (r == max) h = (g - b) / delta; else if (g == max) h = 2 + (b - r) / delta; else if (b == max) h = 4 + (r - g)/ delta; h = Math.min(h * 60, 360); if (h < 0) h += 360; l = (min + max) / 2; if (max == min) s = 0; else if (l <= 0.5) s = delta / (max + min); else s = delta / (2 - max - min); return [h, s * 100, l * 100]; } function rgb2hsv(rgb) { var r = rgb[0], g = rgb[1], b = rgb[2], min = Math.min(r, g, b), max = Math.max(r, g, b), delta = max - min, h, s, v; if (max == 0) s = 0; else s = (delta/max * 1000)/10; if (max == min) h = 0; else if (r == max) h = (g - b) / delta; else if (g == max) h = 2 + (b - r) / delta; else if (b == max) h = 4 + (r - g) / delta; h = Math.min(h * 60, 360); if (h < 0) h += 360; v = ((max / 255) * 1000) / 10; return [h, s, v]; } function rgb2hwb(rgb) { var r = rgb[0], g = rgb[1], b = rgb[2], h = rgb2hsl(rgb)[0], w = 1/255 * Math.min(r, Math.min(g, b)), b = 1 - 1/255 * Math.max(r, Math.max(g, b)); return [h, w * 100, b * 100]; } function rgb2cmyk(rgb) { var r = rgb[0] / 255, g = rgb[1] / 255, b = rgb[2] / 255, c, m, y, k; k = Math.min(1 - r, 1 - g, 1 - b); c = (1 - r - k) / (1 - k) || 0; m = (1 - g - k) / (1 - k) || 0; y = (1 - b - k) / (1 - k) || 0; return [c * 100, m * 100, y * 100, k * 100]; } function rgb2keyword(rgb) { return reverseKeywords[JSON.stringify(rgb)]; } function rgb2xyz(rgb) { var r = rgb[0] / 255, g = rgb[1] / 255, b = rgb[2] / 255; // assume sRGB r = r > 0.04045 ? Math.pow(((r + 0.055) / 1.055), 2.4) : (r / 12.92); g = g > 0.04045 ? Math.pow(((g + 0.055) / 1.055), 2.4) : (g / 12.92); b = b > 0.04045 ? Math.pow(((b + 0.055) / 1.055), 2.4) : (b / 12.92); var x = (r * 0.4124) + (g * 0.3576) + (b * 0.1805); var y = (r * 0.2126) + (g * 0.7152) + (b * 0.0722); var z = (r * 0.0193) + (g * 0.1192) + (b * 0.9505); return [x * 100, y *100, z * 100]; } function rgb2lab(rgb) { var xyz = rgb2xyz(rgb), x = xyz[0], y = xyz[1], z = xyz[2], l, a, b; x /= 95.047; y /= 100; z /= 108.883; x = x > 0.008856 ? Math.pow(x, 1/3) : (7.787 * x) + (16 / 116); y = y > 0.008856 ? Math.pow(y, 1/3) : (7.787 * y) + (16 / 116); z = z > 0.008856 ? Math.pow(z, 1/3) : (7.787 * z) + (16 / 116); l = (116 * y) - 16; a = 500 * (x - y); b = 200 * (y - z); return [l, a, b]; } function rgb2lch(args) { return lab2lch(rgb2lab(args)); } function hsl2rgb(hsl) { var h = hsl[0] / 360, s = hsl[1] / 100, l = hsl[2] / 100, t1, t2, t3, rgb, val; if (s == 0) { val = l * 255; return [val, val, val]; } if (l < 0.5) t2 = l * (1 + s); else t2 = l + s - l * s; t1 = 2 * l - t2; rgb = [0, 0, 0]; for (var i = 0; i < 3; i++) { t3 = h + 1 / 3 * - (i - 1); t3 < 0 && t3++; t3 > 1 && t3--; if (6 * t3 < 1) val = t1 + (t2 - t1) * 6 * t3; else if (2 * t3 < 1) val = t2; else if (3 * t3 < 2) val = t1 + (t2 - t1) * (2 / 3 - t3) * 6; else val = t1; rgb[i] = val * 255; } return rgb; } function hsl2hsv(hsl) { var h = hsl[0], s = hsl[1] / 100, l = hsl[2] / 100, sv, v; if(l === 0) { // no need to do calc on black // also avoids divide by 0 error return [0, 0, 0]; } l *= 2; s *= (l <= 1) ? l : 2 - l; v = (l + s) / 2; sv = (2 * s) / (l + s); return [h, sv * 100, v * 100]; } function hsl2hwb(args) { return rgb2hwb(hsl2rgb(args)); } function hsl2cmyk(args) { return rgb2cmyk(hsl2rgb(args)); } function hsl2keyword(args) { return rgb2keyword(hsl2rgb(args)); } function hsv2rgb(hsv) { var h = hsv[0] / 60, s = hsv[1] / 100, v = hsv[2] / 100, hi = Math.floor(h) % 6; var f = h - Math.floor(h), p = 255 * v * (1 - s), q = 255 * v * (1 - (s * f)), t = 255 * v * (1 - (s * (1 - f))), v = 255 * v; switch(hi) { case 0: return [v, t, p]; case 1: return [q, v, p]; case 2: return [p, v, t]; case 3: return [p, q, v]; case 4: return [t, p, v]; case 5: return [v, p, q]; } } function hsv2hsl(hsv) { var h = hsv[0], s = hsv[1] / 100, v = hsv[2] / 100, sl, l; l = (2 - s) * v; sl = s * v; sl /= (l <= 1) ? l : 2 - l; sl = sl || 0; l /= 2; return [h, sl * 100, l * 100]; } function hsv2hwb(args) { return rgb2hwb(hsv2rgb(args)) } function hsv2cmyk(args) { return rgb2cmyk(hsv2rgb(args)); } function hsv2keyword(args) { return rgb2keyword(hsv2rgb(args)); } // http://dev.w3.org/csswg/css-color/#hwb-to-rgb function hwb2rgb(hwb) { var h = hwb[0] / 360, wh = hwb[1] / 100, bl = hwb[2] / 100, ratio = wh + bl, i, v, f, n; // wh + bl cant be > 1 if (ratio > 1) { wh /= ratio; bl /= ratio; } i = Math.floor(6 * h); v = 1 - bl; f = 6 * h - i; if ((i & 0x01) != 0) { f = 1 - f; } n = wh + f * (v - wh); // linear interpolation switch (i) { default: case 6: case 0: r = v; g = n; b = wh; break; case 1: r = n; g = v; b = wh; break; case 2: r = wh; g = v; b = n; break; case 3: r = wh; g = n; b = v; break; case 4: r = n; g = wh; b = v; break; case 5: r = v; g = wh; b = n; break; } return [r * 255, g * 255, b * 255]; } function hwb2hsl(args) { return rgb2hsl(hwb2rgb(args)); } function hwb2hsv(args) { return rgb2hsv(hwb2rgb(args)); } function hwb2cmyk(args) { return rgb2cmyk(hwb2rgb(args)); } function hwb2keyword(args) { return rgb2keyword(hwb2rgb(args)); } function cmyk2rgb(cmyk) { var c = cmyk[0] / 100, m = cmyk[1] / 100, y = cmyk[2] / 100, k = cmyk[3] / 100, r, g, b; r = 1 - Math.min(1, c * (1 - k) + k); g = 1 - Math.min(1, m * (1 - k) + k); b = 1 - Math.min(1, y * (1 - k) + k); return [r * 255, g * 255, b * 255]; } function cmyk2hsl(args) { return rgb2hsl(cmyk2rgb(args)); } function cmyk2hsv(args) { return rgb2hsv(cmyk2rgb(args)); } function cmyk2hwb(args) { return rgb2hwb(cmyk2rgb(args)); } function cmyk2keyword(args) { return rgb2keyword(cmyk2rgb(args)); } function xyz2rgb(xyz) { var x = xyz[0] / 100, y = xyz[1] / 100, z = xyz[2] / 100, r, g, b; r = (x * 3.2406) + (y * -1.5372) + (z * -0.4986); g = (x * -0.9689) + (y * 1.8758) + (z * 0.0415); b = (x * 0.0557) + (y * -0.2040) + (z * 1.0570); // assume sRGB r = r > 0.0031308 ? ((1.055 * Math.pow(r, 1.0 / 2.4)) - 0.055) : r = (r * 12.92); g = g > 0.0031308 ? ((1.055 * Math.pow(g, 1.0 / 2.4)) - 0.055) : g = (g * 12.92); b = b > 0.0031308 ? ((1.055 * Math.pow(b, 1.0 / 2.4)) - 0.055) : b = (b * 12.92); r = Math.min(Math.max(0, r), 1); g = Math.min(Math.max(0, g), 1); b = Math.min(Math.max(0, b), 1); return [r * 255, g * 255, b * 255]; } function xyz2lab(xyz) { var x = xyz[0], y = xyz[1], z = xyz[2], l, a, b; x /= 95.047; y /= 100; z /= 108.883; x = x > 0.008856 ? Math.pow(x, 1/3) : (7.787 * x) + (16 / 116); y = y > 0.008856 ? Math.pow(y, 1/3) : (7.787 * y) + (16 / 116); z = z > 0.008856 ? Math.pow(z, 1/3) : (7.787 * z) + (16 / 116); l = (116 * y) - 16; a = 500 * (x - y); b = 200 * (y - z); return [l, a, b]; } function xyz2lch(args) { return lab2lch(xyz2lab(args)); } function lab2xyz(lab) { var l = lab[0], a = lab[1], b = lab[2], x, y, z, y2; if (l <= 8) { y = (l * 100) / 903.3; y2 = (7.787 * (y / 100)) + (16 / 116); } else { y = 100 * Math.pow((l + 16) / 116, 3); y2 = Math.pow(y / 100, 1/3); } x = x / 95.047 <= 0.008856 ? x = (95.047 * ((a / 500) + y2 - (16 / 116))) / 7.787 : 95.047 * Math.pow((a / 500) + y2, 3); z = z / 108.883 <= 0.008859 ? z = (108.883 * (y2 - (b / 200) - (16 / 116))) / 7.787 : 108.883 * Math.pow(y2 - (b / 200), 3); return [x, y, z]; } function lab2lch(lab) { var l = lab[0], a = lab[1], b = lab[2], hr, h, c; hr = Math.atan2(b, a); h = hr * 360 / 2 / Math.PI; if (h < 0) { h += 360; } c = Math.sqrt(a * a + b * b); return [l, c, h]; } function lab2rgb(args) { return xyz2rgb(lab2xyz(args)); } function lch2lab(lch) { var l = lch[0], c = lch[1], h = lch[2], a, b, hr; hr = h / 360 * 2 * Math.PI; a = c * Math.cos(hr); b = c * Math.sin(hr); return [l, a, b]; } function lch2xyz(args) { return lab2xyz(lch2lab(args)); } function lch2rgb(args) { return lab2rgb(lch2lab(args)); } function keyword2rgb(keyword) { return cssKeywords[keyword]; } function keyword2hsl(args) { return rgb2hsl(keyword2rgb(args)); } function keyword2hsv(args) { return rgb2hsv(keyword2rgb(args)); } function keyword2hwb(args) { return rgb2hwb(keyword2rgb(args)); } function keyword2cmyk(args) { return rgb2cmyk(keyword2rgb(args)); } function keyword2lab(args) { return rgb2lab(keyword2rgb(args)); } function keyword2xyz(args) { return rgb2xyz(keyword2rgb(args)); } var cssKeywords = { aliceblue: [240,248,255], antiquewhite: [250,235,215], aqua: [0,255,255], aquamarine: [127,255,212], azure: [240,255,255], beige: [245,245,220], bisque: [255,228,196], black: [0,0,0], blanchedalmond: [255,235,205], blue: [0,0,255], blueviolet: [138,43,226], brown: [165,42,42], burlywood: [222,184,135], cadetblue: [95,158,160], chartreuse: [127,255,0], chocolate: [210,105,30], coral: [255,127,80], cornflowerblue: [100,149,237], cornsilk: [255,248,220], crimson: [220,20,60], cyan: [0,255,255], darkblue: [0,0,139], darkcyan: [0,139,139], darkgoldenrod: [184,134,11], darkgray: [169,169,169], darkgreen: [0,100,0], darkgrey: [169,169,169], darkkhaki: [189,183,107], darkmagenta: [139,0,139], darkolivegreen: [85,107,47], darkorange: [255,140,0], darkorchid: [153,50,204], darkred: [139,0,0], darksalmon: [233,150,122], darkseagreen: [143,188,143], darkslateblue: [72,61,139], darkslategray: [47,79,79], darkslategrey: [47,79,79], darkturquoise: [0,206,209], darkviolet: [148,0,211], deeppink: [255,20,147], deepskyblue: [0,191,255], dimgray: [105,105,105], dimgrey: [105,105,105], dodgerblue: [30,144,255], firebrick: [178,34,34], floralwhite: [255,250,240], forestgreen: [34,139,34], fuchsia: [255,0,255], gainsboro: [220,220,220], ghostwhite: [248,248,255], gold: [255,215,0], goldenrod: [218,165,32], gray: [128,128,128], green: [0,128,0], greenyellow: [173,255,47], grey: [128,128,128], honeydew: [240,255,240], hotpink: [255,105,180], indianred: [205,92,92], indigo: [75,0,130], ivory: [255,255,240], khaki: [240,230,140], lavender: [230,230,250], lavenderblush: [255,240,245], lawngreen: [124,252,0], lemonchiffon: [255,250,205], lightblue: [173,216,230], lightcoral: [240,128,128], lightcyan: [224,255,255], lightgoldenrodyellow: [250,250,210], lightgray: [211,211,211], lightgreen: [144,238,144], lightgrey: [211,211,211], lightpink: [255,182,193], lightsalmon: [255,160,122], lightseagreen: [32,178,170], lightskyblue: [135,206,250], lightslategray: [119,136,153], lightslategrey: [119,136,153], lightsteelblue: [176,196,222], lightyellow: [255,255,224], lime: [0,255,0], limegreen: [50,205,50], linen: [250,240,230], magenta: [255,0,255], maroon: [128,0,0], mediumaquamarine: [102,205,170], mediumblue: [0,0,205], mediumorchid: [186,85,211], mediumpurple: [147,112,219], mediumseagreen: [60,179,113], mediumslateblue: [123,104,238], mediumspringgreen: [0,250,154], mediumturquoise: [72,209,204], mediumvioletred: [199,21,133], midnightblue: [25,25,112], mintcream: [245,255,250], mistyrose: [255,228,225], moccasin: [255,228,181], navajowhite: [255,222,173], navy: [0,0,128], oldlace: [253,245,230], olive: [128,128,0], olivedrab: [107,142,35], orange: [255,165,0], orangered: [255,69,0], orchid: [218,112,214], palegoldenrod: [238,232,170], palegreen: [152,251,152], paleturquoise: [175,238,238], palevioletred: [219,112,147], papayawhip: [255,239,213], peachpuff: [255,218,185], peru: [205,133,63], pink: [255,192,203], plum: [221,160,221], powderblue: [176,224,230], purple: [128,0,128], rebeccapurple: [102, 51, 153], red: [255,0,0], rosybrown: [188,143,143], royalblue: [65,105,225], saddlebrown: [139,69,19], salmon: [250,128,114], sandybrown: [244,164,96], seagreen: [46,139,87], seashell: [255,245,238], sienna: [160,82,45], silver: [192,192,192], skyblue: [135,206,235], slateblue: [106,90,205], slategray: [112,128,144], slategrey: [112,128,144], snow: [255,250,250], springgreen: [0,255,127], steelblue: [70,130,180], tan: [210,180,140], teal: [0,128,128], thistle: [216,191,216], tomato: [255,99,71], turquoise: [64,224,208], violet: [238,130,238], wheat: [245,222,179], white: [255,255,255], whitesmoke: [245,245,245], yellow: [255,255,0], yellowgreen: [154,205,50] }; var reverseKeywords = {}; for (var key in cssKeywords) { reverseKeywords[JSON.stringify(cssKeywords[key])] = key; } },{}],4:[function(require,module,exports){ var conversions = require(3); var convert = function() { return new Converter(); } for (var func in conversions) { // export Raw versions convert[func + "Raw"] = (function(func) { // accept array or plain args return function(arg) { if (typeof arg == "number") arg = Array.prototype.slice.call(arguments); return conversions[func](arg); } })(func); var pair = /(\w+)2(\w+)/.exec(func), from = pair[1], to = pair[2]; // export rgb2hsl and ["rgb"]["hsl"] convert[from] = convert[from] || {}; convert[from][to] = convert[func] = (function(func) { return function(arg) { if (typeof arg == "number") arg = Array.prototype.slice.call(arguments); var val = conversions[func](arg); if (typeof val == "string" || val === undefined) return val; // keyword for (var i = 0; i < val.length; i++) val[i] = Math.round(val[i]); return val; } })(func); } /* Converter does lazy conversion and caching */ var Converter = function() { this.convs = {}; }; /* Either get the values for a space or set the values for a space, depending on args */ Converter.prototype.routeSpace = function(space, args) { var values = args[0]; if (values === undefined) { // color.rgb() return this.getValues(space); } // color.rgb(10, 10, 10) if (typeof values == "number") { values = Array.prototype.slice.call(args); } return this.setValues(space, values); }; /* Set the values for a space, invalidating cache */ Converter.prototype.setValues = function(space, values) { this.space = space; this.convs = {}; this.convs[space] = values; return this; }; /* Get the values for a space. If there's already a conversion for the space, fetch it, otherwise compute it */ Converter.prototype.getValues = function(space) { var vals = this.convs[space]; if (!vals) { var fspace = this.space, from = this.convs[fspace]; vals = convert[fspace][space](from); this.convs[space] = vals; } return vals; }; ["rgb", "hsl", "hsv", "cmyk", "keyword"].forEach(function(space) { Converter.prototype[space] = function(vals) { return this.routeSpace(space, arguments); } }); module.exports = convert; },{"3":3}],5:[function(require,module,exports){ module.exports = { "aliceblue": [240, 248, 255], "antiquewhite": [250, 235, 215], "aqua": [0, 255, 255], "aquamarine": [127, 255, 212], "azure": [240, 255, 255], "beige": [245, 245, 220], "bisque": [255, 228, 196], "black": [0, 0, 0], "blanchedalmond": [255, 235, 205], "blue": [0, 0, 255], "blueviolet": [138, 43, 226], "brown": [165, 42, 42], "burlywood": [222, 184, 135], "cadetblue": [95, 158, 160], "chartreuse": [127, 255, 0], "chocolate": [210, 105, 30], "coral": [255, 127, 80], "cornflowerblue": [100, 149, 237], "cornsilk": [255, 248, 220], "crimson": [220, 20, 60], "cyan": [0, 255, 255], "darkblue": [0, 0, 139], "darkcyan": [0, 139, 139], "darkgoldenrod": [184, 134, 11], "darkgray": [169, 169, 169], "darkgreen": [0, 100, 0], "darkgrey": [169, 169, 169], "darkkhaki": [189, 183, 107], "darkmagenta": [139, 0, 139], "darkolivegreen": [85, 107, 47], "darkorange": [255, 140, 0], "darkorchid": [153, 50, 204], "darkred": [139, 0, 0], "darksalmon": [233, 150, 122], "darkseagreen": [143, 188, 143], "darkslateblue": [72, 61, 139], "darkslategray": [47, 79, 79], "darkslategrey": [47, 79, 79], "darkturquoise": [0, 206, 209], "darkviolet": [148, 0, 211], "deeppink": [255, 20, 147], "deepskyblue": [0, 191, 255], "dimgray": [105, 105, 105], "dimgrey": [105, 105, 105], "dodgerblue": [30, 144, 255], "firebrick": [178, 34, 34], "floralwhite": [255, 250, 240], "forestgreen": [34, 139, 34], "fuchsia": [255, 0, 255], "gainsboro": [220, 220, 220], "ghostwhite": [248, 248, 255], "gold": [255, 215, 0], "goldenrod": [218, 165, 32], "gray": [128, 128, 128], "green": [0, 128, 0], "greenyellow": [173, 255, 47], "grey": [128, 128, 128], "honeydew": [240, 255, 240], "hotpink": [255, 105, 180], "indianred": [205, 92, 92], "indigo": [75, 0, 130], "ivory": [255, 255, 240], "khaki": [240, 230, 140], "lavender": [230, 230, 250], "lavenderblush": [255, 240, 245], "lawngreen": [124, 252, 0], "lemonchiffon": [255, 250, 205], "lightblue": [173, 216, 230], "lightcoral": [240, 128, 128], "lightcyan": [224, 255, 255], "lightgoldenrodyellow": [250, 250, 210], "lightgray": [211, 211, 211], "lightgreen": [144, 238, 144], "lightgrey": [211, 211, 211], "lightpink": [255, 182, 193], "lightsalmon": [255, 160, 122], "lightseagreen": [32, 178, 170], "lightskyblue": [135, 206, 250], "lightslategray": [119, 136, 153], "lightslategrey": [119, 136, 153], "lightsteelblue": [176, 196, 222], "lightyellow": [255, 255, 224], "lime": [0, 255, 0], "limegreen": [50, 205, 50], "linen": [250, 240, 230], "magenta": [255, 0, 255], "maroon": [128, 0, 0], "mediumaquamarine": [102, 205, 170], "mediumblue": [0, 0, 205], "mediumorchid": [186, 85, 211], "mediumpurple": [147, 112, 219], "mediumseagreen": [60, 179, 113], "mediumslateblue": [123, 104, 238], "mediumspringgreen": [0, 250, 154], "mediumturquoise": [72, 209, 204], "mediumvioletred": [199, 21, 133], "midnightblue": [25, 25, 112], "mintcream": [245, 255, 250], "mistyrose": [255, 228, 225], "moccasin": [255, 228, 181], "navajowhite": [255, 222, 173], "navy": [0, 0, 128], "oldlace": [253, 245, 230], "olive": [128, 128, 0], "olivedrab": [107, 142, 35], "orange": [255, 165, 0], "orangered": [255, 69, 0], "orchid": [218, 112, 214], "palegoldenrod": [238, 232, 170], "palegreen": [152, 251, 152], "paleturquoise": [175, 238, 238], "palevioletred": [219, 112, 147], "papayawhip": [255, 239, 213], "peachpuff": [255, 218, 185], "peru": [205, 133, 63], "pink": [255, 192, 203], "plum": [221, 160, 221], "powderblue": [176, 224, 230], "purple": [128, 0, 128], "rebeccapurple": [102, 51, 153], "red": [255, 0, 0], "rosybrown": [188, 143, 143], "royalblue": [65, 105, 225], "saddlebrown": [139, 69, 19], "salmon": [250, 128, 114], "sandybrown": [244, 164, 96], "seagreen": [46, 139, 87], "seashell": [255, 245, 238], "sienna": [160, 82, 45], "silver": [192, 192, 192], "skyblue": [135, 206, 235], "slateblue": [106, 90, 205], "slategray": [112, 128, 144], "slategrey": [112, 128, 144], "snow": [255, 250, 250], "springgreen": [0, 255, 127], "steelblue": [70, 130, 180], "tan": [210, 180, 140], "teal": [0, 128, 128], "thistle": [216, 191, 216], "tomato": [255, 99, 71], "turquoise": [64, 224, 208], "violet": [238, 130, 238], "wheat": [245, 222, 179], "white": [255, 255, 255], "whitesmoke": [245, 245, 245], "yellow": [255, 255, 0], "yellowgreen": [154, 205, 50] }; },{}],6:[function(require,module,exports){ //! moment.js //! version : 2.17.1 //! authors : Tim Wood, Iskren Chernev, Moment.js contributors //! license : MIT //! momentjs.com ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof define === 'function' && define.amd ? define(factory) : global.moment = factory() }(this, (function () { 'use strict'; var hookCallback; function hooks () { return hookCallback.apply(null, arguments); } // This is done to register the method called with moment() // without creating circular dependencies. function setHookCallback (callback) { hookCallback = callback; } function isArray(input) { return input instanceof Array || Object.prototype.toString.call(input) === '[object Array]'; } function isObject(input) { // IE8 will treat undefined and null as object if it wasn't for // input != null return input != null && Object.prototype.toString.call(input) === '[object Object]'; } function isObjectEmpty(obj) { var k; for (k in obj) { // even if its not own property I'd still call it non-empty return false; } return true; } function isNumber(input) { return typeof input === 'number' || Object.prototype.toString.call(input) === '[object Number]'; } function isDate(input) { return input instanceof Date || Object.prototype.toString.call(input) === '[object Date]'; } function map(arr, fn) { var res = [], i; for (i = 0; i < arr.length; ++i) { res.push(fn(arr[i], i)); } return res; } function hasOwnProp(a, b) { return Object.prototype.hasOwnProperty.call(a, b); } function extend(a, b) { for (var i in b) { if (hasOwnProp(b, i)) { a[i] = b[i]; } } if (hasOwnProp(b, 'toString')) { a.toString = b.toString; } if (hasOwnProp(b, 'valueOf')) { a.valueOf = b.valueOf; } return a; } function createUTC (input, format, locale, strict) { return createLocalOrUTC(input, format, locale, strict, true).utc(); } function defaultParsingFlags() { // We need to deep clone this object. return { empty : false, unusedTokens : [], unusedInput : [], overflow : -2, charsLeftOver : 0, nullInput : false, invalidMonth : null, invalidFormat : false, userInvalidated : false, iso : false, parsedDateParts : [], meridiem : null }; } function getParsingFlags(m) { if (m._pf == null) { m._pf = defaultParsingFlags(); } return m._pf; } var some; if (Array.prototype.some) { some = Array.prototype.some; } else { some = function (fun) { var t = Object(this); var len = t.length >>> 0; for (var i = 0; i < len; i++) { if (i in t && fun.call(this, t[i], i, t)) { return true; } } return false; }; } var some$1 = some; function isValid(m) { if (m._isValid == null) { var flags = getParsingFlags(m); var parsedParts = some$1.call(flags.parsedDateParts, function (i) { return i != null; }); var isNowValid = !isNaN(m._d.getTime()) && flags.overflow < 0 && !flags.empty && !flags.invalidMonth && !flags.invalidWeekday && !flags.nullInput && !flags.invalidFormat && !flags.userInvalidated && (!flags.meridiem || (flags.meridiem && parsedParts)); if (m._strict) { isNowValid = isNowValid && flags.charsLeftOver === 0 && flags.unusedTokens.length === 0 && flags.bigHour === undefined; } if (Object.isFrozen == null || !Object.isFrozen(m)) { m._isValid = isNowValid; } else { return isNowValid; } } return m._isValid; } function createInvalid (flags) { var m = createUTC(NaN); if (flags != null) { extend(getParsingFlags(m), flags); } else { getParsingFlags(m).userInvalidated = true; } return m; } function isUndefined(input) { return input === void 0; } // Plugins that add properties should also add the key here (null value), // so we can properly clone ourselves. var momentProperties = hooks.momentProperties = []; function copyConfig(to, from) { var i, prop, val; if (!isUndefined(from._isAMomentObject)) { to._isAMomentObject = from._isAMomentObject; } if (!isUndefined(from._i)) { to._i = from._i; } if (!isUndefined(from._f)) { to._f = from._f; } if (!isUndefined(from._l)) { to._l = from._l; } if (!isUndefined(from._strict)) { to._strict = from._strict; } if (!isUndefined(from._tzm)) { to._tzm = from._tzm; } if (!isUndefined(from._isUTC)) { to._isUTC = from._isUTC; } if (!isUndefined(from._offset)) { to._offset = from._offset; } if (!isUndefined(from._pf)) { to._pf = getParsingFlags(from); } if (!isUndefined(from._locale)) { to._locale = from._locale; } if (momentProperties.length > 0) { for (i in momentProperties) { prop = momentProperties[i]; val = from[prop]; if (!isUndefined(val)) { to[prop] = val; } } } return to; } var updateInProgress = false; // Moment prototype object function Moment(config) { copyConfig(this, config); this._d = new Date(config._d != null ? config._d.getTime() : NaN); if (!this.isValid()) { this._d = new Date(NaN); } // Prevent infinite loop in case updateOffset creates new moment // objects. if (updateInProgress === false) { updateInProgress = true; hooks.updateOffset(this); updateInProgress = false; } } function isMoment (obj) { return obj instanceof Moment || (obj != null && obj._isAMomentObject != null); } function absFloor (number) { if (number < 0) { // -0 -> 0 return Math.ceil(number) || 0; } else { return Math.floor(number); } } function toInt(argumentForCoercion) { var coercedNumber = +argumentForCoercion, value = 0; if (coercedNumber !== 0 && isFinite(coercedNumber)) { value = absFloor(coercedNumber); } return value; } // compare two arrays, return the number of differences function compareArrays(array1, array2, dontConvert) { var len = Math.min(array1.length, array2.length), lengthDiff = Math.abs(array1.length - array2.length), diffs = 0, i; for (i = 0; i < len; i++) { if ((dontConvert && array1[i] !== array2[i]) || (!dontConvert && toInt(array1[i]) !== toInt(array2[i]))) { diffs++; } } return diffs + lengthDiff; } function warn(msg) { if (hooks.suppressDeprecationWarnings === false && (typeof console !== 'undefined') && console.warn) { console.warn('Deprecation warning: ' + msg); } } function deprecate(msg, fn) { var firstTime = true; return extend(function () { if (hooks.deprecationHandler != null) { hooks.deprecationHandler(null, msg); } if (firstTime) { var args = []; var arg; for (var i = 0; i < arguments.length; i++) { arg = ''; if (typeof arguments[i] === 'object') { arg += '\n[' + i + '] '; for (var key in arguments[0]) { arg += key + ': ' + arguments[0][key] + ', '; } arg = arg.slice(0, -2); // Remove trailing comma and space } else { arg = arguments[i]; } args.push(arg); } warn(msg + '\nArguments: ' + Array.prototype.slice.call(args).join('') + '\n' + (new Error()).stack); firstTime = false; } return fn.apply(this, arguments); }, fn); } var deprecations = {}; function deprecateSimple(name, msg) { if (hooks.deprecationHandler != null) { hooks.deprecationHandler(name, msg); } if (!deprecations[name]) { warn(msg); deprecations[name] = true; } } hooks.suppressDeprecationWarnings = false; hooks.deprecationHandler = null; function isFunction(input) { return input instanceof Function || Object.prototype.toString.call(input) === '[object Function]'; } function set (config) { var prop, i; for (i in config) { prop = config[i]; if (isFunction(prop)) { this[i] = prop; } else { this['_' + i] = prop; } } this._config = config; // Lenient ordinal parsing accepts just a number in addition to // number + (possibly) stuff coming from _ordinalParseLenient. this._ordinalParseLenient = new RegExp(this._ordinalParse.source + '|' + (/\d{1,2}/).source); } function mergeConfigs(parentConfig, childConfig) { var res = extend({}, parentConfig), prop; for (prop in childConfig) { if (hasOwnProp(childConfig, prop)) { if (isObject(parentConfig[prop]) && isObject(childConfig[prop])) { res[prop] = {}; extend(res[prop], parentConfig[prop]); extend(res[prop], childConfig[prop]); } else if (childConfig[prop] != null) { res[prop] = childConfig[prop]; } else { delete res[prop]; } } } for (prop in parentConfig) { if (hasOwnProp(parentConfig, prop) && !hasOwnProp(childConfig, prop) && isObject(parentConfig[prop])) { // make sure changes to properties don't modify parent config res[prop] = extend({}, res[prop]); } } return res; } function Locale(config) { if (config != null) { this.set(config); } } var keys; if (Object.keys) { keys = Object.keys; } else { keys = function (obj) { var i, res = []; for (i in obj) { if (hasOwnProp(obj, i)) { res.push(i); } } return res; }; } var keys$1 = keys; var defaultCalendar = { sameDay : '[Today at] LT', nextDay : '[Tomorrow at] LT', nextWeek : 'dddd [at] LT', lastDay : '[Yesterday at] LT', lastWeek : '[Last] dddd [at] LT', sameElse : 'L' }; function calendar (key, mom, now) { var output = this._calendar[key] || this._calendar['sameElse']; return isFunction(output) ? output.call(mom, now) : output; } var defaultLongDateFormat = { LTS : 'h:mm:ss A', LT : 'h:mm A', L : 'MM/DD/YYYY', LL : 'MMMM D, YYYY', LLL : 'MMMM D, YYYY h:mm A', LLLL : 'dddd, MMMM D, YYYY h:mm A' }; function longDateFormat (key) { var format = this._longDateFormat[key], formatUpper = this._longDateFormat[key.toUpperCase()]; if (format || !formatUpper) { return format; } this._longDateFormat[key] = formatUpper.replace(/MMMM|MM|DD|dddd/g, function (val) { return val.slice(1); }); return this._longDateFormat[key]; } var defaultInvalidDate = 'Invalid date'; function invalidDate () { return this._invalidDate; } var defaultOrdinal = '%d'; var defaultOrdinalParse = /\d{1,2}/; function ordinal (number) { return this._ordinal.replace('%d', number); } var defaultRelativeTime = { future : 'in %s', past : '%s ago', s : 'a few seconds', m : 'a minute', mm : '%d minutes', h : 'an hour', hh : '%d hours', d : 'a day', dd : '%d days', M : 'a month', MM : '%d months', y : 'a year', yy : '%d years' }; function relativeTime (number, withoutSuffix, string, isFuture) { var output = this._relativeTime[string]; return (isFunction(output)) ? output(number, withoutSuffix, string, isFuture) : output.replace(/%d/i, number); } function pastFuture (diff, output) { var format = this._relativeTime[diff > 0 ? 'future' : 'past']; return isFunction(format) ? format(output) : format.replace(/%s/i, output); } var aliases = {}; function addUnitAlias (unit, shorthand) { var lowerCase = unit.toLowerCase(); aliases[lowerCase] = aliases[lowerCase + 's'] = aliases[shorthand] = unit; } function normalizeUnits(units) { return typeof units === 'string' ? aliases[units] || aliases[units.toLowerCase()] : undefined; } function normalizeObjectUnits(inputObject) { var normalizedInput = {}, normalizedProp, prop; for (prop in inputObject) { if (hasOwnProp(inputObject, prop)) { normalizedProp = normalizeUnits(prop); if (normalizedProp) { normalizedInput[normalizedProp] = inputObject[prop]; } } } return normalizedInput; } var priorities = {}; function addUnitPriority(unit, priority) { priorities[unit] = priority; } function getPrioritizedUnits(unitsObj) { var units = []; for (var u in unitsObj) { units.push({unit: u, priority: priorities[u]}); } units.sort(function (a, b) { return a.priority - b.priority; }); return units; } function makeGetSet (unit, keepTime) { return function (value) { if (value != null) { set$1(this, unit, value); hooks.updateOffset(this, keepTime); return this; } else { return get(this, unit); } }; } function get (mom, unit) { return mom.isValid() ? mom._d['get' + (mom._isUTC ? 'UTC' : '') + unit]() : NaN; } function set$1 (mom, unit, value) { if (mom.isValid()) { mom._d['set' + (mom._isUTC ? 'UTC' : '') + unit](value); } } // MOMENTS function stringGet (units) { units = normalizeUnits(units); if (isFunction(this[units])) { return this[units](); } return this; } function stringSet (units, value) { if (typeof units === 'object') { units = normalizeObjectUnits(units); var prioritized = getPrioritizedUnits(units); for (var i = 0; i < prioritized.length; i++) { this[prioritized[i].unit](units[prioritized[i].unit]); } } else { units = normalizeUnits(units); if (isFunction(this[units])) { return this[units](value); } } return this; } function zeroFill(number, targetLength, forceSign) { var absNumber = '' + Math.abs(number), zerosToFill = targetLength - absNumber.length, sign = number >= 0; return (sign ? (forceSign ? '+' : '') : '-') + Math.pow(10, Math.max(0, zerosToFill)).toString().substr(1) + absNumber; } var formattingTokens = /(\[[^\[]*\])|(\\)?([Hh]mm(ss)?|Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Qo?|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|kk?|mm?|ss?|S{1,9}|x|X|zz?|ZZ?|.)/g; var localFormattingTokens = /(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g; var formatFunctions = {}; var formatTokenFunctions = {}; // token: 'M' // padded: ['MM', 2] // ordinal: 'Mo' // callback: function () { this.month() + 1 } function addFormatToken (token, padded, ordinal, callback) { var func = callback; if (typeof callback === 'string') { func = function () { return this[callback](); }; } if (token) { formatTokenFunctions[token] = func; } if (padded) { formatTokenFunctions[padded[0]] = function () { return zeroFill(func.apply(this, arguments), padded[1], padded[2]); }; } if (ordinal) { formatTokenFunctions[ordinal] = function () { return this.localeData().ordinal(func.apply(this, arguments), token); }; } } function removeFormattingTokens(input) { if (input.match(/\[[\s\S]/)) { return input.replace(/^\[|\]$/g, ''); } return input.replace(/\\/g, ''); } function makeFormatFunction(format) { var array = format.match(formattingTokens), i, length; for (i = 0, length = array.length; i < length; i++) { if (formatTokenFunctions[array[i]]) { array[i] = formatTokenFunctions[array[i]]; } else { array[i] = removeFormattingTokens(array[i]); } } return function (mom) { var output = '', i; for (i = 0; i < length; i++) { output += array[i] instanceof Function ? array[i].call(mom, format) : array[i]; } return output; }; } // format date using native date object function formatMoment(m, format) { if (!m.isValid()) { return m.localeData().invalidDate(); } format = expandFormat(format, m.localeData()); formatFunctions[format] = formatFunctions[format] || makeFormatFunction(format); return formatFunctions[format](m); } function expandFormat(format, locale) { var i = 5; function replaceLongDateFormatTokens(input) { return locale.longDateFormat(input) || input; } localFormattingTokens.lastIndex = 0; while (i >= 0 && localFormattingTokens.test(format)) { format = format.replace(localFormattingTokens, replaceLongDateFormatTokens); localFormattingTokens.lastIndex = 0; i -= 1; } return format; } var match1 = /\d/; // 0 - 9 var match2 = /\d\d/; // 00 - 99 var match3 = /\d{3}/; // 000 - 999 var match4 = /\d{4}/; // 0000 - 9999 var match6 = /[+-]?\d{6}/; // -999999 - 999999 var match1to2 = /\d\d?/; // 0 - 99 var match3to4 = /\d\d\d\d?/; // 999 - 9999 var match5to6 = /\d\d\d\d\d\d?/; // 99999 - 999999 var match1to3 = /\d{1,3}/; // 0 - 999 var match1to4 = /\d{1,4}/; // 0 - 9999 var match1to6 = /[+-]?\d{1,6}/; // -999999 - 999999 var matchUnsigned = /\d+/; // 0 - inf var matchSigned = /[+-]?\d+/; // -inf - inf var matchOffset = /Z|[+-]\d\d:?\d\d/gi; // +00:00 -00:00 +0000 -0000 or Z var matchShortOffset = /Z|[+-]\d\d(?::?\d\d)?/gi; // +00 -00 +00:00 -00:00 +0000 -0000 or Z var matchTimestamp = /[+-]?\d+(\.\d{1,3})?/; // 123456789 123456789.123 // any word (or two) characters or numbers including two/three word month in arabic. // includes scottish gaelic two word and hyphenated months var matchWord = /[0-9]*['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+|[\u0600-\u06FF\/]+(\s*?[\u0600-\u06FF]+){1,2}/i; var regexes = {}; function addRegexToken (token, regex, strictRegex) { regexes[token] = isFunction(regex) ? regex : function (isStrict, localeData) { return (isStrict && strictRegex) ? strictRegex : regex; }; } function getParseRegexForToken (token, config) { if (!hasOwnProp(regexes, token)) { return new RegExp(unescapeFormat(token)); } return regexes[token](config._strict, config._locale); } // Code from http://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript function unescapeFormat(s) { return regexEscape(s.replace('\\', '').replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g, function (matched, p1, p2, p3, p4) { return p1 || p2 || p3 || p4; })); } function regexEscape(s) { return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); } var tokens = {}; function addParseToken (token, callback) { var i, func = callback; if (typeof token === 'string') { token = [token]; } if (isNumber(callback)) { func = function (input, array) { array[callback] = toInt(input); }; } for (i = 0; i < token.length; i++) { tokens[token[i]] = func; } } function addWeekParseToken (token, callback) { addParseToken(token, function (input, array, config, token) { config._w = config._w || {}; callback(input, config._w, config, token); }); } function addTimeToArrayFromToken(token, input, config) { if (input != null && hasOwnProp(tokens, token)) { tokens[token](input, config._a, config, token); } } var YEAR = 0; var MONTH = 1; var DATE = 2; var HOUR = 3; var MINUTE = 4; var SECOND = 5; var MILLISECOND = 6; var WEEK = 7; var WEEKDAY = 8; var indexOf; if (Array.prototype.indexOf) { indexOf = Array.prototype.indexOf; } else { indexOf = function (o) { // I know var i; for (i = 0; i < this.length; ++i) { if (this[i] === o) { return i; } } return -1; }; } var indexOf$1 = indexOf; function daysInMonth(year, month) { return new Date(Date.UTC(year, month + 1, 0)).getUTCDate(); } // FORMATTING addFormatToken('M', ['MM', 2], 'Mo', function () { return this.month() + 1; }); addFormatToken('MMM', 0, 0, function (format) { return this.localeData().monthsShort(this, format); }); addFormatToken('MMMM', 0, 0, function (format) { return this.localeData().months(this, format); }); // ALIASES addUnitAlias('month', 'M'); // PRIORITY addUnitPriority('month', 8); // PARSING addRegexToken('M', match1to2); addRegexToken('MM', match1to2, match2); addRegexToken('MMM', function (isStrict, locale) { return locale.monthsShortRegex(isStrict); }); addRegexToken('MMMM', function (isStrict, locale) { return locale.monthsRegex(isStrict); }); addParseToken(['M', 'MM'], function (input, array) { array[MONTH] = toInt(input) - 1; }); addParseToken(['MMM', 'MMMM'], function (input, array, config, token) { var month = config._locale.monthsParse(input, token, config._strict); // if we didn't find a month name, mark the date as invalid. if (month != null) { array[MONTH] = month; } else { getParsingFlags(config).invalidMonth = input; } }); // LOCALES var MONTHS_IN_FORMAT = /D[oD]?(\[[^\[\]]*\]|\s)+MMMM?/; var defaultLocaleMonths = 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_'); function localeMonths (m, format) { if (!m) { return this._months; } return isArray(this._months) ? this._months[m.month()] : this._months[(this._months.isFormat || MONTHS_IN_FORMAT).test(format) ? 'format' : 'standalone'][m.month()]; } var defaultLocaleMonthsShort = 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split('_'); function localeMonthsShort (m, format) { if (!m) { return this._monthsShort; } return isArray(this._monthsShort) ? this._monthsShort[m.month()] : this._monthsShort[MONTHS_IN_FORMAT.test(format) ? 'format' : 'standalone'][m.month()]; } function handleStrictParse(monthName, format, strict) { var i, ii, mom, llc = monthName.toLocaleLowerCase(); if (!this._monthsParse) { // this is not used this._monthsParse = []; this._longMonthsParse = []; this._shortMonthsParse = []; for (i = 0; i < 12; ++i) { mom = createUTC([2000, i]); this._shortMonthsParse[i] = this.monthsShort(mom, '').toLocaleLowerCase(); this._longMonthsParse[i] = this.months(mom, '').toLocaleLowerCase(); } } if (strict) { if (format === 'MMM') { ii = indexOf$1.call(this._shortMonthsParse, llc); return ii !== -1 ? ii : null; } else { ii = indexOf$1.call(this._longMonthsParse, llc); return ii !== -1 ? ii : null; } } else { if (format === 'MMM') { ii = indexOf$1.call(this._shortMonthsParse, llc); if (ii !== -1) { return ii; } ii = indexOf$1.call(this._longMonthsParse, llc); return ii !== -1 ? ii : null; } else { ii = indexOf$1.call(this._longMonthsParse, llc); if (ii !== -1) { return ii; } ii = indexOf$1.call(this._shortMonthsParse, llc); return ii !== -1 ? ii : null; } } } function localeMonthsParse (monthName, format, strict) { var i, mom, regex; if (this._monthsParseExact) { return handleStrictParse.call(this, monthName, format, strict); } if (!this._monthsParse) { this._monthsParse = []; this._longMonthsParse = []; this._shortMonthsParse = []; } // TODO: add sorting // Sorting makes sure if one month (or abbr) is a prefix of another // see sorting in computeMonthsParse for (i = 0; i < 12; i++) { // make the regex if we don't have it already mom = createUTC([2000, i]); if (strict && !this._longMonthsParse[i]) { this._longMonthsParse[i] = new RegExp('^' + this.months(mom, '').replace('.', '') + '$', 'i'); this._shortMonthsParse[i] = new RegExp('^' + this.monthsShort(mom, '').replace('.', '') + '$', 'i'); } if (!strict && !this._monthsParse[i]) { regex = '^' + this.months(mom, '') + '|^' + this.monthsShort(mom, ''); this._monthsParse[i] = new RegExp(regex.replace('.', ''), 'i'); } // test the regex if (strict && format === 'MMMM' && this._longMonthsParse[i].test(monthName)) { return i; } else if (strict && format === 'MMM' && this._shortMonthsParse[i].test(monthName)) { return i; } else if (!strict && this._monthsParse[i].test(monthName)) { return i; } } } // MOMENTS function setMonth (mom, value) { var dayOfMonth; if (!mom.isValid()) { // No op return mom; } if (typeof value === 'string') { if (/^\d+$/.test(value)) { value = toInt(value); } else { value = mom.localeData().monthsParse(value); // TODO: Another silent failure? if (!isNumber(value)) { return mom; } } } dayOfMonth = Math.min(mom.date(), daysInMonth(mom.year(), value)); mom._d['set' + (mom._isUTC ? 'UTC' : '') + 'Month'](value, dayOfMonth); return mom; } function getSetMonth (value) { if (value != null) { setMonth(this, value); hooks.updateOffset(this, true); return this; } else { return get(this, 'Month'); } } function getDaysInMonth () { return daysInMonth(this.year(), this.month()); } var defaultMonthsShortRegex = matchWord; function monthsShortRegex (isStrict) { if (this._monthsParseExact) { if (!hasOwnProp(this, '_monthsRegex')) { computeMonthsParse.call(this); } if (isStrict) { return this._monthsShortStrictRegex; } else { return this._monthsShortRegex; } } else { if (!hasOwnProp(this, '_monthsShortRegex')) { this._monthsShortRegex = defaultMonthsShortRegex; } return this._monthsShortStrictRegex && isStrict ? this._monthsShortStrictRegex : this._monthsShortRegex; } } var defaultMonthsRegex = matchWord; function monthsRegex (isStrict) { if (this._monthsParseExact) { if (!hasOwnProp(this, '_monthsRegex')) { computeMonthsParse.call(this); } if (isStrict) { return this._monthsStrictRegex; } else { return this._monthsRegex; } } else { if (!hasOwnProp(this, '_monthsRegex')) { this._monthsRegex = defaultMonthsRegex; } return this._monthsStrictRegex && isStrict ? this._monthsStrictRegex : this._monthsRegex; } } function computeMonthsParse () { function cmpLenRev(a, b) { return b.length - a.length; } var shortPieces = [], longPieces = [], mixedPieces = [], i, mom; for (i = 0; i < 12; i++) { // make the regex if we don't have it already mom = createUTC([2000, i]); shortPieces.push(this.monthsShort(mom, '')); longPieces.push(this.months(mom, '')); mixedPieces.push(this.months(mom, '')); mixedPieces.push(this.monthsShort(mom, '')); } // Sorting makes sure if one month (or abbr) is a prefix of another it // will match the longer piece. shortPieces.sort(cmpLenRev); longPieces.sort(cmpLenRev); mixedPieces.sort(cmpLenRev); for (i = 0; i < 12; i++) { shortPieces[i] = regexEscape(shortPieces[i]); longPieces[i] = regexEscape(longPieces[i]); } for (i = 0; i < 24; i++) { mixedPieces[i] = regexEscape(mixedPieces[i]); } this._monthsRegex = new RegExp('^(' + mixedPieces.join('|') + ')', 'i'); this._monthsShortRegex = this._monthsRegex; this._monthsStrictRegex = new RegExp('^(' + longPieces.join('|') + ')', 'i'); this._monthsShortStrictRegex = new RegExp('^(' + shortPieces.join('|') + ')', 'i'); } // FORMATTING addFormatToken('Y', 0, 0, function () { var y = this.year(); return y <= 9999 ? '' + y : '+' + y; }); addFormatToken(0, ['YY', 2], 0, function () { return this.year() % 100; }); addFormatToken(0, ['YYYY', 4], 0, 'year'); addFormatToken(0, ['YYYYY', 5], 0, 'year'); addFormatToken(0, ['YYYYYY', 6, true], 0, 'year'); // ALIASES addUnitAlias('year', 'y'); // PRIORITIES addUnitPriority('year', 1); // PARSING addRegexToken('Y', matchSigned); addRegexToken('YY', match1to2, match2); addRegexToken('YYYY', match1to4, match4); addRegexToken('YYYYY', match1to6, match6); addRegexToken('YYYYYY', match1to6, match6); addParseToken(['YYYYY', 'YYYYYY'], YEAR); addParseToken('YYYY', function (input, array) { array[YEAR] = input.length === 2 ? hooks.parseTwoDigitYear(input) : toInt(input); }); addParseToken('YY', function (input, array) { array[YEAR] = hooks.parseTwoDigitYear(input); }); addParseToken('Y', function (input, array) { array[YEAR] = parseInt(input, 10); }); // HELPERS function daysInYear(year) { return isLeapYear(year) ? 366 : 365; } function isLeapYear(year) { return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0; } // HOOKS hooks.parseTwoDigitYear = function (input) { return toInt(input) + (toInt(input) > 68 ? 1900 : 2000); }; // MOMENTS var getSetYear = makeGetSet('FullYear', true); function getIsLeapYear () { return isLeapYear(this.year()); } function createDate (y, m, d, h, M, s, ms) { //can't just apply() to create a date: //http://stackoverflow.com/questions/181348/instantiating-a-javascript-object-by-calling-prototype-constructor-apply var date = new Date(y, m, d, h, M, s, ms); //the date constructor remaps years 0-99 to 1900-1999 if (y < 100 && y >= 0 && isFinite(date.getFullYear())) { date.setFullYear(y); } return date; } function createUTCDate (y) { var date = new Date(Date.UTC.apply(null, arguments)); //the Date.UTC function remaps years 0-99 to 1900-1999 if (y < 100 && y >= 0 && isFinite(date.getUTCFullYear())) { date.setUTCFullYear(y); } return date; } // start-of-first-week - start-of-year function firstWeekOffset(year, dow, doy) { var // first-week day -- which january is always in the first week (4 for iso, 1 for other) fwd = 7 + dow - doy, // first-week day local weekday -- which local weekday is fwd fwdlw = (7 + createUTCDate(year, 0, fwd).getUTCDay() - dow) % 7; return -fwdlw + fwd - 1; } //http://en.wikipedia.org/wiki/ISO_week_date#Calculating_a_date_given_the_year.2C_week_number_and_weekday function dayOfYearFromWeeks(year, week, weekday, dow, doy) { var localWeekday = (7 + weekday - dow) % 7, weekOffset = firstWeekOffset(year, dow, doy), dayOfYear = 1 + 7 * (week - 1) + localWeekday + weekOffset, resYear, resDayOfYear; if (dayOfYear <= 0) { resYear = year - 1; resDayOfYear = daysInYear(resYear) + dayOfYear; } else if (dayOfYear > daysInYear(year)) { resYear = year + 1; resDayOfYear = dayOfYear - daysInYear(year); } else { resYear = year; resDayOfYear = dayOfYear; } return { year: resYear, dayOfYear: resDayOfYear }; } function weekOfYear(mom, dow, doy) { var weekOffset = firstWeekOffset(mom.year(), dow, doy), week = Math.floor((mom.dayOfYear() - weekOffset - 1) / 7) + 1, resWeek, resYear; if (week < 1) { resYear = mom.year() - 1; resWeek = week + weeksInYear(resYear, dow, doy); } else if (week > weeksInYear(mom.year(), dow, doy)) { resWeek = week - weeksInYear(mom.year(), dow, doy); resYear = mom.year() + 1; } else { resYear = mom.year(); resWeek = week; } return { week: resWeek, year: resYear }; } function weeksInYear(year, dow, doy) { var weekOffset = firstWeekOffset(year, dow, doy), weekOffsetNext = firstWeekOffset(year + 1, dow, doy); return (daysInYear(year) - weekOffset + weekOffsetNext) / 7; } // FORMATTING addFormatToken('w', ['ww', 2], 'wo', 'week'); addFormatToken('W', ['WW', 2], 'Wo', 'isoWeek'); // ALIASES addUnitAlias('week', 'w'); addUnitAlias('isoWeek', 'W'); // PRIORITIES addUnitPriority('week', 5); addUnitPriority('isoWeek', 5); // PARSING addRegexToken('w', match1to2); addRegexToken('ww', match1to2, match2); addRegexToken('W', match1to2); addRegexToken('WW', match1to2, match2); addWeekParseToken(['w', 'ww', 'W', 'WW'], function (input, week, config, token) { week[token.substr(0, 1)] = toInt(input); }); // HELPERS // LOCALES function localeWeek (mom) { return weekOfYear(mom, this._week.dow, this._week.doy).week; } var defaultLocaleWeek = { dow : 0, // Sunday is the first day of the week. doy : 6 // The week that contains Jan 1st is the first week of the year. }; function localeFirstDayOfWeek () { return this._week.dow; } function localeFirstDayOfYear () { return this._week.doy; } // MOMENTS function getSetWeek (input) { var week = this.localeData().week(this); return input == null ? week : this.add((input - week) * 7, 'd'); } function getSetISOWeek (input) { var week = weekOfYear(this, 1, 4).week; return input == null ? week : this.add((input - week) * 7, 'd'); } // FORMATTING addFormatToken('d', 0, 'do', 'day'); addFormatToken('dd', 0, 0, function (format) { return this.localeData().weekdaysMin(this, format); }); addFormatToken('ddd', 0, 0, function (format) { return this.localeData().weekdaysShort(this, format); }); addFormatToken('dddd', 0, 0, function (format) { return this.localeData().weekdays(this, format); }); addFormatToken('e', 0, 0, 'weekday'); addFormatToken('E', 0, 0, 'isoWeekday'); // ALIASES addUnitAlias('day', 'd'); addUnitAlias('weekday', 'e'); addUnitAlias('isoWeekday', 'E'); // PRIORITY addUnitPriority('day', 11); addUnitPriority('weekday', 11); addUnitPriority('isoWeekday', 11); // PARSING addRegexToken('d', match1to2); addRegexToken('e', match1to2); addRegexToken('E', match1to2); addRegexToken('dd', function (isStrict, locale) { return locale.weekdaysMinRegex(isStrict); }); addRegexToken('ddd', function (isStrict, locale) { return locale.weekdaysShortRegex(isStrict); }); addRegexToken('dddd', function (isStrict, locale) { return locale.weekdaysRegex(isStrict); }); addWeekParseToken(['dd', 'ddd', 'dddd'], function (input, week, config, token) { var weekday = config._locale.weekdaysParse(input, token, config._strict); // if we didn't get a weekday name, mark the date as invalid if (weekday != null) { week.d = weekday; } else { getParsingFlags(config).invalidWeekday = input; } }); addWeekParseToken(['d', 'e', 'E'], function (input, week, config, token) { week[token] = toInt(input); }); // HELPERS function parseWeekday(input, locale) { if (typeof input !== 'string') { return input; } if (!isNaN(input)) { return parseInt(input, 10); } input = locale.weekdaysParse(input); if (typeof input === 'number') { return input; } return null; } function parseIsoWeekday(input, locale) { if (typeof input === 'string') { return locale.weekdaysParse(input) % 7 || 7; } return isNaN(input) ? null : input; } // LOCALES var defaultLocaleWeekdays = 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_'); function localeWeekdays (m, format) { if (!m) { return this._weekdays; } return isArray(this._weekdays) ? this._weekdays[m.day()] : this._weekdays[this._weekdays.isFormat.test(format) ? 'format' : 'standalone'][m.day()]; } var defaultLocaleWeekdaysShort = 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_'); function localeWeekdaysShort (m) { return (m) ? this._weekdaysShort[m.day()] : this._weekdaysShort; } var defaultLocaleWeekdaysMin = 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_'); function localeWeekdaysMin (m) { return (m) ? this._weekdaysMin[m.day()] : this._weekdaysMin; } function handleStrictParse$1(weekdayName, format, strict) { var i, ii, mom, llc = weekdayName.toLocaleLowerCase(); if (!this._weekdaysParse) { this._weekdaysParse = []; this._shortWeekdaysParse = []; this._minWeekdaysParse = []; for (i = 0; i < 7; ++i) { mom = createUTC([2000, 1]).day(i); this._minWeekdaysParse[i] = this.weekdaysMin(mom, '').toLocaleLowerCase(); this._shortWeekdaysParse[i] = this.weekdaysShort(mom, '').toLocaleLowerCase(); this._weekdaysParse[i] = this.weekdays(mom, '').toLocaleLowerCase(); } } if (strict) { if (format === 'dddd') { ii = indexOf$1.call(this._weekdaysParse, llc); return ii !== -1 ? ii : null; } else if (format === 'ddd') { ii = indexOf$1.call(this._shortWeekdaysParse, llc); return ii !== -1 ? ii : null; } else { ii = indexOf$1.call(this._minWeekdaysParse, llc); return ii !== -1 ? ii : null; } } else { if (format === 'dddd') { ii = indexOf$1.call(this._weekdaysParse, llc); if (ii !== -1) { return ii; } ii = indexOf$1.call(this._shortWeekdaysParse, llc); if (ii !== -1) { return ii; } ii = indexOf$1.call(this._minWeekdaysParse, llc); return ii !== -1 ? ii : null; } else if (format === 'ddd') { ii = indexOf$1.call(this._shortWeekdaysParse, llc); if (ii !== -1) { return ii; } ii = indexOf$1.call(this._weekdaysParse, llc); if (ii !== -1) { return ii; } ii = indexOf$1.call(this._minWeekdaysParse, llc); return ii !== -1 ? ii : null; } else { ii = indexOf$1.call(this._minWeekdaysParse, llc); if (ii !== -1) { return ii; } ii = indexOf$1.call(this._weekdaysParse, llc); if (ii !== -1) { return ii; } ii = indexOf$1.call(this._shortWeekdaysParse, llc); return ii !== -1 ? ii : null; } } } function localeWeekdaysParse (weekdayName, format, strict) { var i, mom, regex; if (this._weekdaysParseExact) { return handleStrictParse$1.call(this, weekdayName, format, strict); } if (!this._weekdaysParse) { this._weekdaysParse = []; this._minWeekdaysParse = []; this._shortWeekdaysParse = []; this._fullWeekdaysParse = []; } for (i = 0; i < 7; i++) { // make the regex if we don't have it already mom = createUTC([2000, 1]).day(i); if (strict && !this._fullWeekdaysParse[i]) { this._fullWeekdaysParse[i] = new RegExp('^' + this.weekdays(mom, '').replace('.', '\.?') + '$', 'i'); this._shortWeekdaysParse[i] = new RegExp('^' + this.weekdaysShort(mom, '').replace('.', '\.?') + '$', 'i'); this._minWeekdaysParse[i] = new RegExp('^' + this.weekdaysMin(mom, '').replace('.', '\.?') + '$', 'i'); } if (!this._weekdaysParse[i]) { regex = '^' + this.weekdays(mom, '') + '|^' + this.weekdaysShort(mom, '') + '|^' + this.weekdaysMin(mom, ''); this._weekdaysParse[i] = new RegExp(regex.replace('.', ''), 'i'); } // test the regex if (strict && format === 'dddd' && this._fullWeekdaysParse[i].test(weekdayName)) { return i; } else if (strict && format === 'ddd' && this._shortWeekdaysParse[i].test(weekdayName)) { return i; } else if (strict && format === 'dd' && this._minWeekdaysParse[i].test(weekdayName)) { return i; } else if (!strict && this._weekdaysParse[i].test(weekdayName)) { return i; } } } // MOMENTS function getSetDayOfWeek (input) { if (!this.isValid()) { return input != null ? this : NaN; } var day = this._isUTC ? this._d.getUTCDay() : this._d.getDay(); if (input != null) { input = parseWeekday(input, this.localeData()); return this.add(input - day, 'd'); } else { return day; } } function getSetLocaleDayOfWeek (input) { if (!this.isValid()) { return input != null ? this : NaN; } var weekday = (this.day() + 7 - this.localeData()._week.dow) % 7; return input == null ? weekday : this.add(input - weekday, 'd'); } function getSetISODayOfWeek (input) { if (!this.isValid()) { return input != null ? this : NaN; } // behaves the same as moment#day except // as a getter, returns 7 instead of 0 (1-7 range instead of 0-6) // as a setter, sunday should belong to the previous week. if (input != null) { var weekday = parseIsoWeekday(input, this.localeData()); return this.day(this.day() % 7 ? weekday : weekday - 7); } else { return this.day() || 7; } } var defaultWeekdaysRegex = matchWord; function weekdaysRegex (isStrict) { if (this._weekdaysParseExact) { if (!hasOwnProp(this, '_weekdaysRegex')) { computeWeekdaysParse.call(this); } if (isStrict) { return this._weekdaysStrictRegex; } else { return this._weekdaysRegex; } } else { if (!hasOwnProp(this, '_weekdaysRegex')) { this._weekdaysRegex = defaultWeekdaysRegex; } return this._weekdaysStrictRegex && isStrict ? this._weekdaysStrictRegex : this._weekdaysRegex; } } var defaultWeekdaysShortRegex = matchWord; function weekdaysShortRegex (isStrict) { if (this._weekdaysParseExact) { if (!hasOwnProp(this, '_weekdaysRegex')) { computeWeekdaysParse.call(this); } if (isStrict) { return this._weekdaysShortStrictRegex; } else { return this._weekdaysShortRegex; } } else { if (!hasOwnProp(this, '_weekdaysShortRegex')) { this._weekdaysShortRegex = defaultWeekdaysShortRegex; } return this._weekdaysShortStrictRegex && isStrict ? this._weekdaysShortStrictRegex : this._weekdaysShortRegex; } } var defaultWeekdaysMinRegex = matchWord; function weekdaysMinRegex (isStrict) { if (this._weekdaysParseExact) { if (!hasOwnProp(this, '_weekdaysRegex')) { computeWeekdaysParse.call(this); } if (isStrict) { return this._weekdaysMinStrictRegex; } else { return this._weekdaysMinRegex; } } else { if (!hasOwnProp(this, '_weekdaysMinRegex')) { this._weekdaysMinRegex = defaultWeekdaysMinRegex; } return this._weekdaysMinStrictRegex && isStrict ? this._weekdaysMinStrictRegex : this._weekdaysMinRegex; } } function computeWeekdaysParse () { function cmpLenRev(a, b) { return b.length - a.length; } var minPieces = [], shortPieces = [], longPieces = [], mixedPieces = [], i, mom, minp, shortp, longp; for (i = 0; i < 7; i++) { // make the regex if we don't have it already mom = createUTC([2000, 1]).day(i); minp = this.weekdaysMin(mom, ''); shortp = this.weekdaysShort(mom, ''); longp = this.weekdays(mom, ''); minPieces.push(minp); shortPieces.push(shortp); longPieces.push(longp); mixedPieces.push(minp); mixedPieces.push(shortp); mixedPieces.push(longp); } // Sorting makes sure if one weekday (or abbr) is a prefix of another it // will match the longer piece. minPieces.sort(cmpLenRev); shortPieces.sort(cmpLenRev); longPieces.sort(cmpLenRev); mixedPieces.sort(cmpLenRev); for (i = 0; i < 7; i++) { shortPieces[i] = regexEscape(shortPieces[i]); longPieces[i] = regexEscape(longPieces[i]); mixedPieces[i] = regexEscape(mixedPieces[i]); } this._weekdaysRegex = new RegExp('^(' + mixedPieces.join('|') + ')', 'i'); this._weekdaysShortRegex = this._weekdaysRegex; this._weekdaysMinRegex = this._weekdaysRegex; this._weekdaysStrictRegex = new RegExp('^(' + longPieces.join('|') + ')', 'i'); this._weekdaysShortStrictRegex = new RegExp('^(' + shortPieces.join('|') + ')', 'i'); this._weekdaysMinStrictRegex = new RegExp('^(' + minPieces.join('|') + ')', 'i'); } // FORMATTING function hFormat() { return this.hours() % 12 || 12; } function kFormat() { return this.hours() || 24; } addFormatToken('H', ['HH', 2], 0, 'hour'); addFormatToken('h', ['hh', 2], 0, hFormat); addFormatToken('k', ['kk', 2], 0, kFormat); addFormatToken('hmm', 0, 0, function () { return '' + hFormat.apply(this) + zeroFill(this.minutes(), 2); }); addFormatToken('hmmss', 0, 0, function () { return '' + hFormat.apply(this) + zeroFill(this.minutes(), 2) + zeroFill(this.seconds(), 2); }); addFormatToken('Hmm', 0, 0, function () { return '' + this.hours() + zeroFill(this.minutes(), 2); }); addFormatToken('Hmmss', 0, 0, function () { return '' + this.hours() + zeroFill(this.minutes(), 2) + zeroFill(this.seconds(), 2); }); function meridiem (token, lowercase) { addFormatToken(token, 0, 0, function () { return this.localeData().meridiem(this.hours(), this.minutes(), lowercase); }); } meridiem('a', true); meridiem('A', false); // ALIASES addUnitAlias('hour', 'h'); // PRIORITY addUnitPriority('hour', 13); // PARSING function matchMeridiem (isStrict, locale) { return locale._meridiemParse; } addRegexToken('a', matchMeridiem); addRegexToken('A', matchMeridiem); addRegexToken('H', match1to2); addRegexToken('h', match1to2); addRegexToken('HH', match1to2, match2); addRegexToken('hh', match1to2, match2); addRegexToken('hmm', match3to4); addRegexToken('hmmss', match5to6); addRegexToken('Hmm', match3to4); addRegexToken('Hmmss', match5to6); addParseToken(['H', 'HH'], HOUR); addParseToken(['a', 'A'], function (input, array, config) { config._isPm = config._locale.isPM(input); config._meridiem = input; }); addParseToken(['h', 'hh'], function (input, array, config) { array[HOUR] = toInt(input); getParsingFlags(config).bigHour = true; }); addParseToken('hmm', function (input, array, config) { var pos = input.length - 2; array[HOUR] = toInt(input.substr(0, pos)); array[MINUTE] = toInt(input.substr(pos)); getParsingFlags(config).bigHour = true; }); addParseToken('hmmss', function (input, array, config) { var pos1 = input.length - 4; var pos2 = input.length - 2; array[HOUR] = toInt(input.substr(0, pos1)); array[MINUTE] = toInt(input.substr(pos1, 2)); array[SECOND] = toInt(input.substr(pos2)); getParsingFlags(config).bigHour = true; }); addParseToken('Hmm', function (input, array, config) { var pos = input.length - 2; array[HOUR] = toInt(input.substr(0, pos)); array[MINUTE] = toInt(input.substr(pos)); }); addParseToken('Hmmss', function (input, array, config) { var pos1 = input.length - 4; var pos2 = input.length - 2; array[HOUR] = toInt(input.substr(0, pos1)); array[MINUTE] = toInt(input.substr(pos1, 2)); array[SECOND] = toInt(input.substr(pos2)); }); // LOCALES function localeIsPM (input) { // IE8 Quirks Mode & IE7 Standards Mode do not allow accessing strings like arrays // Using charAt should be more compatible. return ((input + '').toLowerCase().charAt(0) === 'p'); } var defaultLocaleMeridiemParse = /[ap]\.?m?\.?/i; function localeMeridiem (hours, minutes, isLower) { if (hours > 11) { return isLower ? 'pm' : 'PM'; } else { return isLower ? 'am' : 'AM'; } } // MOMENTS // Setting the hour should keep the time, because the user explicitly // specified which hour he wants. So trying to maintain the same hour (in // a new timezone) makes sense. Adding/subtracting hours does not follow // this rule. var getSetHour = makeGetSet('Hours', true); // months // week // weekdays // meridiem var baseConfig = { calendar: defaultCalendar, longDateFormat: defaultLongDateFormat, invalidDate: defaultInvalidDate, ordinal: defaultOrdinal, ordinalParse: defaultOrdinalParse, relativeTime: defaultRelativeTime, months: defaultLocaleMonths, monthsShort: defaultLocaleMonthsShort, week: defaultLocaleWeek, weekdays: defaultLocaleWeekdays, weekdaysMin: defaultLocaleWeekdaysMin, weekdaysShort: defaultLocaleWeekdaysShort, meridiemParse: defaultLocaleMeridiemParse }; // internal storage for locale config files var locales = {}; var localeFamilies = {}; var globalLocale; function normalizeLocale(key) { return key ? key.toLowerCase().replace('_', '-') : key; } // pick the locale from the array // try ['en-au', 'en-gb'] as 'en-au', 'en-gb', 'en', as in move through the list trying each // substring from most specific to least, but move to the next array item if it's a more specific variant than the current root function chooseLocale(names) { var i = 0, j, next, locale, split; while (i < names.length) { split = normalizeLocale(names[i]).split('-'); j = split.length; next = normalizeLocale(names[i + 1]); next = next ? next.split('-') : null; while (j > 0) { locale = loadLocale(split.slice(0, j).join('-')); if (locale) { return locale; } if (next && next.length >= j && compareArrays(split, next, true) >= j - 1) { //the next array item is better than a shallower substring of this one break; } j--; } i++; } return null; } function loadLocale(name) { var oldLocale = null; // TODO: Find a better way to register and load all the locales in Node if (!locales[name] && (typeof module !== 'undefined') && module && module.exports) { try { oldLocale = globalLocale._abbr; require('./locale/' + name); // because defineLocale currently also sets the global locale, we // want to undo that for lazy loaded locales getSetGlobalLocale(oldLocale); } catch (e) { } } return locales[name]; } // This function will load locale and then set the global locale. If // no arguments are passed in, it will simply return the current global // locale key. function getSetGlobalLocale (key, values) { var data; if (key) { if (isUndefined(values)) { data = getLocale(key); } else { data = defineLocale(key, values); } if (data) { // moment.duration._locale = moment._locale = data; globalLocale = data; } } return globalLocale._abbr; } function defineLocale (name, config) { if (config !== null) { var parentConfig = baseConfig; config.abbr = name; if (locales[name] != null) { deprecateSimple('defineLocaleOverride', 'use moment.updateLocale(localeName, config) to change ' + 'an existing locale. moment.defineLocale(localeName, ' + 'config) should only be used for creating a new locale ' + 'See http://momentjs.com/guides/#/warnings/define-locale/ for more info.'); parentConfig = locales[name]._config; } else if (config.parentLocale != null) { if (locales[config.parentLocale] != null) { parentConfig = locales[config.parentLocale]._config; } else { if (!localeFamilies[config.parentLocale]) { localeFamilies[config.parentLocale] = []; } localeFamilies[config.parentLocale].push({ name: name, config: config }); return null; } } locales[name] = new Locale(mergeConfigs(parentConfig, config)); if (localeFamilies[name]) { localeFamilies[name].forEach(function (x) { defineLocale(x.name, x.config); }); } // backwards compat for now: also set the locale // make sure we set the locale AFTER all child locales have been // created, so we won't end up with the child locale set. getSetGlobalLocale(name); return locales[name]; } else { // useful for testing delete locales[name]; return null; } } function updateLocale(name, config) { if (config != null) { var locale, parentConfig = baseConfig; // MERGE if (locales[name] != null) { parentConfig = locales[name]._config; } config = mergeConfigs(parentConfig, config); locale = new Locale(config); locale.parentLocale = locales[name]; locales[name] = locale; // backwards compat for now: also set the locale getSetGlobalLocale(name); } else { // pass null for config to unupdate, useful for tests if (locales[name] != null) { if (locales[name].parentLocale != null) { locales[name] = locales[name].parentLocale; } else if (locales[name] != null) { delete locales[name]; } } } return locales[name]; } // returns locale data function getLocale (key) { var locale; if (key && key._locale && key._locale._abbr) { key = key._locale._abbr; } if (!key) { return globalLocale; } if (!isArray(key)) { //short-circuit everything else locale = loadLocale(key); if (locale) { return locale; } key = [key]; } return chooseLocale(key); } function listLocales() { return keys$1(locales); } function checkOverflow (m) { var overflow; var a = m._a; if (a && getParsingFlags(m).overflow === -2) { overflow = a[MONTH] < 0 || a[MONTH] > 11 ? MONTH : a[DATE] < 1 || a[DATE] > daysInMonth(a[YEAR], a[MONTH]) ? DATE : a[HOUR] < 0 || a[HOUR] > 24 || (a[HOUR] === 24 && (a[MINUTE] !== 0 || a[SECOND] !== 0 || a[MILLISECOND] !== 0)) ? HOUR : a[MINUTE] < 0 || a[MINUTE] > 59 ? MINUTE : a[SECOND] < 0 || a[SECOND] > 59 ? SECOND : a[MILLISECOND] < 0 || a[MILLISECOND] > 999 ? MILLISECOND : -1; if (getParsingFlags(m)._overflowDayOfYear && (overflow < YEAR || overflow > DATE)) { overflow = DATE; } if (getParsingFlags(m)._overflowWeeks && overflow === -1) { overflow = WEEK; } if (getParsingFlags(m)._overflowWeekday && overflow === -1) { overflow = WEEKDAY; } getParsingFlags(m).overflow = overflow; } return m; } // iso 8601 regex // 0000-00-00 0000-W00 or 0000-W00-0 + T + 00 or 00:00 or 00:00:00 or 00:00:00.000 + +00:00 or +0000 or +00) var extendedIsoRegex = /^\s*((?:[+-]\d{6}|\d{4})-(?:\d\d-\d\d|W\d\d-\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?::\d\d(?::\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/; var basicIsoRegex = /^\s*((?:[+-]\d{6}|\d{4})(?:\d\d\d\d|W\d\d\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?:\d\d(?:\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/; var tzRegex = /Z|[+-]\d\d(?::?\d\d)?/; var isoDates = [ ['YYYYYY-MM-DD', /[+-]\d{6}-\d\d-\d\d/], ['YYYY-MM-DD', /\d{4}-\d\d-\d\d/], ['GGGG-[W]WW-E', /\d{4}-W\d\d-\d/], ['GGGG-[W]WW', /\d{4}-W\d\d/, false], ['YYYY-DDD', /\d{4}-\d{3}/], ['YYYY-MM', /\d{4}-\d\d/, false], ['YYYYYYMMDD', /[+-]\d{10}/], ['YYYYMMDD', /\d{8}/], // YYYYMM is NOT allowed by the standard ['GGGG[W]WWE', /\d{4}W\d{3}/], ['GGGG[W]WW', /\d{4}W\d{2}/, false], ['YYYYDDD', /\d{7}/] ]; // iso time formats and regexes var isoTimes = [ ['HH:mm:ss.SSSS', /\d\d:\d\d:\d\d\.\d+/], ['HH:mm:ss,SSSS', /\d\d:\d\d:\d\d,\d+/], ['HH:mm:ss', /\d\d:\d\d:\d\d/], ['HH:mm', /\d\d:\d\d/], ['HHmmss.SSSS', /\d\d\d\d\d\d\.\d+/], ['HHmmss,SSSS', /\d\d\d\d\d\d,\d+/], ['HHmmss', /\d\d\d\d\d\d/], ['HHmm', /\d\d\d\d/], ['HH', /\d\d/] ]; var aspNetJsonRegex = /^\/?Date\((\-?\d+)/i; // date from iso format function configFromISO(config) { var i, l, string = config._i, match = extendedIsoRegex.exec(string) || basicIsoRegex.exec(string), allowTime, dateFormat, timeFormat, tzFormat; if (match) { getParsingFlags(config).iso = true; for (i = 0, l = isoDates.length; i < l; i++) { if (isoDates[i][1].exec(match[1])) { dateFormat = isoDates[i][0]; allowTime = isoDates[i][2] !== false; break; } } if (dateFormat == null) { config._isValid = false; return; } if (match[3]) { for (i = 0, l = isoTimes.length; i < l; i++) { if (isoTimes[i][1].exec(match[3])) { // match[2] should be 'T' or space timeFormat = (match[2] || ' ') + isoTimes[i][0]; break; } } if (timeFormat == null) { config._isValid = false; return; } } if (!allowTime && timeFormat != null) { config._isValid = false; return; } if (match[4]) { if (tzRegex.exec(match[4])) { tzFormat = 'Z'; } else { config._isValid = false; return; } } config._f = dateFormat + (timeFormat || '') + (tzFormat || ''); configFromStringAndFormat(config); } else { config._isValid = false; } } // date from iso format or fallback function configFromString(config) { var matched = aspNetJsonRegex.exec(config._i); if (matched !== null) { config._d = new Date(+matched[1]); return; } configFromISO(config); if (config._isValid === false) { delete config._isValid; hooks.createFromInputFallback(config); } } hooks.createFromInputFallback = deprecate( 'value provided is not in a recognized ISO format. moment construction falls back to js Date(), ' + 'which is not reliable across all browsers and versions. Non ISO date formats are ' + 'discouraged and will be removed in an upcoming major release. Please refer to ' + 'http://momentjs.com/guides/#/warnings/js-date/ for more info.', function (config) { config._d = new Date(config._i + (config._useUTC ? ' UTC' : '')); } ); // Pick the first defined of two or three arguments. function defaults(a, b, c) { if (a != null) { return a; } if (b != null) { return b; } return c; } function currentDateArray(config) { // hooks is actually the exported moment object var nowValue = new Date(hooks.now()); if (config._useUTC) { return [nowValue.getUTCFullYear(), nowValue.getUTCMonth(), nowValue.getUTCDate()]; } return [nowValue.getFullYear(), nowValue.getMonth(), nowValue.getDate()]; } // convert an array to a date. // the array should mirror the parameters below // note: all values past the year are optional and will default to the lowest possible value. // [year, month, day , hour, minute, second, millisecond] function configFromArray (config) { var i, date, input = [], currentDate, yearToUse; if (config._d) { return; } currentDate = currentDateArray(config); //compute day of the year from weeks and weekdays if (config._w && config._a[DATE] == null && config._a[MONTH] == null) { dayOfYearFromWeekInfo(config); } //if the day of the year is set, figure out what it is if (config._dayOfYear) { yearToUse = defaults(config._a[YEAR], currentDate[YEAR]); if (config._dayOfYear > daysInYear(yearToUse)) { getParsingFlags(config)._overflowDayOfYear = true; } date = createUTCDate(yearToUse, 0, config._dayOfYear); config._a[MONTH] = date.getUTCMonth(); config._a[DATE] = date.getUTCDate(); } // Default to current date. // * if no year, month, day of month are given, default to today // * if day of month is given, default month and year // * if month is given, default only year // * if year is given, don't default anything for (i = 0; i < 3 && config._a[i] == null; ++i) { config._a[i] = input[i] = currentDate[i]; } // Zero out whatever was not defaulted, including time for (; i < 7; i++) { config._a[i] = input[i] = (config._a[i] == null) ? (i === 2 ? 1 : 0) : config._a[i]; } // Check for 24:00:00.000 if (config._a[HOUR] === 24 && config._a[MINUTE] === 0 && config._a[SECOND] === 0 && config._a[MILLISECOND] === 0) { config._nextDay = true; config._a[HOUR] = 0; } config._d = (config._useUTC ? createUTCDate : createDate).apply(null, input); // Apply timezone offset from input. The actual utcOffset can be changed // with parseZone. if (config._tzm != null) { config._d.setUTCMinutes(config._d.getUTCMinutes() - config._tzm); } if (config._nextDay) { config._a[HOUR] = 24; } } function dayOfYearFromWeekInfo(config) { var w, weekYear, week, weekday, dow, doy, temp, weekdayOverflow; w = config._w; if (w.GG != null || w.W != null || w.E != null) { dow = 1; doy = 4; // TODO: We need to take the current isoWeekYear, but that depends on // how we interpret now (local, utc, fixed offset). So create // a now version of current config (take local/utc/offset flags, and // create now). weekYear = defaults(w.GG, config._a[YEAR], weekOfYear(createLocal(), 1, 4).year); week = defaults(w.W, 1); weekday = defaults(w.E, 1); if (weekday < 1 || weekday > 7) { weekdayOverflow = true; } } else { dow = config._locale._week.dow; doy = config._locale._week.doy; var curWeek = weekOfYear(createLocal(), dow, doy); weekYear = defaults(w.gg, config._a[YEAR], curWeek.year); // Default to current week. week = defaults(w.w, curWeek.week); if (w.d != null) { // weekday -- low day numbers are considered next week weekday = w.d; if (weekday < 0 || weekday > 6) { weekdayOverflow = true; } } else if (w.e != null) { // local weekday -- counting starts from begining of week weekday = w.e + dow; if (w.e < 0 || w.e > 6) { weekdayOverflow = true; } } else { // default to begining of week weekday = dow; } } if (week < 1 || week > weeksInYear(weekYear, dow, doy)) { getParsingFlags(config)._overflowWeeks = true; } else if (weekdayOverflow != null) { getParsingFlags(config)._overflowWeekday = true; } else { temp = dayOfYearFromWeeks(weekYear, week, weekday, dow, doy); config._a[YEAR] = temp.year; config._dayOfYear = temp.dayOfYear; } } // constant that refers to the ISO standard hooks.ISO_8601 = function () {}; // date from string and format string function configFromStringAndFormat(config) { // TODO: Move this to another part of the creation flow to prevent circular deps if (config._f === hooks.ISO_8601) { configFromISO(config); return; } config._a = []; getParsingFlags(config).empty = true; // This array is used to make a Date, either with `new Date` or `Date.UTC` var string = '' + config._i, i, parsedInput, tokens, token, skipped, stringLength = string.length, totalParsedInputLength = 0; tokens = expandFormat(config._f, config._locale).match(formattingTokens) || []; for (i = 0; i < tokens.length; i++) { token = tokens[i]; parsedInput = (string.match(getParseRegexForToken(token, config)) || [])[0]; // console.log('token', token, 'parsedInput', parsedInput, // 'regex', getParseRegexForToken(token, config)); if (parsedInput) { skipped = string.substr(0, string.indexOf(parsedInput)); if (skipped.length > 0) { getParsingFlags(config).unusedInput.push(skipped); } string = string.slice(string.indexOf(parsedInput) + parsedInput.length); totalParsedInputLength += parsedInput.length; } // don't parse if it's not a known token if (formatTokenFunctions[token]) { if (parsedInput) { getParsingFlags(config).empty = false; } else { getParsingFlags(config).unusedTokens.push(token); } addTimeToArrayFromToken(token, parsedInput, config); } else if (config._strict && !parsedInput) { getParsingFlags(config).unusedTokens.push(token); } } // add remaining unparsed input length to the string getParsingFlags(config).charsLeftOver = stringLength - totalParsedInputLength; if (string.length > 0) { getParsingFlags(config).unusedInput.push(string); } // clear _12h flag if hour is <= 12 if (config._a[HOUR] <= 12 && getParsingFlags(config).bigHour === true && config._a[HOUR] > 0) { getParsingFlags(config).bigHour = undefined; } getParsingFlags(config).parsedDateParts = config._a.slice(0); getParsingFlags(config).meridiem = config._meridiem; // handle meridiem config._a[HOUR] = meridiemFixWrap(config._locale, config._a[HOUR], config._meridiem); configFromArray(config); checkOverflow(config); } function meridiemFixWrap (locale, hour, meridiem) { var isPm; if (meridiem == null) { // nothing to do return hour; } if (locale.meridiemHour != null) { return locale.meridiemHour(hour, meridiem); } else if (locale.isPM != null) { // Fallback isPm = locale.isPM(meridiem); if (isPm && hour < 12) { hour += 12; } if (!isPm && hour === 12) { hour = 0; } return hour; } else { // this is not supposed to happen return hour; } } // date from string and array of format strings function configFromStringAndArray(config) { var tempConfig, bestMoment, scoreToBeat, i, currentScore; if (config._f.length === 0) { getParsingFlags(config).invalidFormat = true; config._d = new Date(NaN); return; } for (i = 0; i < config._f.length; i++) { currentScore = 0; tempConfig = copyConfig({}, config); if (config._useUTC != null) { tempConfig._useUTC = config._useUTC; } tempConfig._f = config._f[i]; configFromStringAndFormat(tempConfig); if (!isValid(tempConfig)) { continue; } // if there is any input that was not parsed add a penalty for that format currentScore += getParsingFlags(tempConfig).charsLeftOver; //or tokens currentScore += getParsingFlags(tempConfig).unusedTokens.length * 10; getParsingFlags(tempConfig).score = currentScore; if (scoreToBeat == null || currentScore < scoreToBeat) { scoreToBeat = currentScore; bestMoment = tempConfig; } } extend(config, bestMoment || tempConfig); } function configFromObject(config) { if (config._d) { return; } var i = normalizeObjectUnits(config._i); config._a = map([i.year, i.month, i.day || i.date, i.hour, i.minute, i.second, i.millisecond], function (obj) { return obj && parseInt(obj, 10); }); configFromArray(config); } function createFromConfig (config) { var res = new Moment(checkOverflow(prepareConfig(config))); if (res._nextDay) { // Adding is smart enough around DST res.add(1, 'd'); res._nextDay = undefined; } return res; } function prepareConfig (config) { var input = config._i, format = config._f; config._locale = config._locale || getLocale(config._l); if (input === null || (format === undefined && input === '')) { return createInvalid({nullInput: true}); } if (typeof input === 'string') { config._i = input = config._locale.preparse(input); } if (isMoment(input)) { return new Moment(checkOverflow(input)); } else if (isDate(input)) { config._d = input; } else if (isArray(format)) { configFromStringAndArray(config); } else if (format) { configFromStringAndFormat(config); } else { configFromInput(config); } if (!isValid(config)) { config._d = null; } return config; } function configFromInput(config) { var input = config._i; if (input === undefined) { config._d = new Date(hooks.now()); } else if (isDate(input)) { config._d = new Date(input.valueOf()); } else if (typeof input === 'string') { configFromString(config); } else if (isArray(input)) { config._a = map(input.slice(0), function (obj) { return parseInt(obj, 10); }); configFromArray(config); } else if (typeof(input) === 'object') { configFromObject(config); } else if (isNumber(input)) { // from milliseconds config._d = new Date(input); } else { hooks.createFromInputFallback(config); } } function createLocalOrUTC (input, format, locale, strict, isUTC) { var c = {}; if (locale === true || locale === false) { strict = locale; locale = undefined; } if ((isObject(input) && isObjectEmpty(input)) || (isArray(input) && input.length === 0)) { input = undefined; } // object construction must be done this way. // https://github.com/moment/moment/issues/1423 c._isAMomentObject = true; c._useUTC = c._isUTC = isUTC; c._l = locale; c._i = input; c._f = format; c._strict = strict; return createFromConfig(c); } function createLocal (input, format, locale, strict) { return createLocalOrUTC(input, format, locale, strict, false); } var prototypeMin = deprecate( 'moment().min is deprecated, use moment.max instead. http://momentjs.com/guides/#/warnings/min-max/', function () { var other = createLocal.apply(null, arguments); if (this.isValid() && other.isValid()) { return other < this ? this : other; } else { return createInvalid(); } } ); var prototypeMax = deprecate( 'moment().max is deprecated, use moment.min instead. http://momentjs.com/guides/#/warnings/min-max/', function () { var other = createLocal.apply(null, arguments); if (this.isValid() && other.isValid()) { return other > this ? this : other; } else { return createInvalid(); } } ); // Pick a moment m from moments so that m[fn](other) is true for all // other. This relies on the function fn to be transitive. // // moments should either be an array of moment objects or an array, whose // first element is an array of moment objects. function pickBy(fn, moments) { var res, i; if (moments.length === 1 && isArray(moments[0])) { moments = moments[0]; } if (!moments.length) { return createLocal(); } res = moments[0]; for (i = 1; i < moments.length; ++i) { if (!moments[i].isValid() || moments[i][fn](res)) { res = moments[i]; } } return res; } // TODO: Use [].sort instead? function min () { var args = [].slice.call(arguments, 0); return pickBy('isBefore', args); } function max () { var args = [].slice.call(arguments, 0); return pickBy('isAfter', args); } var now = function () { return Date.now ? Date.now() : +(new Date()); }; function Duration (duration) { var normalizedInput = normalizeObjectUnits(duration), years = normalizedInput.year || 0, quarters = normalizedInput.quarter || 0, months = normalizedInput.month || 0, weeks = normalizedInput.week || 0, days = normalizedInput.day || 0, hours = normalizedInput.hour || 0, minutes = normalizedInput.minute || 0, seconds = normalizedInput.second || 0, milliseconds = normalizedInput.millisecond || 0; // representation for dateAddRemove this._milliseconds = +milliseconds + seconds * 1e3 + // 1000 minutes * 6e4 + // 1000 * 60 hours * 1000 * 60 * 60; //using 1000 * 60 * 60 instead of 36e5 to avoid floating point rounding errors https://github.com/moment/moment/issues/2978 // Because of dateAddRemove treats 24 hours as different from a // day when working around DST, we need to store them separately this._days = +days + weeks * 7; // It is impossible translate months into days without knowing // which months you are are talking about, so we have to store // it separately. this._months = +months + quarters * 3 + years * 12; this._data = {}; this._locale = getLocale(); this._bubble(); } function isDuration (obj) { return obj instanceof Duration; } function absRound (number) { if (number < 0) { return Math.round(-1 * number) * -1; } else { return Math.round(number); } } // FORMATTING function offset (token, separator) { addFormatToken(token, 0, 0, function () { var offset = this.utcOffset(); var sign = '+'; if (offset < 0) { offset = -offset; sign = '-'; } return sign + zeroFill(~~(offset / 60), 2) + separator + zeroFill(~~(offset) % 60, 2); }); } offset('Z', ':'); offset('ZZ', ''); // PARSING addRegexToken('Z', matchShortOffset); addRegexToken('ZZ', matchShortOffset); addParseToken(['Z', 'ZZ'], function (input, array, config) { config._useUTC = true; config._tzm = offsetFromString(matchShortOffset, input); }); // HELPERS // timezone chunker // '+10:00' > ['10', '00'] // '-1530' > ['-15', '30'] var chunkOffset = /([\+\-]|\d\d)/gi; function offsetFromString(matcher, string) { var matches = (string || '').match(matcher); if (matches === null) { return null; } var chunk = matches[matches.length - 1] || []; var parts = (chunk + '').match(chunkOffset) || ['-', 0, 0]; var minutes = +(parts[1] * 60) + toInt(parts[2]); return minutes === 0 ? 0 : parts[0] === '+' ? minutes : -minutes; } // Return a moment from input, that is local/utc/zone equivalent to model. function cloneWithOffset(input, model) { var res, diff; if (model._isUTC) { res = model.clone(); diff = (isMoment(input) || isDate(input) ? input.valueOf() : createLocal(input).valueOf()) - res.valueOf(); // Use low-level api, because this fn is low-level api. res._d.setTime(res._d.valueOf() + diff); hooks.updateOffset(res, false); return res; } else { return createLocal(input).local(); } } function getDateOffset (m) { // On Firefox.24 Date#getTimezoneOffset returns a floating point. // https://github.com/moment/moment/pull/1871 return -Math.round(m._d.getTimezoneOffset() / 15) * 15; } // HOOKS // This function will be called whenever a moment is mutated. // It is intended to keep the offset in sync with the timezone. hooks.updateOffset = function () {}; // MOMENTS // keepLocalTime = true means only change the timezone, without // affecting the local hour. So 5:31:26 +0300 --[utcOffset(2, true)]--> // 5:31:26 +0200 It is possible that 5:31:26 doesn't exist with offset // +0200, so we adjust the time as needed, to be valid. // // Keeping the time actually adds/subtracts (one hour) // from the actual represented time. That is why we call updateOffset // a second time. In case it wants us to change the offset again // _changeInProgress == true case, then we have to adjust, because // there is no such time in the given timezone. function getSetOffset (input, keepLocalTime) { var offset = this._offset || 0, localAdjust; if (!this.isValid()) { return input != null ? this : NaN; } if (input != null) { if (typeof input === 'string') { input = offsetFromString(matchShortOffset, input); if (input === null) { return this; } } else if (Math.abs(input) < 16) { input = input * 60; } if (!this._isUTC && keepLocalTime) { localAdjust = getDateOffset(this); } this._offset = input; this._isUTC = true; if (localAdjust != null) { this.add(localAdjust, 'm'); } if (offset !== input) { if (!keepLocalTime || this._changeInProgress) { addSubtract(this, createDuration(input - offset, 'm'), 1, false); } else if (!this._changeInProgress) { this._changeInProgress = true; hooks.updateOffset(this, true); this._changeInProgress = null; } } return this; } else { return this._isUTC ? offset : getDateOffset(this); } } function getSetZone (input, keepLocalTime) { if (input != null) { if (typeof input !== 'string') { input = -input; } this.utcOffset(input, keepLocalTime); return this; } else { return -this.utcOffset(); } } function setOffsetToUTC (keepLocalTime) { return this.utcOffset(0, keepLocalTime); } function setOffsetToLocal (keepLocalTime) { if (this._isUTC) { this.utcOffset(0, keepLocalTime); this._isUTC = false; if (keepLocalTime) { this.subtract(getDateOffset(this), 'm'); } } return this; } function setOffsetToParsedOffset () { if (this._tzm != null) { this.utcOffset(this._tzm); } else if (typeof this._i === 'string') { var tZone = offsetFromString(matchOffset, this._i); if (tZone != null) { this.utcOffset(tZone); } else { this.utcOffset(0, true); } } return this; } function hasAlignedHourOffset (input) { if (!this.isValid()) { return false; } input = input ? createLocal(input).utcOffset() : 0; return (this.utcOffset() - input) % 60 === 0; } function isDaylightSavingTime () { return ( this.utcOffset() > this.clone().month(0).utcOffset() || this.utcOffset() > this.clone().month(5).utcOffset() ); } function isDaylightSavingTimeShifted () { if (!isUndefined(this._isDSTShifted)) { return this._isDSTShifted; } var c = {}; copyConfig(c, this); c = prepareConfig(c); if (c._a) { var other = c._isUTC ? createUTC(c._a) : createLocal(c._a); this._isDSTShifted = this.isValid() && compareArrays(c._a, other.toArray()) > 0; } else { this._isDSTShifted = false; } return this._isDSTShifted; } function isLocal () { return this.isValid() ? !this._isUTC : false; } function isUtcOffset () { return this.isValid() ? this._isUTC : false; } function isUtc () { return this.isValid() ? this._isUTC && this._offset === 0 : false; } // ASP.NET json date format regex var aspNetRegex = /^(\-)?(?:(\d*)[. ])?(\d+)\:(\d+)(?:\:(\d+)(\.\d*)?)?$/; // from http://docs.closure-library.googlecode.com/git/closure_goog_date_date.js.source.html // somewhat more in line with 4.4.3.2 2004 spec, but allows decimal anywhere // and further modified to allow for strings containing both week and day var isoRegex = /^(-)?P(?:(-?[0-9,.]*)Y)?(?:(-?[0-9,.]*)M)?(?:(-?[0-9,.]*)W)?(?:(-?[0-9,.]*)D)?(?:T(?:(-?[0-9,.]*)H)?(?:(-?[0-9,.]*)M)?(?:(-?[0-9,.]*)S)?)?$/; function createDuration (input, key) { var duration = input, // matching against regexp is expensive, do it on demand match = null, sign, ret, diffRes; if (isDuration(input)) { duration = { ms : input._milliseconds, d : input._days, M : input._months }; } else if (isNumber(input)) { duration = {}; if (key) { duration[key] = input; } else { duration.milliseconds = input; } } else if (!!(match = aspNetRegex.exec(input))) { sign = (match[1] === '-') ? -1 : 1; duration = { y : 0, d : toInt(match[DATE]) * sign, h : toInt(match[HOUR]) * sign, m : toInt(match[MINUTE]) * sign, s : toInt(match[SECOND]) * sign, ms : toInt(absRound(match[MILLISECOND] * 1000)) * sign // the millisecond decimal point is included in the match }; } else if (!!(match = isoRegex.exec(input))) { sign = (match[1] === '-') ? -1 : 1; duration = { y : parseIso(match[2], sign), M : parseIso(match[3], sign), w : parseIso(match[4], sign), d : parseIso(match[5], sign), h : parseIso(match[6], sign), m : parseIso(match[7], sign), s : parseIso(match[8], sign) }; } else if (duration == null) {// checks for null or undefined duration = {}; } else if (typeof duration === 'object' && ('from' in duration || 'to' in duration)) { diffRes = momentsDifference(createLocal(duration.from), createLocal(duration.to)); duration = {}; duration.ms = diffRes.milliseconds; duration.M = diffRes.months; } ret = new Duration(duration); if (isDuration(input) && hasOwnProp(input, '_locale')) { ret._locale = input._locale; } return ret; } createDuration.fn = Duration.prototype; function parseIso (inp, sign) { // We'd normally use ~~inp for this, but unfortunately it also // converts floats to ints. // inp may be undefined, so careful calling replace on it. var res = inp && parseFloat(inp.replace(',', '.')); // apply sign while we're at it return (isNaN(res) ? 0 : res) * sign; } function positiveMomentsDifference(base, other) { var res = {milliseconds: 0, months: 0}; res.months = other.month() - base.month() + (other.year() - base.year()) * 12; if (base.clone().add(res.months, 'M').isAfter(other)) { --res.months; } res.milliseconds = +other - +(base.clone().add(res.months, 'M')); return res; } function momentsDifference(base, other) { var res; if (!(base.isValid() && other.isValid())) { return {milliseconds: 0, months: 0}; } other = cloneWithOffset(other, base); if (base.isBefore(other)) { res = positiveMomentsDifference(base, other); } else { res = positiveMomentsDifference(other, base); res.milliseconds = -res.milliseconds; res.months = -res.months; } return res; } // TODO: remove 'name' arg after deprecation is removed function createAdder(direction, name) { return function (val, period) { var dur, tmp; //invert the arguments, but complain about it if (period !== null && !isNaN(+period)) { deprecateSimple(name, 'moment().' + name + '(period, number) is deprecated. Please use moment().' + name + '(number, period). ' + 'See http://momentjs.com/guides/#/warnings/add-inverted-param/ for more info.'); tmp = val; val = period; period = tmp; } val = typeof val === 'string' ? +val : val; dur = createDuration(val, period); addSubtract(this, dur, direction); return this; }; } function addSubtract (mom, duration, isAdding, updateOffset) { var milliseconds = duration._milliseconds, days = absRound(duration._days), months = absRound(duration._months); if (!mom.isValid()) { // No op return; } updateOffset = updateOffset == null ? true : updateOffset; if (milliseconds) { mom._d.setTime(mom._d.valueOf() + milliseconds * isAdding); } if (days) { set$1(mom, 'Date', get(mom, 'Date') + days * isAdding); } if (months) { setMonth(mom, get(mom, 'Month') + months * isAdding); } if (updateOffset) { hooks.updateOffset(mom, days || months); } } var add = createAdder(1, 'add'); var subtract = createAdder(-1, 'subtract'); function getCalendarFormat(myMoment, now) { var diff = myMoment.diff(now, 'days', true); return diff < -6 ? 'sameElse' : diff < -1 ? 'lastWeek' : diff < 0 ? 'lastDay' : diff < 1 ? 'sameDay' : diff < 2 ? 'nextDay' : diff < 7 ? 'nextWeek' : 'sameElse'; } function calendar$1 (time, formats) { // We want to compare the start of today, vs this. // Getting start-of-today depends on whether we're local/utc/offset or not. var now = time || createLocal(), sod = cloneWithOffset(now, this).startOf('day'), format = hooks.calendarFormat(this, sod) || 'sameElse'; var output = formats && (isFunction(formats[format]) ? formats[format].call(this, now) : formats[format]); return this.format(output || this.localeData().calendar(format, this, createLocal(now))); } function clone () { return new Moment(this); } function isAfter (input, units) { var localInput = isMoment(input) ? input : createLocal(input); if (!(this.isValid() && localInput.isValid())) { return false; } units = normalizeUnits(!isUndefined(units) ? units : 'millisecond'); if (units === 'millisecond') { return this.valueOf() > localInput.valueOf(); } else { return localInput.valueOf() < this.clone().startOf(units).valueOf(); } } function isBefore (input, units) { var localInput = isMoment(input) ? input : createLocal(input); if (!(this.isValid() && localInput.isValid())) { return false; } units = normalizeUnits(!isUndefined(units) ? units : 'millisecond'); if (units === 'millisecond') { return this.valueOf() < localInput.valueOf(); } else { return this.clone().endOf(units).valueOf() < localInput.valueOf(); } } function isBetween (from, to, units, inclusivity) { inclusivity = inclusivity || '()'; return (inclusivity[0] === '(' ? this.isAfter(from, units) : !this.isBefore(from, units)) && (inclusivity[1] === ')' ? this.isBefore(to, units) : !this.isAfter(to, units)); } function isSame (input, units) { var localInput = isMoment(input) ? input : createLocal(input), inputMs; if (!(this.isValid() && localInput.isValid())) { return false; } units = normalizeUnits(units || 'millisecond'); if (units === 'millisecond') { return this.valueOf() === localInput.valueOf(); } else { inputMs = localInput.valueOf(); return this.clone().startOf(units).valueOf() <= inputMs && inputMs <= this.clone().endOf(units).valueOf(); } } function isSameOrAfter (input, units) { return this.isSame(input, units) || this.isAfter(input,units); } function isSameOrBefore (input, units) { return this.isSame(input, units) || this.isBefore(input,units); } function diff (input, units, asFloat) { var that, zoneDelta, delta, output; if (!this.isValid()) { return NaN; } that = cloneWithOffset(input, this); if (!that.isValid()) { return NaN; } zoneDelta = (that.utcOffset() - this.utcOffset()) * 6e4; units = normalizeUnits(units); if (units === 'year' || units === 'month' || units === 'quarter') { output = monthDiff(this, that); if (units === 'quarter') { output = output / 3; } else if (units === 'year') { output = output / 12; } } else { delta = this - that; output = units === 'second' ? delta / 1e3 : // 1000 units === 'minute' ? delta / 6e4 : // 1000 * 60 units === 'hour' ? delta / 36e5 : // 1000 * 60 * 60 units === 'day' ? (delta - zoneDelta) / 864e5 : // 1000 * 60 * 60 * 24, negate dst units === 'week' ? (delta - zoneDelta) / 6048e5 : // 1000 * 60 * 60 * 24 * 7, negate dst delta; } return asFloat ? output : absFloor(output); } function monthDiff (a, b) { // difference in months var wholeMonthDiff = ((b.year() - a.year()) * 12) + (b.month() - a.month()), // b is in (anchor - 1 month, anchor + 1 month) anchor = a.clone().add(wholeMonthDiff, 'months'), anchor2, adjust; if (b - anchor < 0) { anchor2 = a.clone().add(wholeMonthDiff - 1, 'months'); // linear across the month adjust = (b - anchor) / (anchor - anchor2); } else { anchor2 = a.clone().add(wholeMonthDiff + 1, 'months'); // linear across the month adjust = (b - anchor) / (anchor2 - anchor); } //check for negative zero, return zero if negative zero return -(wholeMonthDiff + adjust) || 0; } hooks.defaultFormat = 'YYYY-MM-DDTHH:mm:ssZ'; hooks.defaultFormatUtc = 'YYYY-MM-DDTHH:mm:ss[Z]'; function toString () { return this.clone().locale('en').format('ddd MMM DD YYYY HH:mm:ss [GMT]ZZ'); } function toISOString () { var m = this.clone().utc(); if (0 < m.year() && m.year() <= 9999) { if (isFunction(Date.prototype.toISOString)) { // native implementation is ~50x faster, use it when we can return this.toDate().toISOString(); } else { return formatMoment(m, 'YYYY-MM-DD[T]HH:mm:ss.SSS[Z]'); } } else { return formatMoment(m, 'YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]'); } } /** * Return a human readable representation of a moment that can * also be evaluated to get a new moment which is the same * * @link https://nodejs.org/dist/latest/docs/api/util.html#util_custom_inspect_function_on_objects */ function inspect () { if (!this.isValid()) { return 'moment.invalid(/* ' + this._i + ' */)'; } var func = 'moment'; var zone = ''; if (!this.isLocal()) { func = this.utcOffset() === 0 ? 'moment.utc' : 'moment.parseZone'; zone = 'Z'; } var prefix = '[' + func + '("]'; var year = (0 < this.year() && this.year() <= 9999) ? 'YYYY' : 'YYYYYY'; var datetime = '-MM-DD[T]HH:mm:ss.SSS'; var suffix = zone + '[")]'; return this.format(prefix + year + datetime + suffix); } function format (inputString) { if (!inputString) { inputString = this.isUtc() ? hooks.defaultFormatUtc : hooks.defaultFormat; } var output = formatMoment(this, inputString); return this.localeData().postformat(output); } function from (time, withoutSuffix) { if (this.isValid() && ((isMoment(time) && time.isValid()) || createLocal(time).isValid())) { return createDuration({to: this, from: time}).locale(this.locale()).humanize(!withoutSuffix); } else { return this.localeData().invalidDate(); } } function fromNow (withoutSuffix) { return this.from(createLocal(), withoutSuffix); } function to (time, withoutSuffix) { if (this.isValid() && ((isMoment(time) && time.isValid()) || createLocal(time).isValid())) { return createDuration({from: this, to: time}).locale(this.locale()).humanize(!withoutSuffix); } else { return this.localeData().invalidDate(); } } function toNow (withoutSuffix) { return this.to(createLocal(), withoutSuffix); } // If passed a locale key, it will set the locale for this // instance. Otherwise, it will return the locale configuration // variables for this instance. function locale (key) { var newLocaleData; if (key === undefined) { return this._locale._abbr; } else { newLocaleData = getLocale(key); if (newLocaleData != null) { this._locale = newLocaleData; } return this; } } var lang = deprecate( 'moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.', function (key) { if (key === undefined) { return this.localeData(); } else { return this.locale(key); } } ); function localeData () { return this._locale; } function startOf (units) { units = normalizeUnits(units); // the following switch intentionally omits break keywords // to utilize falling through the cases. switch (units) { case 'year': this.month(0); /* falls through */ case 'quarter': case 'month': this.date(1); /* falls through */ case 'week': case 'isoWeek': case 'day': case 'date': this.hours(0); /* falls through */ case 'hour': this.minutes(0); /* falls through */ case 'minute': this.seconds(0); /* falls through */ case 'second': this.milliseconds(0); } // weeks are a special case if (units === 'week') { this.weekday(0); } if (units === 'isoWeek') { this.isoWeekday(1); } // quarters are also special if (units === 'quarter') { this.month(Math.floor(this.month() / 3) * 3); } return this; } function endOf (units) { units = normalizeUnits(units); if (units === undefined || units === 'millisecond') { return this; } // 'date' is an alias for 'day', so it should be considered as such. if (units === 'date') { units = 'day'; } return this.startOf(units).add(1, (units === 'isoWeek' ? 'week' : units)).subtract(1, 'ms'); } function valueOf () { return this._d.valueOf() - ((this._offset || 0) * 60000); } function unix () { return Math.floor(this.valueOf() / 1000); } function toDate () { return new Date(this.valueOf()); } function toArray () { var m = this; return [m.year(), m.month(), m.date(), m.hour(), m.minute(), m.second(), m.millisecond()]; } function toObject () { var m = this; return { years: m.year(), months: m.month(), date: m.date(), hours: m.hours(), minutes: m.minutes(), seconds: m.seconds(), milliseconds: m.milliseconds() }; } function toJSON () { // new Date(NaN).toJSON() === null return this.isValid() ? this.toISOString() : null; } function isValid$1 () { return isValid(this); } function parsingFlags () { return extend({}, getParsingFlags(this)); } function invalidAt () { return getParsingFlags(this).overflow; } function creationData() { return { input: this._i, format: this._f, locale: this._locale, isUTC: this._isUTC, strict: this._strict }; } // FORMATTING addFormatToken(0, ['gg', 2], 0, function () { return this.weekYear() % 100; }); addFormatToken(0, ['GG', 2], 0, function () { return this.isoWeekYear() % 100; }); function addWeekYearFormatToken (token, getter) { addFormatToken(0, [token, token.length], 0, getter); } addWeekYearFormatToken('gggg', 'weekYear'); addWeekYearFormatToken('ggggg', 'weekYear'); addWeekYearFormatToken('GGGG', 'isoWeekYear'); addWeekYearFormatToken('GGGGG', 'isoWeekYear'); // ALIASES addUnitAlias('weekYear', 'gg'); addUnitAlias('isoWeekYear', 'GG'); // PRIORITY addUnitPriority('weekYear', 1); addUnitPriority('isoWeekYear', 1); // PARSING addRegexToken('G', matchSigned); addRegexToken('g', matchSigned); addRegexToken('GG', match1to2, match2); addRegexToken('gg', match1to2, match2); addRegexToken('GGGG', match1to4, match4); addRegexToken('gggg', match1to4, match4); addRegexToken('GGGGG', match1to6, match6); addRegexToken('ggggg', match1to6, match6); addWeekParseToken(['gggg', 'ggggg', 'GGGG', 'GGGGG'], function (input, week, config, token) { week[token.substr(0, 2)] = toInt(input); }); addWeekParseToken(['gg', 'GG'], function (input, week, config, token) { week[token] = hooks.parseTwoDigitYear(input); }); // MOMENTS function getSetWeekYear (input) { return getSetWeekYearHelper.call(this, input, this.week(), this.weekday(), this.localeData()._week.dow, this.localeData()._week.doy); } function getSetISOWeekYear (input) { return getSetWeekYearHelper.call(this, input, this.isoWeek(), this.isoWeekday(), 1, 4); } function getISOWeeksInYear () { return weeksInYear(this.year(), 1, 4); } function getWeeksInYear () { var weekInfo = this.localeData()._week; return weeksInYear(this.year(), weekInfo.dow, weekInfo.doy); } function getSetWeekYearHelper(input, week, weekday, dow, doy) { var weeksTarget; if (input == null) { return weekOfYear(this, dow, doy).year; } else { weeksTarget = weeksInYear(input, dow, doy); if (week > weeksTarget) { week = weeksTarget; } return setWeekAll.call(this, input, week, weekday, dow, doy); } } function setWeekAll(weekYear, week, weekday, dow, doy) { var dayOfYearData = dayOfYearFromWeeks(weekYear, week, weekday, dow, doy), date = createUTCDate(dayOfYearData.year, 0, dayOfYearData.dayOfYear); this.year(date.getUTCFullYear()); this.month(date.getUTCMonth()); this.date(date.getUTCDate()); return this; } // FORMATTING addFormatToken('Q', 0, 'Qo', 'quarter'); // ALIASES addUnitAlias('quarter', 'Q'); // PRIORITY addUnitPriority('quarter', 7); // PARSING addRegexToken('Q', match1); addParseToken('Q', function (input, array) { array[MONTH] = (toInt(input) - 1) * 3; }); // MOMENTS function getSetQuarter (input) { return input == null ? Math.ceil((this.month() + 1) / 3) : this.month((input - 1) * 3 + this.month() % 3); } // FORMATTING addFormatToken('D', ['DD', 2], 'Do', 'date'); // ALIASES addUnitAlias('date', 'D'); // PRIOROITY addUnitPriority('date', 9); // PARSING addRegexToken('D', match1to2); addRegexToken('DD', match1to2, match2); addRegexToken('Do', function (isStrict, locale) { return isStrict ? locale._ordinalParse : locale._ordinalParseLenient; }); addParseToken(['D', 'DD'], DATE); addParseToken('Do', function (input, array) { array[DATE] = toInt(input.match(match1to2)[0], 10); }); // MOMENTS var getSetDayOfMonth = makeGetSet('Date', true); // FORMATTING addFormatToken('DDD', ['DDDD', 3], 'DDDo', 'dayOfYear'); // ALIASES addUnitAlias('dayOfYear', 'DDD'); // PRIORITY addUnitPriority('dayOfYear', 4); // PARSING addRegexToken('DDD', match1to3); addRegexToken('DDDD', match3); addParseToken(['DDD', 'DDDD'], function (input, array, config) { config._dayOfYear = toInt(input); }); // HELPERS // MOMENTS function getSetDayOfYear (input) { var dayOfYear = Math.round((this.clone().startOf('day') - this.clone().startOf('year')) / 864e5) + 1; return input == null ? dayOfYear : this.add((input - dayOfYear), 'd'); } // FORMATTING addFormatToken('m', ['mm', 2], 0, 'minute'); // ALIASES addUnitAlias('minute', 'm'); // PRIORITY addUnitPriority('minute', 14); // PARSING addRegexToken('m', match1to2); addRegexToken('mm', match1to2, match2); addParseToken(['m', 'mm'], MINUTE); // MOMENTS var getSetMinute = makeGetSet('Minutes', false); // FORMATTING addFormatToken('s', ['ss', 2], 0, 'second'); // ALIASES addUnitAlias('second', 's'); // PRIORITY addUnitPriority('second', 15); // PARSING addRegexToken('s', match1to2); addRegexToken('ss', match1to2, match2); addParseToken(['s', 'ss'], SECOND); // MOMENTS var getSetSecond = makeGetSet('Seconds', false); // FORMATTING addFormatToken('S', 0, 0, function () { return ~~(this.millisecond() / 100); }); addFormatToken(0, ['SS', 2], 0, function () { return ~~(this.millisecond() / 10); }); addFormatToken(0, ['SSS', 3], 0, 'millisecond'); addFormatToken(0, ['SSSS', 4], 0, function () { return this.millisecond() * 10; }); addFormatToken(0, ['SSSSS', 5], 0, function () { return this.millisecond() * 100; }); addFormatToken(0, ['SSSSSS', 6], 0, function () { return this.millisecond() * 1000; }); addFormatToken(0, ['SSSSSSS', 7], 0, function () { return this.millisecond() * 10000; }); addFormatToken(0, ['SSSSSSSS', 8], 0, function () { return this.millisecond() * 100000; }); addFormatToken(0, ['SSSSSSSSS', 9], 0, function () { return this.millisecond() * 1000000; }); // ALIASES addUnitAlias('millisecond', 'ms'); // PRIORITY addUnitPriority('millisecond', 16); // PARSING addRegexToken('S', match1to3, match1); addRegexToken('SS', match1to3, match2); addRegexToken('SSS', match1to3, match3); var token; for (token = 'SSSS'; token.length <= 9; token += 'S') { addRegexToken(token, matchUnsigned); } function parseMs(input, array) { array[MILLISECOND] = toInt(('0.' + input) * 1000); } for (token = 'S'; token.length <= 9; token += 'S') { addParseToken(token, parseMs); } // MOMENTS var getSetMillisecond = makeGetSet('Milliseconds', false); // FORMATTING addFormatToken('z', 0, 0, 'zoneAbbr'); addFormatToken('zz', 0, 0, 'zoneName'); // MOMENTS function getZoneAbbr () { return this._isUTC ? 'UTC' : ''; } function getZoneName () { return this._isUTC ? 'Coordinated Universal Time' : ''; } var proto = Moment.prototype; proto.add = add; proto.calendar = calendar$1; proto.clone = clone; proto.diff = diff; proto.endOf = endOf; proto.format = format; proto.from = from; proto.fromNow = fromNow; proto.to = to; proto.toNow = toNow; proto.get = stringGet; proto.invalidAt = invalidAt; proto.isAfter = isAfter; proto.isBefore = isBefore; proto.isBetween = isBetween; proto.isSame = isSame; proto.isSameOrAfter = isSameOrAfter; proto.isSameOrBefore = isSameOrBefore; proto.isValid = isValid$1; proto.lang = lang; proto.locale = locale; proto.localeData = localeData; proto.max = prototypeMax; proto.min = prototypeMin; proto.parsingFlags = parsingFlags; proto.set = stringSet; proto.startOf = startOf; proto.subtract = subtract; proto.toArray = toArray; proto.toObject = toObject; proto.toDate = toDate; proto.toISOString = toISOString; proto.inspect = inspect; proto.toJSON = toJSON; proto.toString = toString; proto.unix = unix; proto.valueOf = valueOf; proto.creationData = creationData; // Year proto.year = getSetYear; proto.isLeapYear = getIsLeapYear; // Week Year proto.weekYear = getSetWeekYear; proto.isoWeekYear = getSetISOWeekYear; // Quarter proto.quarter = proto.quarters = getSetQuarter; // Month proto.month = getSetMonth; proto.daysInMonth = getDaysInMonth; // Week proto.week = proto.weeks = getSetWeek; proto.isoWeek = proto.isoWeeks = getSetISOWeek; proto.weeksInYear = getWeeksInYear; proto.isoWeeksInYear = getISOWeeksInYear; // Day proto.date = getSetDayOfMonth; proto.day = proto.days = getSetDayOfWeek; proto.weekday = getSetLocaleDayOfWeek; proto.isoWeekday = getSetISODayOfWeek; proto.dayOfYear = getSetDayOfYear; // Hour proto.hour = proto.hours = getSetHour; // Minute proto.minute = proto.minutes = getSetMinute; // Second proto.second = proto.seconds = getSetSecond; // Millisecond proto.millisecond = proto.milliseconds = getSetMillisecond; // Offset proto.utcOffset = getSetOffset; proto.utc = setOffsetToUTC; proto.local = setOffsetToLocal; proto.parseZone = setOffsetToParsedOffset; proto.hasAlignedHourOffset = hasAlignedHourOffset; proto.isDST = isDaylightSavingTime; proto.isLocal = isLocal; proto.isUtcOffset = isUtcOffset; proto.isUtc = isUtc; proto.isUTC = isUtc; // Timezone proto.zoneAbbr = getZoneAbbr; proto.zoneName = getZoneName; // Deprecations proto.dates = deprecate('dates accessor is deprecated. Use date instead.', getSetDayOfMonth); proto.months = deprecate('months accessor is deprecated. Use month instead', getSetMonth); proto.years = deprecate('years accessor is deprecated. Use year instead', getSetYear); proto.zone = deprecate('moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/', getSetZone); proto.isDSTShifted = deprecate('isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information', isDaylightSavingTimeShifted); function createUnix (input) { return createLocal(input * 1000); } function createInZone () { return createLocal.apply(null, arguments).parseZone(); } function preParsePostFormat (string) { return string; } var proto$1 = Locale.prototype; proto$1.calendar = calendar; proto$1.longDateFormat = longDateFormat; proto$1.invalidDate = invalidDate; proto$1.ordinal = ordinal; proto$1.preparse = preParsePostFormat; proto$1.postformat = preParsePostFormat; proto$1.relativeTime = relativeTime; proto$1.pastFuture = pastFuture; proto$1.set = set; // Month proto$1.months = localeMonths; proto$1.monthsShort = localeMonthsShort; proto$1.monthsParse = localeMonthsParse; proto$1.monthsRegex = monthsRegex; proto$1.monthsShortRegex = monthsShortRegex; // Week proto$1.week = localeWeek; proto$1.firstDayOfYear = localeFirstDayOfYear; proto$1.firstDayOfWeek = localeFirstDayOfWeek; // Day of Week proto$1.weekdays = localeWeekdays; proto$1.weekdaysMin = localeWeekdaysMin; proto$1.weekdaysShort = localeWeekdaysShort; proto$1.weekdaysParse = localeWeekdaysParse; proto$1.weekdaysRegex = weekdaysRegex; proto$1.weekdaysShortRegex = weekdaysShortRegex; proto$1.weekdaysMinRegex = weekdaysMinRegex; // Hours proto$1.isPM = localeIsPM; proto$1.meridiem = localeMeridiem; function get$1 (format, index, field, setter) { var locale = getLocale(); var utc = createUTC().set(setter, index); return locale[field](utc, format); } function listMonthsImpl (format, index, field) { if (isNumber(format)) { index = format; format = undefined; } format = format || ''; if (index != null) { return get$1(format, index, field, 'month'); } var i; var out = []; for (i = 0; i < 12; i++) { out[i] = get$1(format, i, field, 'month'); } return out; } // () // (5) // (fmt, 5) // (fmt) // (true) // (true, 5) // (true, fmt, 5) // (true, fmt) function listWeekdaysImpl (localeSorted, format, index, field) { if (typeof localeSorted === 'boolean') { if (isNumber(format)) { index = format; format = undefined; } format = format || ''; } else { format = localeSorted; index = format; localeSorted = false; if (isNumber(format)) { index = format; format = undefined; } format = format || ''; } var locale = getLocale(), shift = localeSorted ? locale._week.dow : 0; if (index != null) { return get$1(format, (index + shift) % 7, field, 'day'); } var i; var out = []; for (i = 0; i < 7; i++) { out[i] = get$1(format, (i + shift) % 7, field, 'day'); } return out; } function listMonths (format, index) { return listMonthsImpl(format, index, 'months'); } function listMonthsShort (format, index) { return listMonthsImpl(format, index, 'monthsShort'); } function listWeekdays (localeSorted, format, index) { return listWeekdaysImpl(localeSorted, format, index, 'weekdays'); } function listWeekdaysShort (localeSorted, format, index) { return listWeekdaysImpl(localeSorted, format, index, 'weekdaysShort'); } function listWeekdaysMin (localeSorted, format, index) { return listWeekdaysImpl(localeSorted, format, index, 'weekdaysMin'); } getSetGlobalLocale('en', { ordinalParse: /\d{1,2}(th|st|nd|rd)/, ordinal : function (number) { var b = number % 10, output = (toInt(number % 100 / 10) === 1) ? 'th' : (b === 1) ? 'st' : (b === 2) ? 'nd' : (b === 3) ? 'rd' : 'th'; return number + output; } }); // Side effect imports hooks.lang = deprecate('moment.lang is deprecated. Use moment.locale instead.', getSetGlobalLocale); hooks.langData = deprecate('moment.langData is deprecated. Use moment.localeData instead.', getLocale); var mathAbs = Math.abs; function abs () { var data = this._data; this._milliseconds = mathAbs(this._milliseconds); this._days = mathAbs(this._days); this._months = mathAbs(this._months); data.milliseconds = mathAbs(data.milliseconds); data.seconds = mathAbs(data.seconds); data.minutes = mathAbs(data.minutes); data.hours = mathAbs(data.hours); data.months = mathAbs(data.months); data.years = mathAbs(data.years); return this; } function addSubtract$1 (duration, input, value, direction) { var other = createDuration(input, value); duration._milliseconds += direction * other._milliseconds; duration._days += direction * other._days; duration._months += direction * other._months; return duration._bubble(); } // supports only 2.0-style add(1, 's') or add(duration) function add$1 (input, value) { return addSubtract$1(this, input, value, 1); } // supports only 2.0-style subtract(1, 's') or subtract(duration) function subtract$1 (input, value) { return addSubtract$1(this, input, value, -1); } function absCeil (number) { if (number < 0) { return Math.floor(number); } else { return Math.ceil(number); } } function bubble () { var milliseconds = this._milliseconds; var days = this._days; var months = this._months; var data = this._data; var seconds, minutes, hours, years, monthsFromDays; // if we have a mix of positive and negative values, bubble down first // check: https://github.com/moment/moment/issues/2166 if (!((milliseconds >= 0 && days >= 0 && months >= 0) || (milliseconds <= 0 && days <= 0 && months <= 0))) { milliseconds += absCeil(monthsToDays(months) + days) * 864e5; days = 0; months = 0; } // The following code bubbles up values, see the tests for // examples of what that means. data.milliseconds = milliseconds % 1000; seconds = absFloor(milliseconds / 1000); data.seconds = seconds % 60; minutes = absFloor(seconds / 60); data.minutes = minutes % 60; hours = absFloor(minutes / 60); data.hours = hours % 24; days += absFloor(hours / 24); // convert days to months monthsFromDays = absFloor(daysToMonths(days)); months += monthsFromDays; days -= absCeil(monthsToDays(monthsFromDays)); // 12 months -> 1 year years = absFloor(months / 12); months %= 12; data.days = days; data.months = months; data.years = years; return this; } function daysToMonths (days) { // 400 years have 146097 days (taking into account leap year rules) // 400 years have 12 months === 4800 return days * 4800 / 146097; } function monthsToDays (months) { // the reverse of daysToMonths return months * 146097 / 4800; } function as (units) { var days; var months; var milliseconds = this._milliseconds; units = normalizeUnits(units); if (units === 'month' || units === 'year') { days = this._days + milliseconds / 864e5; months = this._months + daysToMonths(days); return units === 'month' ? months : months / 12; } else { // handle milliseconds separately because of floating point math errors (issue #1867) days = this._days + Math.round(monthsToDays(this._months)); switch (units) { case 'week' : return days / 7 + milliseconds / 6048e5; case 'day' : return days + milliseconds / 864e5; case 'hour' : return days * 24 + milliseconds / 36e5; case 'minute' : return days * 1440 + milliseconds / 6e4; case 'second' : return days * 86400 + milliseconds / 1000; // Math.floor prevents floating point math errors here case 'millisecond': return Math.floor(days * 864e5) + milliseconds; default: throw new Error('Unknown unit ' + units); } } } // TODO: Use this.as('ms')? function valueOf$1 () { return ( this._milliseconds + this._days * 864e5 + (this._months % 12) * 2592e6 + toInt(this._months / 12) * 31536e6 ); } function makeAs (alias) { return function () { return this.as(alias); }; } var asMilliseconds = makeAs('ms'); var asSeconds = makeAs('s'); var asMinutes = makeAs('m'); var asHours = makeAs('h'); var asDays = makeAs('d'); var asWeeks = makeAs('w'); var asMonths = makeAs('M'); var asYears = makeAs('y'); function get$2 (units) { units = normalizeUnits(units); return this[units + 's'](); } function makeGetter(name) { return function () { return this._data[name]; }; } var milliseconds = makeGetter('milliseconds'); var seconds = makeGetter('seconds'); var minutes = makeGetter('minutes'); var hours = makeGetter('hours'); var days = makeGetter('days'); var months = makeGetter('months'); var years = makeGetter('years'); function weeks () { return absFloor(this.days() / 7); } var round = Math.round; var thresholds = { s: 45, // seconds to minute m: 45, // minutes to hour h: 22, // hours to day d: 26, // days to month M: 11 // months to year }; // helper function for moment.fn.from, moment.fn.fromNow, and moment.duration.fn.humanize function substituteTimeAgo(string, number, withoutSuffix, isFuture, locale) { return locale.relativeTime(number || 1, !!withoutSuffix, string, isFuture); } function relativeTime$1 (posNegDuration, withoutSuffix, locale) { var duration = createDuration(posNegDuration).abs(); var seconds = round(duration.as('s')); var minutes = round(duration.as('m')); var hours = round(duration.as('h')); var days = round(duration.as('d')); var months = round(duration.as('M')); var years = round(duration.as('y')); var a = seconds < thresholds.s && ['s', seconds] || minutes <= 1 && ['m'] || minutes < thresholds.m && ['mm', minutes] || hours <= 1 && ['h'] || hours < thresholds.h && ['hh', hours] || days <= 1 && ['d'] || days < thresholds.d && ['dd', days] || months <= 1 && ['M'] || months < thresholds.M && ['MM', months] || years <= 1 && ['y'] || ['yy', years]; a[2] = withoutSuffix; a[3] = +posNegDuration > 0; a[4] = locale; return substituteTimeAgo.apply(null, a); } // This function allows you to set the rounding function for relative time strings function getSetRelativeTimeRounding (roundingFunction) { if (roundingFunction === undefined) { return round; } if (typeof(roundingFunction) === 'function') { round = roundingFunction; return true; } return false; } // This function allows you to set a threshold for relative time strings function getSetRelativeTimeThreshold (threshold, limit) { if (thresholds[threshold] === undefined) { return false; } if (limit === undefined) { return thresholds[threshold]; } thresholds[threshold] = limit; return true; } function humanize (withSuffix) { var locale = this.localeData(); var output = relativeTime$1(this, !withSuffix, locale); if (withSuffix) { output = locale.pastFuture(+this, output); } return locale.postformat(output); } var abs$1 = Math.abs; function toISOString$1() { // for ISO strings we do not use the normal bubbling rules: // * milliseconds bubble up until they become hours // * days do not bubble at all // * months bubble up until they become years // This is because there is no context-free conversion between hours and days // (think of clock changes) // and also not between days and months (28-31 days per month) var seconds = abs$1(this._milliseconds) / 1000; var days = abs$1(this._days); var months = abs$1(this._months); var minutes, hours, years; // 3600 seconds -> 60 minutes -> 1 hour minutes = absFloor(seconds / 60); hours = absFloor(minutes / 60); seconds %= 60; minutes %= 60; // 12 months -> 1 year years = absFloor(months / 12); months %= 12; // inspired by https://github.com/dordille/moment-isoduration/blob/master/moment.isoduration.js var Y = years; var M = months; var D = days; var h = hours; var m = minutes; var s = seconds; var total = this.asSeconds(); if (!total) { // this is the same as C#'s (Noda) and python (isodate)... // but not other JS (goog.date) return 'P0D'; } return (total < 0 ? '-' : '') + 'P' + (Y ? Y + 'Y' : '') + (M ? M + 'M' : '') + (D ? D + 'D' : '') + ((h || m || s) ? 'T' : '') + (h ? h + 'H' : '') + (m ? m + 'M' : '') + (s ? s + 'S' : ''); } var proto$2 = Duration.prototype; proto$2.abs = abs; proto$2.add = add$1; proto$2.subtract = subtract$1; proto$2.as = as; proto$2.asMilliseconds = asMilliseconds; proto$2.asSeconds = asSeconds; proto$2.asMinutes = asMinutes; proto$2.asHours = asHours; proto$2.asDays = asDays; proto$2.asWeeks = asWeeks; proto$2.asMonths = asMonths; proto$2.asYears = asYears; proto$2.valueOf = valueOf$1; proto$2._bubble = bubble; proto$2.get = get$2; proto$2.milliseconds = milliseconds; proto$2.seconds = seconds; proto$2.minutes = minutes; proto$2.hours = hours; proto$2.days = days; proto$2.weeks = weeks; proto$2.months = months; proto$2.years = years; proto$2.humanize = humanize; proto$2.toISOString = toISOString$1; proto$2.toString = toISOString$1; proto$2.toJSON = toISOString$1; proto$2.locale = locale; proto$2.localeData = localeData; // Deprecations proto$2.toIsoString = deprecate('toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)', toISOString$1); proto$2.lang = lang; // Side effect imports // FORMATTING addFormatToken('X', 0, 0, 'unix'); addFormatToken('x', 0, 0, 'valueOf'); // PARSING addRegexToken('x', matchSigned); addRegexToken('X', matchTimestamp); addParseToken('X', function (input, array, config) { config._d = new Date(parseFloat(input, 10) * 1000); }); addParseToken('x', function (input, array, config) { config._d = new Date(toInt(input)); }); // Side effect imports hooks.version = '2.17.1'; setHookCallback(createLocal); hooks.fn = proto; hooks.min = min; hooks.max = max; hooks.now = now; hooks.utc = createUTC; hooks.unix = createUnix; hooks.months = listMonths; hooks.isDate = isDate; hooks.locale = getSetGlobalLocale; hooks.invalid = createInvalid; hooks.duration = createDuration; hooks.isMoment = isMoment; hooks.weekdays = listWeekdays; hooks.parseZone = createInZone; hooks.localeData = getLocale; hooks.isDuration = isDuration; hooks.monthsShort = listMonthsShort; hooks.weekdaysMin = listWeekdaysMin; hooks.defineLocale = defineLocale; hooks.updateLocale = updateLocale; hooks.locales = listLocales; hooks.weekdaysShort = listWeekdaysShort; hooks.normalizeUnits = normalizeUnits; hooks.relativeTimeRounding = getSetRelativeTimeRounding; hooks.relativeTimeThreshold = getSetRelativeTimeThreshold; hooks.calendarFormat = getCalendarFormat; hooks.prototype = proto; return hooks; }))); },{}],7:[function(require,module,exports){ /** * @namespace Chart */ var Chart = require(28)(); require(26)(Chart); require(42)(Chart); require(22)(Chart); require(31)(Chart); require(25)(Chart); require(21)(Chart); require(23)(Chart); require(24)(Chart); require(29)(Chart); require(33)(Chart); require(34)(Chart); require(32)(Chart); require(35)(Chart); require(30)(Chart); require(27)(Chart); require(36)(Chart); require(37)(Chart); require(38)(Chart); require(39)(Chart); require(40)(Chart); require(45)(Chart); require(43)(Chart); require(44)(Chart); require(46)(Chart); require(47)(Chart); require(48)(Chart); // Controllers must be loaded after elements // See Chart.core.datasetController.dataElementType require(15)(Chart); require(16)(Chart); require(17)(Chart); require(18)(Chart); require(19)(Chart); require(20)(Chart); require(8)(Chart); require(9)(Chart); require(10)(Chart); require(11)(Chart); require(12)(Chart); require(13)(Chart); require(14)(Chart); window.Chart = module.exports = Chart; },{"10":10,"11":11,"12":12,"13":13,"14":14,"15":15,"16":16,"17":17,"18":18,"19":19,"20":20,"21":21,"22":22,"23":23,"24":24,"25":25,"26":26,"27":27,"28":28,"29":29,"30":30,"31":31,"32":32,"33":33,"34":34,"35":35,"36":36,"37":37,"38":38,"39":39,"40":40,"42":42,"43":43,"44":44,"45":45,"46":46,"47":47,"48":48,"8":8,"9":9}],8:[function(require,module,exports){ 'use strict'; module.exports = function(Chart) { Chart.Bar = function(context, config) { config.type = 'bar'; return new Chart(context, config); }; }; },{}],9:[function(require,module,exports){ 'use strict'; module.exports = function(Chart) { Chart.Bubble = function(context, config) { config.type = 'bubble'; return new Chart(context, config); }; }; },{}],10:[function(require,module,exports){ 'use strict'; module.exports = function(Chart) { Chart.Doughnut = function(context, config) { config.type = 'doughnut'; return new Chart(context, config); }; }; },{}],11:[function(require,module,exports){ 'use strict'; module.exports = function(Chart) { Chart.Line = function(context, config) { config.type = 'line'; return new Chart(context, config); }; }; },{}],12:[function(require,module,exports){ 'use strict'; module.exports = function(Chart) { Chart.PolarArea = function(context, config) { config.type = 'polarArea'; return new Chart(context, config); }; }; },{}],13:[function(require,module,exports){ 'use strict'; module.exports = function(Chart) { Chart.Radar = function(context, config) { config.type = 'radar'; return new Chart(context, config); }; }; },{}],14:[function(require,module,exports){ 'use strict'; module.exports = function(Chart) { var defaultConfig = { hover: { mode: 'single' }, scales: { xAxes: [{ type: 'linear', // scatter should not use a category axis position: 'bottom', id: 'x-axis-1' // need an ID so datasets can reference the scale }], yAxes: [{ type: 'linear', position: 'left', id: 'y-axis-1' }] }, tooltips: { callbacks: { title: function() { // Title doesn't make sense for scatter since we format the data as a point return ''; }, label: function(tooltipItem) { return '(' + tooltipItem.xLabel + ', ' + tooltipItem.yLabel + ')'; } } } }; // Register the default config for this type Chart.defaults.scatter = defaultConfig; // Scatter charts use line controllers Chart.controllers.scatter = Chart.controllers.line; Chart.Scatter = function(context, config) { config.type = 'scatter'; return new Chart(context, config); }; }; },{}],15:[function(require,module,exports){ 'use strict'; module.exports = function(Chart) { var helpers = Chart.helpers; Chart.defaults.bar = { hover: { mode: 'label' }, scales: { xAxes: [{ type: 'category', // Specific to Bar Controller categoryPercentage: 0.8, barPercentage: 0.9, // grid line settings gridLines: { offsetGridLines: true } }], yAxes: [{ type: 'linear' }] } }; Chart.controllers.bar = Chart.DatasetController.extend({ dataElementType: Chart.elements.Rectangle, initialize: function(chart, datasetIndex) { Chart.DatasetController.prototype.initialize.call(this, chart, datasetIndex); var me = this; var meta = me.getMeta(); var dataset = me.getDataset(); meta.stack = dataset.stack; // Use this to indicate that this is a bar dataset. meta.bar = true; }, // Correctly calculate the bar width accounting for stacks and the fact that not all bars are visible getStackCount: function() { var me = this; var meta = me.getMeta(); var yScale = me.getScaleForId(meta.yAxisID); var stacks = []; helpers.each(me.chart.data.datasets, function(dataset, datasetIndex) { var dsMeta = me.chart.getDatasetMeta(datasetIndex); if (dsMeta.bar && me.chart.isDatasetVisible(datasetIndex) && (yScale.options.stacked === false || (yScale.options.stacked === true && stacks.indexOf(dsMeta.stack) === -1) || (yScale.options.stacked === undefined && (dsMeta.stack === undefined || stacks.indexOf(dsMeta.stack) === -1)))) { stacks.push(dsMeta.stack); } }, me); return stacks.length; }, update: function(reset) { var me = this; helpers.each(me.getMeta().data, function(rectangle, index) { me.updateElement(rectangle, index, reset); }, me); }, updateElement: function(rectangle, index, reset) { var me = this; var meta = me.getMeta(); var xScale = me.getScaleForId(meta.xAxisID); var yScale = me.getScaleForId(meta.yAxisID); var scaleBase = yScale.getBasePixel(); var rectangleElementOptions = me.chart.options.elements.rectangle; var custom = rectangle.custom || {}; var dataset = me.getDataset(); rectangle._xScale = xScale; rectangle._yScale = yScale; rectangle._datasetIndex = me.index; rectangle._index = index; var ruler = me.getRuler(index); // The index argument for compatible rectangle._model = { x: me.calculateBarX(index, me.index, ruler), y: reset ? scaleBase : me.calculateBarY(index, me.index), // Tooltip label: me.chart.data.labels[index], datasetLabel: dataset.label, // Appearance horizontal: false, base: reset ? scaleBase : me.calculateBarBase(me.index, index), width: me.calculateBarWidth(ruler), backgroundColor: custom.backgroundColor ? custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.backgroundColor, index, rectangleElementOptions.backgroundColor), borderSkipped: custom.borderSkipped ? custom.borderSkipped : rectangleElementOptions.borderSkipped, borderColor: custom.borderColor ? custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.borderColor, index, rectangleElementOptions.borderColor), borderWidth: custom.borderWidth ? custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, rectangleElementOptions.borderWidth) }; rectangle.pivot(); }, calculateBarBase: function(datasetIndex, index) { var me = this; var meta = me.getMeta(); var yScale = me.getScaleForId(meta.yAxisID); var base = yScale.getBaseValue(); var original = base; if ((yScale.options.stacked === true) || (yScale.options.stacked === undefined && meta.stack !== undefined)) { var chart = me.chart; var datasets = chart.data.datasets; var value = Number(datasets[datasetIndex].data[index]); for (var i = 0; i < datasetIndex; i++) { var currentDs = datasets[i]; var currentDsMeta = chart.getDatasetMeta(i); if (currentDsMeta.bar && currentDsMeta.yAxisID === yScale.id && chart.isDatasetVisible(i) && meta.stack === currentDsMeta.stack) { var currentVal = Number(currentDs.data[index]); base += value < 0 ? Math.min(currentVal, original) : Math.max(currentVal, original); } } return yScale.getPixelForValue(base); } return yScale.getBasePixel(); }, getRuler: function() { var me = this; var meta = me.getMeta(); var xScale = me.getScaleForId(meta.xAxisID); var stackCount = me.getStackCount(); var tickWidth = xScale.width / xScale.ticks.length; var categoryWidth = tickWidth * xScale.options.categoryPercentage; var categorySpacing = (tickWidth - (tickWidth * xScale.options.categoryPercentage)) / 2; var fullBarWidth = categoryWidth / stackCount; var barWidth = fullBarWidth * xScale.options.barPercentage; var barSpacing = fullBarWidth - (fullBarWidth * xScale.options.barPercentage); return { stackCount: stackCount, tickWidth: tickWidth, categoryWidth: categoryWidth, categorySpacing: categorySpacing, fullBarWidth: fullBarWidth, barWidth: barWidth, barSpacing: barSpacing }; }, calculateBarWidth: function(ruler) { var me = this; var meta = me.getMeta(); var xScale = me.getScaleForId(meta.xAxisID); if (xScale.options.barThickness) { return xScale.options.barThickness; } return ruler.barWidth; }, // Get stack index from the given dataset index accounting for stacks and the fact that not all bars are visible getStackIndex: function(datasetIndex) { var me = this; var meta = me.chart.getDatasetMeta(datasetIndex); var yScale = me.getScaleForId(meta.yAxisID); var dsMeta, j; var stacks = [meta.stack]; for (j = 0; j < datasetIndex; ++j) { dsMeta = this.chart.getDatasetMeta(j); if (dsMeta.bar && this.chart.isDatasetVisible(j) && (yScale.options.stacked === false || (yScale.options.stacked === true && stacks.indexOf(dsMeta.stack) === -1) || (yScale.options.stacked === undefined && (dsMeta.stack === undefined || stacks.indexOf(dsMeta.stack) === -1)))) { stacks.push(dsMeta.stack); } } return stacks.length - 1; }, calculateBarX: function(index, datasetIndex, ruler) { var me = this; var meta = me.getMeta(); var xScale = me.getScaleForId(meta.xAxisID); var stackIndex = me.getStackIndex(datasetIndex); var leftTick = xScale.getPixelForValue(null, index, datasetIndex, me.chart.isCombo); leftTick -= me.chart.isCombo ? (ruler.tickWidth / 2) : 0; return leftTick + (ruler.barWidth / 2) + ruler.categorySpacing + (ruler.barWidth * stackIndex) + (ruler.barSpacing / 2) + (ruler.barSpacing * stackIndex); }, calculateBarY: function(index, datasetIndex) { var me = this; var meta = me.getMeta(); var yScale = me.getScaleForId(meta.yAxisID); var value = Number(me.getDataset().data[index]); if (yScale.options.stacked || (yScale.options.stacked === undefined && meta.stack !== undefined)) { var base = yScale.getBaseValue(); var sumPos = base, sumNeg = base; for (var i = 0; i < datasetIndex; i++) { var ds = me.chart.data.datasets[i]; var dsMeta = me.chart.getDatasetMeta(i); if (dsMeta.bar && dsMeta.yAxisID === yScale.id && me.chart.isDatasetVisible(i) && meta.stack === dsMeta.stack) { var stackedVal = Number(ds.data[index]); if (stackedVal < 0) { sumNeg += stackedVal || 0; } else { sumPos += stackedVal || 0; } } } if (value < 0) { return yScale.getPixelForValue(sumNeg + value); } return yScale.getPixelForValue(sumPos + value); } return yScale.getPixelForValue(value); }, draw: function(ease) { var me = this; var easingDecimal = ease || 1; var metaData = me.getMeta().data; var dataset = me.getDataset(); var i, len; Chart.canvasHelpers.clipArea(me.chart.chart.ctx, me.chart.chartArea); for (i = 0, len = metaData.length; i < len; ++i) { var d = dataset.data[i]; if (d !== null && d !== undefined && !isNaN(d)) { metaData[i].transition(easingDecimal).draw(); } } Chart.canvasHelpers.unclipArea(me.chart.chart.ctx); }, setHoverStyle: function(rectangle) { var dataset = this.chart.data.datasets[rectangle._datasetIndex]; var index = rectangle._index; var custom = rectangle.custom || {}; var model = rectangle._model; model.backgroundColor = custom.hoverBackgroundColor ? custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.hoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor)); model.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.hoverBorderColor, index, helpers.getHoverColor(model.borderColor)); model.borderWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.hoverBorderWidth, index, model.borderWidth); }, removeHoverStyle: function(rectangle) { var dataset = this.chart.data.datasets[rectangle._datasetIndex]; var index = rectangle._index; var custom = rectangle.custom || {}; var model = rectangle._model; var rectangleElementOptions = this.chart.options.elements.rectangle; model.backgroundColor = custom.backgroundColor ? custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.backgroundColor, index, rectangleElementOptions.backgroundColor); model.borderColor = custom.borderColor ? custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.borderColor, index, rectangleElementOptions.borderColor); model.borderWidth = custom.borderWidth ? custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, rectangleElementOptions.borderWidth); } }); // including horizontalBar in the bar file, instead of a file of its own // it extends bar (like pie extends doughnut) Chart.defaults.horizontalBar = { hover: { mode: 'label' }, scales: { xAxes: [{ type: 'linear', position: 'bottom' }], yAxes: [{ position: 'left', type: 'category', // Specific to Horizontal Bar Controller categoryPercentage: 0.8, barPercentage: 0.9, // grid line settings gridLines: { offsetGridLines: true } }] }, elements: { rectangle: { borderSkipped: 'left' } }, tooltips: { callbacks: { title: function(tooltipItems, data) { // Pick first xLabel for now var title = ''; if (tooltipItems.length > 0) { if (tooltipItems[0].yLabel) { title = tooltipItems[0].yLabel; } else if (data.labels.length > 0 && tooltipItems[0].index < data.labels.length) { title = data.labels[tooltipItems[0].index]; } } return title; }, label: function(tooltipItem, data) { var datasetLabel = data.datasets[tooltipItem.datasetIndex].label || ''; return datasetLabel + ': ' + tooltipItem.xLabel; } } } }; Chart.controllers.horizontalBar = Chart.controllers.bar.extend({ // Correctly calculate the bar width accounting for stacks and the fact that not all bars are visible getStackCount: function() { var me = this; var meta = me.getMeta(); var xScale = me.getScaleForId(meta.xAxisID); var stacks = []; helpers.each(me.chart.data.datasets, function(dataset, datasetIndex) { var dsMeta = me.chart.getDatasetMeta(datasetIndex); if (dsMeta.bar && me.chart.isDatasetVisible(datasetIndex) && (xScale.options.stacked === false || (xScale.options.stacked === true && stacks.indexOf(dsMeta.stack) === -1) || (xScale.options.stacked === undefined && (dsMeta.stack === undefined || stacks.indexOf(dsMeta.stack) === -1)))) { stacks.push(dsMeta.stack); } }, me); return stacks.length; }, updateElement: function(rectangle, index, reset) { var me = this; var meta = me.getMeta(); var xScale = me.getScaleForId(meta.xAxisID); var yScale = me.getScaleForId(meta.yAxisID); var scaleBase = xScale.getBasePixel(); var custom = rectangle.custom || {}; var dataset = me.getDataset(); var rectangleElementOptions = me.chart.options.elements.rectangle; rectangle._xScale = xScale; rectangle._yScale = yScale; rectangle._datasetIndex = me.index; rectangle._index = index; var ruler = me.getRuler(index); // The index argument for compatible rectangle._model = { x: reset ? scaleBase : me.calculateBarX(index, me.index), y: me.calculateBarY(index, me.index, ruler), // Tooltip label: me.chart.data.labels[index], datasetLabel: dataset.label, // Appearance horizontal: true, base: reset ? scaleBase : me.calculateBarBase(me.index, index), height: me.calculateBarHeight(ruler), backgroundColor: custom.backgroundColor ? custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.backgroundColor, index, rectangleElementOptions.backgroundColor), borderSkipped: custom.borderSkipped ? custom.borderSkipped : rectangleElementOptions.borderSkipped, borderColor: custom.borderColor ? custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.borderColor, index, rectangleElementOptions.borderColor), borderWidth: custom.borderWidth ? custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, rectangleElementOptions.borderWidth) }; rectangle.pivot(); }, calculateBarBase: function(datasetIndex, index) { var me = this; var meta = me.getMeta(); var xScale = me.getScaleForId(meta.xAxisID); var base = xScale.getBaseValue(); var originalBase = base; if (xScale.options.stacked || (xScale.options.stacked === undefined && meta.stack !== undefined)) { var chart = me.chart; var datasets = chart.data.datasets; var value = Number(datasets[datasetIndex].data[index]); for (var i = 0; i < datasetIndex; i++) { var currentDs = datasets[i]; var currentDsMeta = chart.getDatasetMeta(i); if (currentDsMeta.bar && currentDsMeta.xAxisID === xScale.id && chart.isDatasetVisible(i) && meta.stack === currentDsMeta.stack) { var currentVal = Number(currentDs.data[index]); base += value < 0 ? Math.min(currentVal, originalBase) : Math.max(currentVal, originalBase); } } return xScale.getPixelForValue(base); } return xScale.getBasePixel(); }, getRuler: function() { var me = this; var meta = me.getMeta(); var yScale = me.getScaleForId(meta.yAxisID); var stackCount = me.getStackCount(); var tickHeight = yScale.height / yScale.ticks.length; var categoryHeight = tickHeight * yScale.options.categoryPercentage; var categorySpacing = (tickHeight - (tickHeight * yScale.options.categoryPercentage)) / 2; var fullBarHeight = categoryHeight / stackCount; var barHeight = fullBarHeight * yScale.options.barPercentage; var barSpacing = fullBarHeight - (fullBarHeight * yScale.options.barPercentage); return { stackCount: stackCount, tickHeight: tickHeight, categoryHeight: categoryHeight, categorySpacing: categorySpacing, fullBarHeight: fullBarHeight, barHeight: barHeight, barSpacing: barSpacing }; }, calculateBarHeight: function(ruler) { var me = this; var meta = me.getMeta(); var yScale = me.getScaleForId(meta.yAxisID); if (yScale.options.barThickness) { return yScale.options.barThickness; } return ruler.barHeight; }, // Get stack index from the given dataset index accounting for stacks and the fact that not all bars are visible getStackIndex: function(datasetIndex) { var me = this; var meta = me.chart.getDatasetMeta(datasetIndex); var xScale = me.getScaleForId(meta.xAxisID); var dsMeta, j; var stacks = [meta.stack]; for (j = 0; j < datasetIndex; ++j) { dsMeta = this.chart.getDatasetMeta(j); if (dsMeta.bar && this.chart.isDatasetVisible(j) && (xScale.options.stacked === false || (xScale.options.stacked === true && stacks.indexOf(dsMeta.stack) === -1) || (xScale.options.stacked === undefined && (dsMeta.stack === undefined || stacks.indexOf(dsMeta.stack) === -1)))) { stacks.push(dsMeta.stack); } } return stacks.length - 1; }, calculateBarX: function(index, datasetIndex) { var me = this; var meta = me.getMeta(); var xScale = me.getScaleForId(meta.xAxisID); var value = Number(me.getDataset().data[index]); if (xScale.options.stacked || (xScale.options.stacked === undefined && meta.stack !== undefined)) { var base = xScale.getBaseValue(); var sumPos = base, sumNeg = base; for (var i = 0; i < datasetIndex; i++) { var ds = me.chart.data.datasets[i]; var dsMeta = me.chart.getDatasetMeta(i); if (dsMeta.bar && dsMeta.xAxisID === xScale.id && me.chart.isDatasetVisible(i) && meta.stack === dsMeta.stack) { var stackedVal = Number(ds.data[index]); if (stackedVal < 0) { sumNeg += stackedVal || 0; } else { sumPos += stackedVal || 0; } } } if (value < 0) { return xScale.getPixelForValue(sumNeg + value); } return xScale.getPixelForValue(sumPos + value); } return xScale.getPixelForValue(value); }, calculateBarY: function(index, datasetIndex, ruler) { var me = this; var meta = me.getMeta(); var yScale = me.getScaleForId(meta.yAxisID); var stackIndex = me.getStackIndex(datasetIndex); var topTick = yScale.getPixelForValue(null, index, datasetIndex, me.chart.isCombo); topTick -= me.chart.isCombo ? (ruler.tickHeight / 2) : 0; return topTick + (ruler.barHeight / 2) + ruler.categorySpacing + (ruler.barHeight * stackIndex) + (ruler.barSpacing / 2) + (ruler.barSpacing * stackIndex); } }); }; },{}],16:[function(require,module,exports){ 'use strict'; module.exports = function(Chart) { var helpers = Chart.helpers; Chart.defaults.bubble = { hover: { mode: 'single' }, scales: { xAxes: [{ type: 'linear', // bubble should probably use a linear scale by default position: 'bottom', id: 'x-axis-0' // need an ID so datasets can reference the scale }], yAxes: [{ type: 'linear', position: 'left', id: 'y-axis-0' }] }, tooltips: { callbacks: { title: function() { // Title doesn't make sense for scatter since we format the data as a point return ''; }, label: function(tooltipItem, data) { var datasetLabel = data.datasets[tooltipItem.datasetIndex].label || ''; var dataPoint = data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index]; return datasetLabel + ': (' + tooltipItem.xLabel + ', ' + tooltipItem.yLabel + ', ' + dataPoint.r + ')'; } } } }; Chart.controllers.bubble = Chart.DatasetController.extend({ dataElementType: Chart.elements.Point, update: function(reset) { var me = this; var meta = me.getMeta(); var points = meta.data; // Update Points helpers.each(points, function(point, index) { me.updateElement(point, index, reset); }); }, updateElement: function(point, index, reset) { var me = this; var meta = me.getMeta(); var xScale = me.getScaleForId(meta.xAxisID); var yScale = me.getScaleForId(meta.yAxisID); var custom = point.custom || {}; var dataset = me.getDataset(); var data = dataset.data[index]; var pointElementOptions = me.chart.options.elements.point; var dsIndex = me.index; helpers.extend(point, { // Utility _xScale: xScale, _yScale: yScale, _datasetIndex: dsIndex, _index: index, // Desired view properties _model: { x: reset ? xScale.getPixelForDecimal(0.5) : xScale.getPixelForValue(typeof data === 'object' ? data : NaN, index, dsIndex, me.chart.isCombo), y: reset ? yScale.getBasePixel() : yScale.getPixelForValue(data, index, dsIndex), // Appearance radius: reset ? 0 : custom.radius ? custom.radius : me.getRadius(data), // Tooltip hitRadius: custom.hitRadius ? custom.hitRadius : helpers.getValueAtIndexOrDefault(dataset.hitRadius, index, pointElementOptions.hitRadius) } }); // Trick to reset the styles of the point Chart.DatasetController.prototype.removeHoverStyle.call(me, point, pointElementOptions); var model = point._model; model.skip = custom.skip ? custom.skip : (isNaN(model.x) || isNaN(model.y)); point.pivot(); }, getRadius: function(value) { return value.r || this.chart.options.elements.point.radius; }, setHoverStyle: function(point) { var me = this; Chart.DatasetController.prototype.setHoverStyle.call(me, point); // Radius var dataset = me.chart.data.datasets[point._datasetIndex]; var index = point._index; var custom = point.custom || {}; var model = point._model; model.radius = custom.hoverRadius ? custom.hoverRadius : (helpers.getValueAtIndexOrDefault(dataset.hoverRadius, index, me.chart.options.elements.point.hoverRadius)) + me.getRadius(dataset.data[index]); }, removeHoverStyle: function(point) { var me = this; Chart.DatasetController.prototype.removeHoverStyle.call(me, point, me.chart.options.elements.point); var dataVal = me.chart.data.datasets[point._datasetIndex].data[point._index]; var custom = point.custom || {}; var model = point._model; model.radius = custom.radius ? custom.radius : me.getRadius(dataVal); } }); }; },{}],17:[function(require,module,exports){ 'use strict'; module.exports = function(Chart) { var helpers = Chart.helpers, defaults = Chart.defaults; defaults.doughnut = { animation: { // Boolean - Whether we animate the rotation of the Doughnut animateRotate: true, // Boolean - Whether we animate scaling the Doughnut from the centre animateScale: false }, aspectRatio: 1, hover: { mode: 'single' }, legendCallback: function(chart) { var text = []; text.push('
                                                                                                '); var data = chart.data; var datasets = data.datasets; var labels = data.labels; if (datasets.length) { for (var i = 0; i < datasets[0].data.length; ++i) { text.push('
                                                                                              • '); if (labels[i]) { text.push(labels[i]); } text.push('
                                                                                              • '); } } text.push('
                                                                                              '); return text.join(''); }, legend: { labels: { generateLabels: function(chart) { var data = chart.data; if (data.labels.length && data.datasets.length) { return data.labels.map(function(label, i) { var meta = chart.getDatasetMeta(0); var ds = data.datasets[0]; var arc = meta.data[i]; var custom = arc && arc.custom || {}; var getValueAtIndexOrDefault = helpers.getValueAtIndexOrDefault; var arcOpts = chart.options.elements.arc; var fill = custom.backgroundColor ? custom.backgroundColor : getValueAtIndexOrDefault(ds.backgroundColor, i, arcOpts.backgroundColor); var stroke = custom.borderColor ? custom.borderColor : getValueAtIndexOrDefault(ds.borderColor, i, arcOpts.borderColor); var bw = custom.borderWidth ? custom.borderWidth : getValueAtIndexOrDefault(ds.borderWidth, i, arcOpts.borderWidth); return { text: label, fillStyle: fill, strokeStyle: stroke, lineWidth: bw, hidden: isNaN(ds.data[i]) || meta.data[i].hidden, // Extra data used for toggling the correct item index: i }; }); } return []; } }, onClick: function(e, legendItem) { var index = legendItem.index; var chart = this.chart; var i, ilen, meta; for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) { meta = chart.getDatasetMeta(i); // toggle visibility of index if exists if (meta.data[index]) { meta.data[index].hidden = !meta.data[index].hidden; } } chart.update(); } }, // The percentage of the chart that we cut out of the middle. cutoutPercentage: 50, // The rotation of the chart, where the first data arc begins. rotation: Math.PI * -0.5, // The total circumference of the chart. circumference: Math.PI * 2.0, // Need to override these to give a nice default tooltips: { callbacks: { title: function() { return ''; }, label: function(tooltipItem, data) { var dataLabel = data.labels[tooltipItem.index]; var value = ': ' + data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index]; if (helpers.isArray(dataLabel)) { // show value on first line of multiline label // need to clone because we are changing the value dataLabel = dataLabel.slice(); dataLabel[0] += value; } else { dataLabel += value; } return dataLabel; } } } }; defaults.pie = helpers.clone(defaults.doughnut); helpers.extend(defaults.pie, { cutoutPercentage: 0 }); Chart.controllers.doughnut = Chart.controllers.pie = Chart.DatasetController.extend({ dataElementType: Chart.elements.Arc, linkScales: helpers.noop, // Get index of the dataset in relation to the visible datasets. This allows determining the inner and outer radius correctly getRingIndex: function(datasetIndex) { var ringIndex = 0; for (var j = 0; j < datasetIndex; ++j) { if (this.chart.isDatasetVisible(j)) { ++ringIndex; } } return ringIndex; }, update: function(reset) { var me = this; var chart = me.chart, chartArea = chart.chartArea, opts = chart.options, arcOpts = opts.elements.arc, availableWidth = chartArea.right - chartArea.left - arcOpts.borderWidth, availableHeight = chartArea.bottom - chartArea.top - arcOpts.borderWidth, minSize = Math.min(availableWidth, availableHeight), offset = { x: 0, y: 0 }, meta = me.getMeta(), cutoutPercentage = opts.cutoutPercentage, circumference = opts.circumference; // If the chart's circumference isn't a full circle, calculate minSize as a ratio of the width/height of the arc if (circumference < Math.PI * 2.0) { var startAngle = opts.rotation % (Math.PI * 2.0); startAngle += Math.PI * 2.0 * (startAngle >= Math.PI ? -1 : startAngle < -Math.PI ? 1 : 0); var endAngle = startAngle + circumference; var start = {x: Math.cos(startAngle), y: Math.sin(startAngle)}; var end = {x: Math.cos(endAngle), y: Math.sin(endAngle)}; var contains0 = (startAngle <= 0 && 0 <= endAngle) || (startAngle <= Math.PI * 2.0 && Math.PI * 2.0 <= endAngle); var contains90 = (startAngle <= Math.PI * 0.5 && Math.PI * 0.5 <= endAngle) || (startAngle <= Math.PI * 2.5 && Math.PI * 2.5 <= endAngle); var contains180 = (startAngle <= -Math.PI && -Math.PI <= endAngle) || (startAngle <= Math.PI && Math.PI <= endAngle); var contains270 = (startAngle <= -Math.PI * 0.5 && -Math.PI * 0.5 <= endAngle) || (startAngle <= Math.PI * 1.5 && Math.PI * 1.5 <= endAngle); var cutout = cutoutPercentage / 100.0; var min = {x: contains180 ? -1 : Math.min(start.x * (start.x < 0 ? 1 : cutout), end.x * (end.x < 0 ? 1 : cutout)), y: contains270 ? -1 : Math.min(start.y * (start.y < 0 ? 1 : cutout), end.y * (end.y < 0 ? 1 : cutout))}; var max = {x: contains0 ? 1 : Math.max(start.x * (start.x > 0 ? 1 : cutout), end.x * (end.x > 0 ? 1 : cutout)), y: contains90 ? 1 : Math.max(start.y * (start.y > 0 ? 1 : cutout), end.y * (end.y > 0 ? 1 : cutout))}; var size = {width: (max.x - min.x) * 0.5, height: (max.y - min.y) * 0.5}; minSize = Math.min(availableWidth / size.width, availableHeight / size.height); offset = {x: (max.x + min.x) * -0.5, y: (max.y + min.y) * -0.5}; } chart.borderWidth = me.getMaxBorderWidth(meta.data); chart.outerRadius = Math.max((minSize - chart.borderWidth) / 2, 0); chart.innerRadius = Math.max(cutoutPercentage ? (chart.outerRadius / 100) * (cutoutPercentage) : 0, 0); chart.radiusLength = (chart.outerRadius - chart.innerRadius) / chart.getVisibleDatasetCount(); chart.offsetX = offset.x * chart.outerRadius; chart.offsetY = offset.y * chart.outerRadius; meta.total = me.calculateTotal(); me.outerRadius = chart.outerRadius - (chart.radiusLength * me.getRingIndex(me.index)); me.innerRadius = Math.max(me.outerRadius - chart.radiusLength, 0); helpers.each(meta.data, function(arc, index) { me.updateElement(arc, index, reset); }); }, updateElement: function(arc, index, reset) { var me = this; var chart = me.chart, chartArea = chart.chartArea, opts = chart.options, animationOpts = opts.animation, centerX = (chartArea.left + chartArea.right) / 2, centerY = (chartArea.top + chartArea.bottom) / 2, startAngle = opts.rotation, // non reset case handled later endAngle = opts.rotation, // non reset case handled later dataset = me.getDataset(), circumference = reset && animationOpts.animateRotate ? 0 : arc.hidden ? 0 : me.calculateCircumference(dataset.data[index]) * (opts.circumference / (2.0 * Math.PI)), innerRadius = reset && animationOpts.animateScale ? 0 : me.innerRadius, outerRadius = reset && animationOpts.animateScale ? 0 : me.outerRadius, valueAtIndexOrDefault = helpers.getValueAtIndexOrDefault; helpers.extend(arc, { // Utility _datasetIndex: me.index, _index: index, // Desired view properties _model: { x: centerX + chart.offsetX, y: centerY + chart.offsetY, startAngle: startAngle, endAngle: endAngle, circumference: circumference, outerRadius: outerRadius, innerRadius: innerRadius, label: valueAtIndexOrDefault(dataset.label, index, chart.data.labels[index]) } }); var model = arc._model; // Resets the visual styles this.removeHoverStyle(arc); // Set correct angles if not resetting if (!reset || !animationOpts.animateRotate) { if (index === 0) { model.startAngle = opts.rotation; } else { model.startAngle = me.getMeta().data[index - 1]._model.endAngle; } model.endAngle = model.startAngle + model.circumference; } arc.pivot(); }, removeHoverStyle: function(arc) { Chart.DatasetController.prototype.removeHoverStyle.call(this, arc, this.chart.options.elements.arc); }, calculateTotal: function() { var dataset = this.getDataset(); var meta = this.getMeta(); var total = 0; var value; helpers.each(meta.data, function(element, index) { value = dataset.data[index]; if (!isNaN(value) && !element.hidden) { total += Math.abs(value); } }); /* if (total === 0) { total = NaN; }*/ return total; }, calculateCircumference: function(value) { var total = this.getMeta().total; if (total > 0 && !isNaN(value)) { return (Math.PI * 2.0) * (value / total); } return 0; }, // gets the max border or hover width to properly scale pie charts getMaxBorderWidth: function(elements) { var max = 0, index = this.index, length = elements.length, borderWidth, hoverWidth; for (var i = 0; i < length; i++) { borderWidth = elements[i]._model ? elements[i]._model.borderWidth : 0; hoverWidth = elements[i]._chart ? elements[i]._chart.config.data.datasets[index].hoverBorderWidth : 0; max = borderWidth > max ? borderWidth : max; max = hoverWidth > max ? hoverWidth : max; } return max; } }); }; },{}],18:[function(require,module,exports){ 'use strict'; module.exports = function(Chart) { var helpers = Chart.helpers; Chart.defaults.line = { showLines: true, spanGaps: false, hover: { mode: 'label' }, scales: { xAxes: [{ type: 'category', id: 'x-axis-0' }], yAxes: [{ type: 'linear', id: 'y-axis-0' }] } }; function lineEnabled(dataset, options) { return helpers.getValueOrDefault(dataset.showLine, options.showLines); } Chart.controllers.line = Chart.DatasetController.extend({ datasetElementType: Chart.elements.Line, dataElementType: Chart.elements.Point, update: function(reset) { var me = this; var meta = me.getMeta(); var line = meta.dataset; var points = meta.data || []; var options = me.chart.options; var lineElementOptions = options.elements.line; var scale = me.getScaleForId(meta.yAxisID); var i, ilen, custom; var dataset = me.getDataset(); var showLine = lineEnabled(dataset, options); // Update Line if (showLine) { custom = line.custom || {}; // Compatibility: If the properties are defined with only the old name, use those values if ((dataset.tension !== undefined) && (dataset.lineTension === undefined)) { dataset.lineTension = dataset.tension; } // Utility line._scale = scale; line._datasetIndex = me.index; // Data line._children = points; // Model line._model = { // Appearance // The default behavior of lines is to break at null values, according // to https://github.com/chartjs/Chart.js/issues/2435#issuecomment-216718158 // This option gives lines the ability to span gaps spanGaps: dataset.spanGaps ? dataset.spanGaps : options.spanGaps, tension: custom.tension ? custom.tension : helpers.getValueOrDefault(dataset.lineTension, lineElementOptions.tension), backgroundColor: custom.backgroundColor ? custom.backgroundColor : (dataset.backgroundColor || lineElementOptions.backgroundColor), borderWidth: custom.borderWidth ? custom.borderWidth : (dataset.borderWidth || lineElementOptions.borderWidth), borderColor: custom.borderColor ? custom.borderColor : (dataset.borderColor || lineElementOptions.borderColor), borderCapStyle: custom.borderCapStyle ? custom.borderCapStyle : (dataset.borderCapStyle || lineElementOptions.borderCapStyle), borderDash: custom.borderDash ? custom.borderDash : (dataset.borderDash || lineElementOptions.borderDash), borderDashOffset: custom.borderDashOffset ? custom.borderDashOffset : (dataset.borderDashOffset || lineElementOptions.borderDashOffset), borderJoinStyle: custom.borderJoinStyle ? custom.borderJoinStyle : (dataset.borderJoinStyle || lineElementOptions.borderJoinStyle), fill: custom.fill ? custom.fill : (dataset.fill !== undefined ? dataset.fill : lineElementOptions.fill), steppedLine: custom.steppedLine ? custom.steppedLine : helpers.getValueOrDefault(dataset.steppedLine, lineElementOptions.stepped), cubicInterpolationMode: custom.cubicInterpolationMode ? custom.cubicInterpolationMode : helpers.getValueOrDefault(dataset.cubicInterpolationMode, lineElementOptions.cubicInterpolationMode), // Scale scaleTop: scale.top, scaleBottom: scale.bottom, scaleZero: scale.getBasePixel() }; line.pivot(); } // Update Points for (i=0, ilen=points.length; i'); var data = chart.data; var datasets = data.datasets; var labels = data.labels; if (datasets.length) { for (var i = 0; i < datasets[0].data.length; ++i) { text.push('
                                                                                            • '); if (labels[i]) { text.push(labels[i]); } text.push('
                                                                                            • '); } } text.push(''); return text.join(''); }, legend: { labels: { generateLabels: function(chart) { var data = chart.data; if (data.labels.length && data.datasets.length) { return data.labels.map(function(label, i) { var meta = chart.getDatasetMeta(0); var ds = data.datasets[0]; var arc = meta.data[i]; var custom = arc.custom || {}; var getValueAtIndexOrDefault = helpers.getValueAtIndexOrDefault; var arcOpts = chart.options.elements.arc; var fill = custom.backgroundColor ? custom.backgroundColor : getValueAtIndexOrDefault(ds.backgroundColor, i, arcOpts.backgroundColor); var stroke = custom.borderColor ? custom.borderColor : getValueAtIndexOrDefault(ds.borderColor, i, arcOpts.borderColor); var bw = custom.borderWidth ? custom.borderWidth : getValueAtIndexOrDefault(ds.borderWidth, i, arcOpts.borderWidth); return { text: label, fillStyle: fill, strokeStyle: stroke, lineWidth: bw, hidden: isNaN(ds.data[i]) || meta.data[i].hidden, // Extra data used for toggling the correct item index: i }; }); } return []; } }, onClick: function(e, legendItem) { var index = legendItem.index; var chart = this.chart; var i, ilen, meta; for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) { meta = chart.getDatasetMeta(i); meta.data[index].hidden = !meta.data[index].hidden; } chart.update(); } }, // Need to override these to give a nice default tooltips: { callbacks: { title: function() { return ''; }, label: function(tooltipItem, data) { return data.labels[tooltipItem.index] + ': ' + tooltipItem.yLabel; } } } }; Chart.controllers.polarArea = Chart.DatasetController.extend({ dataElementType: Chart.elements.Arc, linkScales: helpers.noop, update: function(reset) { var me = this; var chart = me.chart; var chartArea = chart.chartArea; var meta = me.getMeta(); var opts = chart.options; var arcOpts = opts.elements.arc; var minSize = Math.min(chartArea.right - chartArea.left, chartArea.bottom - chartArea.top); chart.outerRadius = Math.max((minSize - arcOpts.borderWidth / 2) / 2, 0); chart.innerRadius = Math.max(opts.cutoutPercentage ? (chart.outerRadius / 100) * (opts.cutoutPercentage) : 1, 0); chart.radiusLength = (chart.outerRadius - chart.innerRadius) / chart.getVisibleDatasetCount(); me.outerRadius = chart.outerRadius - (chart.radiusLength * me.index); me.innerRadius = me.outerRadius - chart.radiusLength; meta.count = me.countVisibleElements(); helpers.each(meta.data, function(arc, index) { me.updateElement(arc, index, reset); }); }, updateElement: function(arc, index, reset) { var me = this; var chart = me.chart; var dataset = me.getDataset(); var opts = chart.options; var animationOpts = opts.animation; var scale = chart.scale; var getValueAtIndexOrDefault = helpers.getValueAtIndexOrDefault; var labels = chart.data.labels; var circumference = me.calculateCircumference(dataset.data[index]); var centerX = scale.xCenter; var centerY = scale.yCenter; // If there is NaN data before us, we need to calculate the starting angle correctly. // We could be way more efficient here, but its unlikely that the polar area chart will have a lot of data var visibleCount = 0; var meta = me.getMeta(); for (var i = 0; i < index; ++i) { if (!isNaN(dataset.data[i]) && !meta.data[i].hidden) { ++visibleCount; } } // var negHalfPI = -0.5 * Math.PI; var datasetStartAngle = opts.startAngle; var distance = arc.hidden ? 0 : scale.getDistanceFromCenterForValue(dataset.data[index]); var startAngle = datasetStartAngle + (circumference * visibleCount); var endAngle = startAngle + (arc.hidden ? 0 : circumference); var resetRadius = animationOpts.animateScale ? 0 : scale.getDistanceFromCenterForValue(dataset.data[index]); helpers.extend(arc, { // Utility _datasetIndex: me.index, _index: index, _scale: scale, // Desired view properties _model: { x: centerX, y: centerY, innerRadius: 0, outerRadius: reset ? resetRadius : distance, startAngle: reset && animationOpts.animateRotate ? datasetStartAngle : startAngle, endAngle: reset && animationOpts.animateRotate ? datasetStartAngle : endAngle, label: getValueAtIndexOrDefault(labels, index, labels[index]) } }); // Apply border and fill style me.removeHoverStyle(arc); arc.pivot(); }, removeHoverStyle: function(arc) { Chart.DatasetController.prototype.removeHoverStyle.call(this, arc, this.chart.options.elements.arc); }, countVisibleElements: function() { var dataset = this.getDataset(); var meta = this.getMeta(); var count = 0; helpers.each(meta.data, function(element, index) { if (!isNaN(dataset.data[index]) && !element.hidden) { count++; } }); return count; }, calculateCircumference: function(value) { var count = this.getMeta().count; if (count > 0 && !isNaN(value)) { return (2 * Math.PI) / count; } return 0; } }); }; },{}],20:[function(require,module,exports){ 'use strict'; module.exports = function(Chart) { var helpers = Chart.helpers; Chart.defaults.radar = { aspectRatio: 1, scale: { type: 'radialLinear' }, elements: { line: { tension: 0 // no bezier in radar } } }; Chart.controllers.radar = Chart.DatasetController.extend({ datasetElementType: Chart.elements.Line, dataElementType: Chart.elements.Point, linkScales: helpers.noop, update: function(reset) { var me = this; var meta = me.getMeta(); var line = meta.dataset; var points = meta.data; var custom = line.custom || {}; var dataset = me.getDataset(); var lineElementOptions = me.chart.options.elements.line; var scale = me.chart.scale; // Compatibility: If the properties are defined with only the old name, use those values if ((dataset.tension !== undefined) && (dataset.lineTension === undefined)) { dataset.lineTension = dataset.tension; } helpers.extend(meta.dataset, { // Utility _datasetIndex: me.index, // Data _children: points, _loop: true, // Model _model: { // Appearance tension: custom.tension ? custom.tension : helpers.getValueOrDefault(dataset.lineTension, lineElementOptions.tension), backgroundColor: custom.backgroundColor ? custom.backgroundColor : (dataset.backgroundColor || lineElementOptions.backgroundColor), borderWidth: custom.borderWidth ? custom.borderWidth : (dataset.borderWidth || lineElementOptions.borderWidth), borderColor: custom.borderColor ? custom.borderColor : (dataset.borderColor || lineElementOptions.borderColor), fill: custom.fill ? custom.fill : (dataset.fill !== undefined ? dataset.fill : lineElementOptions.fill), borderCapStyle: custom.borderCapStyle ? custom.borderCapStyle : (dataset.borderCapStyle || lineElementOptions.borderCapStyle), borderDash: custom.borderDash ? custom.borderDash : (dataset.borderDash || lineElementOptions.borderDash), borderDashOffset: custom.borderDashOffset ? custom.borderDashOffset : (dataset.borderDashOffset || lineElementOptions.borderDashOffset), borderJoinStyle: custom.borderJoinStyle ? custom.borderJoinStyle : (dataset.borderJoinStyle || lineElementOptions.borderJoinStyle), // Scale scaleTop: scale.top, scaleBottom: scale.bottom, scaleZero: scale.getBasePosition() } }); meta.dataset.pivot(); // Update Points helpers.each(points, function(point, index) { me.updateElement(point, index, reset); }, me); // Update bezier control points me.updateBezierControlPoints(); }, updateElement: function(point, index, reset) { var me = this; var custom = point.custom || {}; var dataset = me.getDataset(); var scale = me.chart.scale; var pointElementOptions = me.chart.options.elements.point; var pointPosition = scale.getPointPositionForValue(index, dataset.data[index]); helpers.extend(point, { // Utility _datasetIndex: me.index, _index: index, _scale: scale, // Desired view properties _model: { x: reset ? scale.xCenter : pointPosition.x, // value not used in dataset scale, but we want a consistent API between scales y: reset ? scale.yCenter : pointPosition.y, // Appearance tension: custom.tension ? custom.tension : helpers.getValueOrDefault(dataset.lineTension, me.chart.options.elements.line.tension), radius: custom.radius ? custom.radius : helpers.getValueAtIndexOrDefault(dataset.pointRadius, index, pointElementOptions.radius), backgroundColor: custom.backgroundColor ? custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointBackgroundColor, index, pointElementOptions.backgroundColor), borderColor: custom.borderColor ? custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.pointBorderColor, index, pointElementOptions.borderColor), borderWidth: custom.borderWidth ? custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, pointElementOptions.borderWidth), pointStyle: custom.pointStyle ? custom.pointStyle : helpers.getValueAtIndexOrDefault(dataset.pointStyle, index, pointElementOptions.pointStyle), // Tooltip hitRadius: custom.hitRadius ? custom.hitRadius : helpers.getValueAtIndexOrDefault(dataset.hitRadius, index, pointElementOptions.hitRadius) } }); point._model.skip = custom.skip ? custom.skip : (isNaN(point._model.x) || isNaN(point._model.y)); }, updateBezierControlPoints: function() { var chartArea = this.chart.chartArea; var meta = this.getMeta(); helpers.each(meta.data, function(point, index) { var model = point._model; var controlPoints = helpers.splineCurve( helpers.previousItem(meta.data, index, true)._model, model, helpers.nextItem(meta.data, index, true)._model, model.tension ); // Prevent the bezier going outside of the bounds of the graph model.controlPointPreviousX = Math.max(Math.min(controlPoints.previous.x, chartArea.right), chartArea.left); model.controlPointPreviousY = Math.max(Math.min(controlPoints.previous.y, chartArea.bottom), chartArea.top); model.controlPointNextX = Math.max(Math.min(controlPoints.next.x, chartArea.right), chartArea.left); model.controlPointNextY = Math.max(Math.min(controlPoints.next.y, chartArea.bottom), chartArea.top); // Now pivot the point for animation point.pivot(); }); }, draw: function(ease) { var meta = this.getMeta(); var easingDecimal = ease || 1; // Transition Point Locations helpers.each(meta.data, function(point) { point.transition(easingDecimal); }); // Transition and Draw the line meta.dataset.transition(easingDecimal).draw(); // Draw the points helpers.each(meta.data, function(point) { point.draw(); }); }, setHoverStyle: function(point) { // Point var dataset = this.chart.data.datasets[point._datasetIndex]; var custom = point.custom || {}; var index = point._index; var model = point._model; model.radius = custom.hoverRadius ? custom.hoverRadius : helpers.getValueAtIndexOrDefault(dataset.pointHoverRadius, index, this.chart.options.elements.point.hoverRadius); model.backgroundColor = custom.hoverBackgroundColor ? custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor)); model.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.getHoverColor(model.borderColor)); model.borderWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderWidth, index, model.borderWidth); }, removeHoverStyle: function(point) { var dataset = this.chart.data.datasets[point._datasetIndex]; var custom = point.custom || {}; var index = point._index; var model = point._model; var pointElementOptions = this.chart.options.elements.point; model.radius = custom.radius ? custom.radius : helpers.getValueAtIndexOrDefault(dataset.radius, index, pointElementOptions.radius); model.backgroundColor = custom.backgroundColor ? custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointBackgroundColor, index, pointElementOptions.backgroundColor); model.borderColor = custom.borderColor ? custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.pointBorderColor, index, pointElementOptions.borderColor); model.borderWidth = custom.borderWidth ? custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, pointElementOptions.borderWidth); } }); }; },{}],21:[function(require,module,exports){ /* global window: false */ 'use strict'; module.exports = function(Chart) { var helpers = Chart.helpers; Chart.defaults.global.animation = { duration: 1000, easing: 'easeOutQuart', onProgress: helpers.noop, onComplete: helpers.noop }; Chart.Animation = Chart.Element.extend({ currentStep: null, // the current animation step numSteps: 60, // default number of steps easing: '', // the easing to use for this animation render: null, // render function used by the animation service onAnimationProgress: null, // user specified callback to fire on each step of the animation onAnimationComplete: null // user specified callback to fire when the animation finishes }); Chart.animationService = { frameDuration: 17, animations: [], dropFrames: 0, request: null, /** * @function Chart.animationService.addAnimation * @param chartInstance {ChartController} the chart to animate * @param animationObject {IAnimation} the animation that we will animate * @param duration {Number} length of animation in ms * @param lazy {Boolean} if true, the chart is not marked as animating to enable more responsive interactions */ addAnimation: function(chartInstance, animationObject, duration, lazy) { var me = this; if (!lazy) { chartInstance.animating = true; } for (var index = 0; index < me.animations.length; ++index) { if (me.animations[index].chartInstance === chartInstance) { // replacing an in progress animation me.animations[index].animationObject = animationObject; return; } } me.animations.push({ chartInstance: chartInstance, animationObject: animationObject }); // If there are no animations queued, manually kickstart a digest, for lack of a better word if (me.animations.length === 1) { me.requestAnimationFrame(); } }, // Cancel the animation for a given chart instance cancelAnimation: function(chartInstance) { var index = helpers.findIndex(this.animations, function(animationWrapper) { return animationWrapper.chartInstance === chartInstance; }); if (index !== -1) { this.animations.splice(index, 1); chartInstance.animating = false; } }, requestAnimationFrame: function() { var me = this; if (me.request === null) { // Skip animation frame requests until the active one is executed. // This can happen when processing mouse events, e.g. 'mousemove' // and 'mouseout' events will trigger multiple renders. me.request = helpers.requestAnimFrame.call(window, function() { me.request = null; me.startDigest(); }); } }, startDigest: function() { var me = this; var startTime = Date.now(); var framesToDrop = 0; if (me.dropFrames > 1) { framesToDrop = Math.floor(me.dropFrames); me.dropFrames = me.dropFrames % 1; } var i = 0; while (i < me.animations.length) { if (me.animations[i].animationObject.currentStep === null) { me.animations[i].animationObject.currentStep = 0; } me.animations[i].animationObject.currentStep += 1 + framesToDrop; if (me.animations[i].animationObject.currentStep > me.animations[i].animationObject.numSteps) { me.animations[i].animationObject.currentStep = me.animations[i].animationObject.numSteps; } me.animations[i].animationObject.render(me.animations[i].chartInstance, me.animations[i].animationObject); if (me.animations[i].animationObject.onAnimationProgress && me.animations[i].animationObject.onAnimationProgress.call) { me.animations[i].animationObject.onAnimationProgress.call(me.animations[i].chartInstance, me.animations[i]); } if (me.animations[i].animationObject.currentStep === me.animations[i].animationObject.numSteps) { if (me.animations[i].animationObject.onAnimationComplete && me.animations[i].animationObject.onAnimationComplete.call) { me.animations[i].animationObject.onAnimationComplete.call(me.animations[i].chartInstance, me.animations[i]); } // executed the last frame. Remove the animation. me.animations[i].chartInstance.animating = false; me.animations.splice(i, 1); } else { ++i; } } var endTime = Date.now(); var dropFrames = (endTime - startTime) / me.frameDuration; me.dropFrames += dropFrames; // Do we have more stuff to animate? if (me.animations.length > 0) { me.requestAnimationFrame(); } } }; }; },{}],22:[function(require,module,exports){ 'use strict'; module.exports = function(Chart) { // Global Chart canvas helpers object for drawing items to canvas var helpers = Chart.canvasHelpers = {}; helpers.drawPoint = function(ctx, pointStyle, radius, x, y) { var type, edgeLength, xOffset, yOffset, height, size; if (typeof pointStyle === 'object') { type = pointStyle.toString(); if (type === '[object HTMLImageElement]' || type === '[object HTMLCanvasElement]') { ctx.drawImage(pointStyle, x - pointStyle.width / 2, y - pointStyle.height / 2); return; } } if (isNaN(radius) || radius <= 0) { return; } switch (pointStyle) { // Default includes circle default: ctx.beginPath(); ctx.arc(x, y, radius, 0, Math.PI * 2); ctx.closePath(); ctx.fill(); break; case 'triangle': ctx.beginPath(); edgeLength = 3 * radius / Math.sqrt(3); height = edgeLength * Math.sqrt(3) / 2; ctx.moveTo(x - edgeLength / 2, y + height / 3); ctx.lineTo(x + edgeLength / 2, y + height / 3); ctx.lineTo(x, y - 2 * height / 3); ctx.closePath(); ctx.fill(); break; case 'rect': size = 1 / Math.SQRT2 * radius; ctx.beginPath(); ctx.fillRect(x - size, y - size, 2 * size, 2 * size); ctx.strokeRect(x - size, y - size, 2 * size, 2 * size); break; case 'rectRounded': var offset = radius / Math.SQRT2; var leftX = x - offset; var topY = y - offset; var sideSize = Math.SQRT2 * radius; Chart.helpers.drawRoundedRectangle(ctx, leftX, topY, sideSize, sideSize, radius / 2); ctx.fill(); break; case 'rectRot': size = 1 / Math.SQRT2 * radius; ctx.beginPath(); ctx.moveTo(x - size, y); ctx.lineTo(x, y + size); ctx.lineTo(x + size, y); ctx.lineTo(x, y - size); ctx.closePath(); ctx.fill(); break; case 'cross': ctx.beginPath(); ctx.moveTo(x, y + radius); ctx.lineTo(x, y - radius); ctx.moveTo(x - radius, y); ctx.lineTo(x + radius, y); ctx.closePath(); break; case 'crossRot': ctx.beginPath(); xOffset = Math.cos(Math.PI / 4) * radius; yOffset = Math.sin(Math.PI / 4) * radius; ctx.moveTo(x - xOffset, y - yOffset); ctx.lineTo(x + xOffset, y + yOffset); ctx.moveTo(x - xOffset, y + yOffset); ctx.lineTo(x + xOffset, y - yOffset); ctx.closePath(); break; case 'star': ctx.beginPath(); ctx.moveTo(x, y + radius); ctx.lineTo(x, y - radius); ctx.moveTo(x - radius, y); ctx.lineTo(x + radius, y); xOffset = Math.cos(Math.PI / 4) * radius; yOffset = Math.sin(Math.PI / 4) * radius; ctx.moveTo(x - xOffset, y - yOffset); ctx.lineTo(x + xOffset, y + yOffset); ctx.moveTo(x - xOffset, y + yOffset); ctx.lineTo(x + xOffset, y - yOffset); ctx.closePath(); break; case 'line': ctx.beginPath(); ctx.moveTo(x - radius, y); ctx.lineTo(x + radius, y); ctx.closePath(); break; case 'dash': ctx.beginPath(); ctx.moveTo(x, y); ctx.lineTo(x + radius, y); ctx.closePath(); break; } ctx.stroke(); }; helpers.clipArea = function(ctx, clipArea) { ctx.save(); ctx.beginPath(); ctx.rect(clipArea.left, clipArea.top, clipArea.right - clipArea.left, clipArea.bottom - clipArea.top); ctx.clip(); }; helpers.unclipArea = function(ctx) { ctx.restore(); }; }; },{}],23:[function(require,module,exports){ 'use strict'; module.exports = function(Chart) { var helpers = Chart.helpers; var plugins = Chart.plugins; var platform = Chart.platform; // Create a dictionary of chart types, to allow for extension of existing types Chart.types = {}; // Store a reference to each instance - allowing us to globally resize chart instances on window resize. // Destroy method on the chart will remove the instance of the chart from this reference. Chart.instances = {}; // Controllers available for dataset visualization eg. bar, line, slice, etc. Chart.controllers = {}; /** * Initializes the given config with global and chart default values. */ function initConfig(config) { config = config || {}; // Do NOT use configMerge() for the data object because this method merges arrays // and so would change references to labels and datasets, preventing data updates. var data = config.data = config.data || {}; data.datasets = data.datasets || []; data.labels = data.labels || []; config.options = helpers.configMerge( Chart.defaults.global, Chart.defaults[config.type], config.options || {}); return config; } /** * Updates the config of the chart * @param chart {Chart.Controller} chart to update the options for */ function updateConfig(chart) { var newOptions = chart.options; // Update Scale(s) with options if (newOptions.scale) { chart.scale.options = newOptions.scale; } else if (newOptions.scales) { newOptions.scales.xAxes.concat(newOptions.scales.yAxes).forEach(function(scaleOptions) { chart.scales[scaleOptions.id].options = scaleOptions; }); } // Tooltip chart.tooltip._options = newOptions.tooltips; } /** * @class Chart.Controller * The main controller of a chart. */ Chart.Controller = function(item, config, instance) { var me = this; config = initConfig(config); var context = platform.acquireContext(item, config); var canvas = context && context.canvas; var height = canvas && canvas.height; var width = canvas && canvas.width; instance.ctx = context; instance.canvas = canvas; instance.config = config; instance.width = width; instance.height = height; instance.aspectRatio = height? width / height : null; me.id = helpers.uid(); me.chart = instance; me.config = config; me.options = config.options; me._bufferedRender = false; // Add the chart instance to the global namespace Chart.instances[me.id] = me; Object.defineProperty(me, 'data', { get: function() { return me.config.data; } }); if (!context || !canvas) { // The given item is not a compatible context2d element, let's return before finalizing // the chart initialization but after setting basic chart / controller properties that // can help to figure out that the chart is not valid (e.g chart.canvas !== null); // https://github.com/chartjs/Chart.js/issues/2807 console.error("Failed to create chart: can't acquire context from the given item"); return me; } me.initialize(); me.update(); return me; }; helpers.extend(Chart.Controller.prototype, /** @lends Chart.Controller.prototype */ { initialize: function() { var me = this; // Before init plugin notification plugins.notify(me, 'beforeInit'); helpers.retinaScale(me.chart); me.bindEvents(); if (me.options.responsive) { // Initial resize before chart draws (must be silent to preserve initial animations). me.resize(true); } // Make sure scales have IDs and are built before we build any controllers. me.ensureScalesHaveIDs(); me.buildScales(); me.initToolTip(); // After init plugin notification plugins.notify(me, 'afterInit'); return me; }, clear: function() { helpers.clear(this.chart); return this; }, stop: function() { // Stops any current animation loop occurring Chart.animationService.cancelAnimation(this); return this; }, resize: function(silent) { var me = this; var chart = me.chart; var options = me.options; var canvas = chart.canvas; var aspectRatio = (options.maintainAspectRatio && chart.aspectRatio) || null; // the canvas render width and height will be casted to integers so make sure that // the canvas display style uses the same integer values to avoid blurring effect. var newWidth = Math.floor(helpers.getMaximumWidth(canvas)); var newHeight = Math.floor(aspectRatio? newWidth / aspectRatio : helpers.getMaximumHeight(canvas)); if (chart.width === newWidth && chart.height === newHeight) { return; } canvas.width = chart.width = newWidth; canvas.height = chart.height = newHeight; canvas.style.width = newWidth + 'px'; canvas.style.height = newHeight + 'px'; helpers.retinaScale(chart); if (!silent) { // Notify any plugins about the resize var newSize = {width: newWidth, height: newHeight}; plugins.notify(me, 'resize', [newSize]); // Notify of resize if (me.options.onResize) { me.options.onResize(me, newSize); } me.stop(); me.update(me.options.responsiveAnimationDuration); } }, ensureScalesHaveIDs: function() { var options = this.options; var scalesOptions = options.scales || {}; var scaleOptions = options.scale; helpers.each(scalesOptions.xAxes, function(xAxisOptions, index) { xAxisOptions.id = xAxisOptions.id || ('x-axis-' + index); }); helpers.each(scalesOptions.yAxes, function(yAxisOptions, index) { yAxisOptions.id = yAxisOptions.id || ('y-axis-' + index); }); if (scaleOptions) { scaleOptions.id = scaleOptions.id || 'scale'; } }, /** * Builds a map of scale ID to scale object for future lookup. */ buildScales: function() { var me = this; var options = me.options; var scales = me.scales = {}; var items = []; if (options.scales) { items = items.concat( (options.scales.xAxes || []).map(function(xAxisOptions) { return {options: xAxisOptions, dtype: 'category'}; }), (options.scales.yAxes || []).map(function(yAxisOptions) { return {options: yAxisOptions, dtype: 'linear'}; }) ); } if (options.scale) { items.push({options: options.scale, dtype: 'radialLinear', isDefault: true}); } helpers.each(items, function(item) { var scaleOptions = item.options; var scaleType = helpers.getValueOrDefault(scaleOptions.type, item.dtype); var scaleClass = Chart.scaleService.getScaleConstructor(scaleType); if (!scaleClass) { return; } var scale = new scaleClass({ id: scaleOptions.id, options: scaleOptions, ctx: me.chart.ctx, chart: me }); scales[scale.id] = scale; // TODO(SB): I think we should be able to remove this custom case (options.scale) // and consider it as a regular scale part of the "scales"" map only! This would // make the logic easier and remove some useless? custom code. if (item.isDefault) { me.scale = scale; } }); Chart.scaleService.addScalesToLayout(this); }, buildOrUpdateControllers: function() { var me = this; var types = []; var newControllers = []; helpers.each(me.data.datasets, function(dataset, datasetIndex) { var meta = me.getDatasetMeta(datasetIndex); if (!meta.type) { meta.type = dataset.type || me.config.type; } types.push(meta.type); if (meta.controller) { meta.controller.updateIndex(datasetIndex); } else { meta.controller = new Chart.controllers[meta.type](me, datasetIndex); newControllers.push(meta.controller); } }, me); if (types.length > 1) { for (var i = 1; i < types.length; i++) { if (types[i] !== types[i - 1]) { me.isCombo = true; break; } } } return newControllers; }, /** * Reset the elements of all datasets * @private */ resetElements: function() { var me = this; helpers.each(me.data.datasets, function(dataset, datasetIndex) { me.getDatasetMeta(datasetIndex).controller.reset(); }, me); }, /** * Resets the chart back to it's state before the initial animation */ reset: function() { this.resetElements(); this.tooltip.initialize(); }, update: function(animationDuration, lazy) { var me = this; updateConfig(me); if (plugins.notify(me, 'beforeUpdate') === false) { return; } // In case the entire data object changed me.tooltip._data = me.data; // Make sure dataset controllers are updated and new controllers are reset var newControllers = me.buildOrUpdateControllers(); // Make sure all dataset controllers have correct meta data counts helpers.each(me.data.datasets, function(dataset, datasetIndex) { me.getDatasetMeta(datasetIndex).controller.buildOrUpdateElements(); }, me); me.updateLayout(); // Can only reset the new controllers after the scales have been updated helpers.each(newControllers, function(controller) { controller.reset(); }); me.updateDatasets(); // Do this before render so that any plugins that need final scale updates can use it plugins.notify(me, 'afterUpdate'); if (me._bufferedRender) { me._bufferedRequest = { lazy: lazy, duration: animationDuration }; } else { me.render(animationDuration, lazy); } }, /** * Updates the chart layout unless a plugin returns `false` to the `beforeLayout` * hook, in which case, plugins will not be called on `afterLayout`. * @private */ updateLayout: function() { var me = this; if (plugins.notify(me, 'beforeLayout') === false) { return; } Chart.layoutService.update(this, this.chart.width, this.chart.height); /** * Provided for backward compatibility, use `afterLayout` instead. * @method IPlugin#afterScaleUpdate * @deprecated since version 2.5.0 * @todo remove at version 3 */ plugins.notify(me, 'afterScaleUpdate'); plugins.notify(me, 'afterLayout'); }, /** * Updates all datasets unless a plugin returns `false` to the `beforeDatasetsUpdate` * hook, in which case, plugins will not be called on `afterDatasetsUpdate`. * @private */ updateDatasets: function() { var me = this; if (plugins.notify(me, 'beforeDatasetsUpdate') === false) { return; } for (var i = 0, ilen = me.data.datasets.length; i < ilen; ++i) { me.getDatasetMeta(i).controller.update(); } plugins.notify(me, 'afterDatasetsUpdate'); }, render: function(duration, lazy) { var me = this; if (plugins.notify(me, 'beforeRender') === false) { return; } var animationOptions = me.options.animation; var onComplete = function() { plugins.notify(me, 'afterRender'); var callback = animationOptions && animationOptions.onComplete; if (callback && callback.call) { callback.call(me); } }; if (animationOptions && ((typeof duration !== 'undefined' && duration !== 0) || (typeof duration === 'undefined' && animationOptions.duration !== 0))) { var animation = new Chart.Animation(); animation.numSteps = (duration || animationOptions.duration) / 16.66; // 60 fps animation.easing = animationOptions.easing; // render function animation.render = function(chartInstance, animationObject) { var easingFunction = helpers.easingEffects[animationObject.easing]; var stepDecimal = animationObject.currentStep / animationObject.numSteps; var easeDecimal = easingFunction(stepDecimal); chartInstance.draw(easeDecimal, stepDecimal, animationObject.currentStep); }; // user events animation.onAnimationProgress = animationOptions.onProgress; animation.onAnimationComplete = onComplete; Chart.animationService.addAnimation(me, animation, duration, lazy); } else { me.draw(); onComplete(); } return me; }, draw: function(easingValue) { var me = this; me.clear(); if (easingValue === undefined || easingValue === null) { easingValue = 1; } if (plugins.notify(me, 'beforeDraw', [easingValue]) === false) { return; } // Draw all the scales helpers.each(me.boxes, function(box) { box.draw(me.chartArea); }, me); if (me.scale) { me.scale.draw(); } me.drawDatasets(easingValue); // Finally draw the tooltip me.tooltip.transition(easingValue).draw(); plugins.notify(me, 'afterDraw', [easingValue]); }, /** * Draws all datasets unless a plugin returns `false` to the `beforeDatasetsDraw` * hook, in which case, plugins will not be called on `afterDatasetsDraw`. * @private */ drawDatasets: function(easingValue) { var me = this; if (plugins.notify(me, 'beforeDatasetsDraw', [easingValue]) === false) { return; } // Draw each dataset via its respective controller (reversed to support proper line stacking) helpers.each(me.data.datasets, function(dataset, datasetIndex) { if (me.isDatasetVisible(datasetIndex)) { me.getDatasetMeta(datasetIndex).controller.draw(easingValue); } }, me, true); plugins.notify(me, 'afterDatasetsDraw', [easingValue]); }, // Get the single element that was clicked on // @return : An object containing the dataset index and element index of the matching element. Also contains the rectangle that was draw getElementAtEvent: function(e) { return Chart.Interaction.modes.single(this, e); }, getElementsAtEvent: function(e) { return Chart.Interaction.modes.label(this, e, {intersect: true}); }, getElementsAtXAxis: function(e) { return Chart.Interaction.modes['x-axis'](this, e, {intersect: true}); }, getElementsAtEventForMode: function(e, mode, options) { var method = Chart.Interaction.modes[mode]; if (typeof method === 'function') { return method(this, e, options); } return []; }, getDatasetAtEvent: function(e) { return Chart.Interaction.modes.dataset(this, e, {intersect: true}); }, getDatasetMeta: function(datasetIndex) { var me = this; var dataset = me.data.datasets[datasetIndex]; if (!dataset._meta) { dataset._meta = {}; } var meta = dataset._meta[me.id]; if (!meta) { meta = dataset._meta[me.id] = { type: null, data: [], dataset: null, controller: null, hidden: null, // See isDatasetVisible() comment xAxisID: null, yAxisID: null }; } return meta; }, getVisibleDatasetCount: function() { var count = 0; for (var i = 0, ilen = this.data.datasets.length; i